Load workflow JSON on open and enable tab closing

This commit is contained in:
2025-08-09 21:11:20 -06:00
parent 63280864dd
commit 23913c2ce1
3 changed files with 93 additions and 32 deletions

View File

@@ -136,6 +136,45 @@ export default function App() {
const handleAboutMenuClose = () => setAboutAnchorEl(null); const handleAboutMenuClose = () => setAboutAnchorEl(null);
const openCreditsDialog = () => { handleAboutMenuClose(); setCreditsDialogOpen(true); }; const openCreditsDialog = () => { handleAboutMenuClose(); setCreditsDialogOpen(true); };
const handleTabRightClick = (evt, tabId) => {
evt.preventDefault();
setTabMenuAnchor({ x: evt.clientX, y: evt.clientY });
setTabMenuTabId(tabId);
};
const handleCloseTab = () => {
setTabs((prev) => {
const filtered = prev.filter((t) => t.id !== tabMenuTabId);
if (filtered.length === 0) {
const newTab = { id: "flow_1", tab_name: "Flow 1", nodes: [], edges: [] };
setActiveTabId(newTab.id);
return [newTab];
}
if (activeTabId === tabMenuTabId) {
setActiveTabId(filtered[0].id);
}
return filtered;
});
setTabMenuAnchor(null);
};
const handleRenameTab = () => {
const tab = tabs.find((t) => t.id === tabMenuTabId);
if (tab) {
setRenameTabId(tabMenuTabId);
setRenameValue(tab.tab_name);
setRenameDialogOpen(true);
}
setTabMenuAnchor(null);
};
const handleSaveRename = () => {
setTabs((prev) =>
prev.map((t) => (t.id === renameTabId ? { ...t, tab_name: renameValue } : t))
);
setRenameDialogOpen(false);
};
const renderMainContent = () => { const renderMainContent = () => {
switch (currentPage) { switch (currentPage) {
case "devices": case "devices":
@@ -147,29 +186,35 @@ export default function App() {
case "workflows": case "workflows":
return ( return (
<WorkflowList <WorkflowList
onOpenWorkflow={(workflow) => { onOpenWorkflow={async (workflow) => {
// If workflow name exists in tabs, just switch to it const newId = "flow_" + Date.now();
if (workflow?.name) { if (workflow && workflow.rel_path) {
const existing = tabs.find( try {
(t) => t.tab_name.toLowerCase() === workflow.name.toLowerCase() const resp = await fetch(
); `/api/storage/load_workflow?path=${encodeURIComponent(
if (existing) { workflow.rel_path
setActiveTabId(existing.id); )}`
setCurrentPage("workflow-editor"); );
return; if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
const data = await resp.json();
setTabs([
{
id: newId,
tab_name:
data.tab_name || workflow.name || workflow.file_name || "Workflow",
nodes: data.nodes || [],
edges: data.edges || []
}
]);
} catch (err) {
console.error("Failed to load workflow:", err);
setTabs([
{ id: newId, tab_name: workflow?.name || "Workflow", nodes: [], edges: [] }
]);
} }
} else {
setTabs([{ id: newId, tab_name: `Flow`, nodes: [], edges: [] }]);
} }
// Otherwise, create a new workflow tab
const newId = "flow_" + (tabs.length + 1);
setTabs((prev) => [
...prev,
{
id: newId,
tab_name: workflow?.name || `Flow ${tabs.length + 1}`,
nodes: [],
edges: []
}
]);
setActiveTabId(newId); setActiveTabId(newId);
setCurrentPage("workflow-editor"); setCurrentPage("workflow-editor");
}} }}
@@ -191,13 +236,13 @@ export default function App() {
onFileInputChange={() => {}} onFileInputChange={() => {}}
/> />
<Box sx={{ display: "flex", flexDirection: "column", flexGrow: 1, overflow: "hidden" }}> <Box sx={{ display: "flex", flexDirection: "column", flexGrow: 1, overflow: "hidden" }}>
<FlowTabs <FlowTabs
tabs={tabs} tabs={tabs}
activeTabId={activeTabId} activeTabId={activeTabId}
onTabChange={setActiveTabId} onTabChange={setActiveTabId}
onAddTab={() => {}} onAddTab={() => {}}
onTabRightClick={() => {}} onTabRightClick={handleTabRightClick}
/> />
<Box sx={{ flexGrow: 1, position: "relative" }}> <Box sx={{ flexGrow: 1, position: "relative" }}>
{tabs.map((tab) => ( {tabs.map((tab) => (
<Box <Box
@@ -276,13 +321,13 @@ export default function App() {
value={renameValue} value={renameValue}
onChange={setRenameValue} onChange={setRenameValue}
onCancel={() => setRenameDialogOpen(false)} onCancel={() => setRenameDialogOpen(false)}
onSave={() => {}} onSave={handleSaveRename}
/> />
<TabContextMenu <TabContextMenu
anchor={tabMenuAnchor} anchor={tabMenuAnchor}
onClose={() => setTabMenuAnchor(null)} onClose={() => setTabMenuAnchor(null)}
onRename={() => {}} onRename={handleRenameTab}
onCloseTab={() => {}} onCloseTab={handleCloseTab}
/> />
</ThemeProvider> </ThemeProvider>
); );

View File

@@ -133,7 +133,7 @@ export function TabContextMenu({ anchor, onClose, onRename, onCloseTab }) {
}} }}
> >
<MenuItem onClick={onRename}>Rename</MenuItem> <MenuItem onClick={onRename}>Rename</MenuItem>
<MenuItem onClick={onCloseTab}>Close</MenuItem> <MenuItem onClick={onCloseTab}>Close Workflow</MenuItem>
</Menu> </Menu>
); );
} }

View File

@@ -170,6 +170,22 @@ def load_workflows():
"workflows": results "workflows": results
}) })
@app.route("/api/storage/load_workflow", methods=["GET"])
def load_workflow():
"""Load a single workflow JSON by its relative path."""
rel_path = request.args.get("path", "")
workflows_root = os.path.abspath(
os.path.join(os.path.dirname(__file__), "..", "..", "Workflows")
)
abs_path = os.path.abspath(os.path.join(workflows_root, rel_path))
if not abs_path.startswith(workflows_root) or not os.path.isfile(abs_path):
return jsonify({"error": "Workflow not found"}), 404
obj = _safe_read_json(abs_path)
return jsonify(obj)
# --------------------------------------------- # ---------------------------------------------
# Borealis Agent API Endpoints # Borealis Agent API Endpoints
# --------------------------------------------- # ---------------------------------------------