Added Collapsible Sidebar

This commit is contained in:
2025-04-30 23:40:15 -06:00
parent 1047e8ee51
commit cf14ee0111
4 changed files with 159 additions and 142 deletions

View File

@ -82,7 +82,7 @@
input, input,
select, select,
button { button {
background-color: #2a2a2a; background-color: #1d1d1d;
color: #ccc; color: #ccc;
border: 1px solid #444; border: 1px solid #444;
font-size: 12px; font-size: 12px;
@ -91,7 +91,7 @@ button {
/* Label / Dark Text styling */ /* Label / Dark Text styling */
label { label {
color: #aaa; color: #aaa;
font-size: 10px; font-size: 9px;
} }
/* ======================================= */ /* ======================================= */

View File

@ -7,7 +7,8 @@ import {
AccordionDetails, AccordionDetails,
Button, Button,
Tooltip, Tooltip,
Typography Typography,
IconButton
} from "@mui/material"; } from "@mui/material";
import { import {
ExpandMore as ExpandMoreIcon, ExpandMore as ExpandMoreIcon,
@ -15,7 +16,9 @@ import {
FileOpen as FileOpenIcon, FileOpen as FileOpenIcon,
DeleteForever as DeleteForeverIcon, DeleteForever as DeleteForeverIcon,
DragIndicator as DragIndicatorIcon, DragIndicator as DragIndicatorIcon,
Polyline as PolylineIcon Polyline as PolylineIcon,
ChevronLeft as ChevronLeftIcon,
ChevronRight as ChevronRightIcon
} from "@mui/icons-material"; } from "@mui/icons-material";
export default function NodeSidebar({ export default function NodeSidebar({
@ -27,6 +30,7 @@ export default function NodeSidebar({
onFileInputChange onFileInputChange
}) { }) {
const [expandedCategory, setExpandedCategory] = useState(null); const [expandedCategory, setExpandedCategory] = useState(null);
const [collapsed, setCollapsed] = useState(false);
const handleAccordionChange = (category) => (_, isExpanded) => { const handleAccordionChange = (category) => (_, isExpanded) => {
setExpandedCategory(isExpanded ? category : null); setExpandedCategory(isExpanded ? category : null);
@ -35,132 +39,154 @@ export default function NodeSidebar({
return ( return (
<div <div
style={{ style={{
width: 300, //Width of the Node Sidebar width: collapsed ? 40 : 300,
backgroundColor: "#121212", backgroundColor: "#121212",
borderRight: "1px solid #333", borderRight: "1px solid #333",
overflowY: "auto" overflow: "hidden",
display: "flex",
flexDirection: "column",
height: "100%"
}} }}
> >
{/* Workflows Section */} <div style={{ flex: 1, overflowY: "auto" }}>
<Accordion {!collapsed && (
defaultExpanded <>
square {/* Workflows Section */}
disableGutters
sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
sx={{
backgroundColor: "#2c2c2c",
minHeight: "36px",
"& .MuiAccordionSummary-content": { margin: 0 }
}}
>
<Typography sx={{ fontSize: "0.9rem", color: "#0475c2" }}>
<b>Workflows</b>
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ p: 0, bgcolor: "#232323" }}>
<Button fullWidth startIcon={<SaveIcon />} onClick={handleExportFlow} sx={buttonStyle}>
Export Current Flow
</Button>
<Button fullWidth startIcon={<FileOpenIcon />} onClick={handleImportFlow} sx={buttonStyle}>
Import Flow
</Button>
<Button fullWidth startIcon={<DeleteForeverIcon />} onClick={handleOpenCloseAllDialog} sx={buttonStyle}>
Close All Flows
</Button>
</AccordionDetails>
</Accordion>
{/* Nodes Section */}
<Accordion
defaultExpanded
square
disableGutters
sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
sx={{
backgroundColor: "#2c2c2c",
minHeight: "36px",
"& .MuiAccordionSummary-content": { margin: 0 }
}}
>
<Typography sx={{ fontSize: "0.9rem", color: "#0475c2" }}>
<b>Nodes</b>
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ p: 0 }}>
{Object.entries(categorizedNodes).map(([category, items]) => (
<Accordion <Accordion
key={category} defaultExpanded
square square
expanded={expandedCategory === category}
onChange={handleAccordionChange(category)}
disableGutters disableGutters
sx={{ sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }}
bgcolor: "#232323",
"&:before": { display: "none" },
margin: 0,
border: 0
}}
> >
<AccordionSummary <AccordionSummary
expandIcon={<ExpandMoreIcon />} expandIcon={<ExpandMoreIcon />}
sx={{ sx={{
bgcolor: "#1e1e1e", backgroundColor: "#2c2c2c",
px: 2, minHeight: "36px",
minHeight: "32px",
"& .MuiAccordionSummary-content": { margin: 0 } "& .MuiAccordionSummary-content": { margin: 0 }
}} }}
> >
<Typography sx={{ color: "#888", fontSize: "0.75rem" }}> <Typography sx={{ fontSize: "0.9rem", color: "#0475c2" }}>
{category} <b>Workflows</b>
</Typography> </Typography>
</AccordionSummary> </AccordionSummary>
<AccordionDetails sx={{ px: 1, py: 0 }}> <AccordionDetails sx={{ p: 0, bgcolor: "#232323" }}>
{items.map((nodeDef) => ( <Button fullWidth startIcon={<SaveIcon />} onClick={handleExportFlow} sx={buttonStyle}>
<Tooltip Export Current Flow
key={`${category}-${nodeDef.type}`} </Button>
title={ <Button fullWidth startIcon={<FileOpenIcon />} onClick={handleImportFlow} sx={buttonStyle}>
<span style={{ whiteSpace: "pre-line", wordWrap: "break-word", maxWidth: 220 }}> Import Flow
{nodeDef.description || "Drag & Drop into Editor"} </Button>
</span> <Button fullWidth startIcon={<DeleteForeverIcon />} onClick={handleOpenCloseAllDialog} sx={buttonStyle}>
} Close All Flows
placement="right" </Button>
arrow </AccordionDetails>
</Accordion>
{/* Nodes Section */}
<Accordion
defaultExpanded
square
disableGutters
sx={{ "&:before": { display: "none" }, margin: 0, border: 0 }}
>
<AccordionSummary
expandIcon={<ExpandMoreIcon />}
sx={{
backgroundColor: "#2c2c2c",
minHeight: "36px",
"& .MuiAccordionSummary-content": { margin: 0 }
}}
>
<Typography sx={{ fontSize: "0.9rem", color: "#0475c2" }}>
<b>Nodes</b>
</Typography>
</AccordionSummary>
<AccordionDetails sx={{ p: 0 }}>
{Object.entries(categorizedNodes).map(([category, items]) => (
<Accordion
key={category}
square
expanded={expandedCategory === category}
onChange={handleAccordionChange(category)}
disableGutters
sx={{
bgcolor: "#232323",
"&:before": { display: "none" },
margin: 0,
border: 0
}}
> >
<Button <AccordionSummary
fullWidth expandIcon={<ExpandMoreIcon />}
sx={nodeButtonStyle} sx={{
draggable bgcolor: "#1e1e1e",
onDragStart={(event) => { px: 2,
event.dataTransfer.setData("application/reactflow", nodeDef.type); minHeight: "32px",
event.dataTransfer.effectAllowed = "move"; "& .MuiAccordionSummary-content": { margin: 0 }
}} }}
startIcon={<DragIndicatorIcon sx={{ color: "#666", fontSize: 18 }} />}
> >
<span style={{ flexGrow: 1, textAlign: "left" }}>{nodeDef.label}</span> <Typography sx={{ color: "#888", fontSize: "0.75rem" }}>
<PolylineIcon sx={{ color: "#58a6ff", fontSize: 18, ml: 1 }} /> {category}
</Button> </Typography>
</Tooltip> </AccordionSummary>
<AccordionDetails sx={{ px: 1, py: 0 }}>
{items.map((nodeDef) => (
<Tooltip
key={`${category}-${nodeDef.type}`}
title={
<span style={{ whiteSpace: "pre-line", wordWrap: "break-word", maxWidth: 220 }}>
{nodeDef.description || "Drag & Drop into Editor"}
</span>
}
placement="right"
arrow
>
<Button
fullWidth
sx={nodeButtonStyle}
draggable
onDragStart={(event) => {
event.dataTransfer.setData("application/reactflow", nodeDef.type);
event.dataTransfer.effectAllowed = "move";
}}
startIcon={<DragIndicatorIcon sx={{ color: "#666", fontSize: 18 }} />}
>
<span style={{ flexGrow: 1, textAlign: "left" }}>{nodeDef.label}</span>
<PolylineIcon sx={{ color: "#58a6ff", fontSize: 18, ml: 1 }} />
</Button>
</Tooltip>
))}
</AccordionDetails>
</Accordion>
))} ))}
</AccordionDetails> </AccordionDetails>
</Accordion> </Accordion>
))}
</AccordionDetails>
</Accordion>
{/* Hidden file input fallback for older browsers */} {/* Hidden file input */}
<input <input
type="file" type="file"
accept=".json,application/json" accept=".json,application/json"
style={{ display: "none" }} style={{ display: "none" }}
ref={fileInputRef} ref={fileInputRef}
onChange={onFileInputChange} onChange={onFileInputChange}
/> />
</>
)}
</div>
{/* Bottom toggle button */}
<div style={{ padding: "6px", borderTop: "1px solid #333", display: "flex", justifyContent: "center" }}>
<Tooltip title={collapsed ? "Expand Sidebar" : "Collapse Sidebar"} placement="right">
<IconButton
onClick={() => setCollapsed(!collapsed)}
size="small"
sx={{ color: "#888" }}
>
{collapsed ? <ChevronRightIcon /> : <ChevronLeftIcon />}
</IconButton>
</Tooltip>
</div>
</div> </div>
); );
} }

View File

@ -104,7 +104,7 @@ const BorealisAgentNode = ({ id, data }) => {
> >
<option value="">-- Select --</option> <option value="">-- Select --</option>
{Object.entries(agents).map(([id, info]) => { {Object.entries(agents).map(([id, info]) => {
const label = info.status === "provisioned" ? "(Provisioned)" : "(Idle)"; const label = info.status === "provisioned" ? "(Provisioned)" : "(Not Provisioned)";
return ( return (
<option key={id} value={id}> <option key={id} value={id}>
{id} {label} {id} {label}
@ -123,16 +123,8 @@ const BorealisAgentNode = ({ id, data }) => {
<hr style={{ margin: "6px 0", borderColor: "#444" }} /> <hr style={{ margin: "6px 0", borderColor: "#444" }} />
<div style={{ fontSize: "8px", color: "#aaa" }}> <div style={{ fontSize: "8px", color: "#aaa" }}>
Connect <strong>Instruction Nodes</strong> below to define roles. Connect <strong>Agent Role Nodes</strong> below to define roles for this agent.
Each instruction node will send back its results (like screenshots) and act as a separate data output. Each connected <strong>Agent Role Node</strong> will send back its collected data (like screenshots) through the role node itself and act as a separate data output.
</div>
<div style={{ fontSize: "8px", color: "#aaa", marginTop: "4px" }}>
<strong>Supported Roles:</strong>
<ul style={{ paddingLeft: "14px", marginTop: "2px", marginBottom: "0" }}>
<li><code>screenshot</code>: Capture a region with interval and overlay</li>
{/* Future roles will be listed here */}
</ul>
</div> </div>
</div> </div>
</div> </div>

View File

@ -1,25 +1,24 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ESNext", "target": "ESNext",
"useDefineForClassFields": true, "useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"], "lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false, "allowJs": false,
"skipLibCheck": true, "skipLibCheck": true,
"esModuleInterop": false, "esModuleInterop": false,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"strict": true, "strict": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"module": "ESNext", "module": "ESNext",
"moduleResolution": "Node", "moduleResolution": "Node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "react-jsx", "jsx": "react-jsx",
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@/*": ["src/*"] "@/*": ["src/*"]
} }
}, },
"include": ["src"], "include": ["src"],
"exclude": ["node_modules", "build", "dist"] "exclude": ["node_modules", "build", "dist"]
} }