mirror of
https://github.com/bunny-lab-io/Borealis.git
synced 2025-09-11 06:28:43 -06:00
Load workflow JSON on open and enable tab closing
This commit is contained in:
@@ -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(
|
||||||
|
workflow.rel_path
|
||||||
|
)}`
|
||||||
);
|
);
|
||||||
if (existing) {
|
if (!resp.ok) throw new Error(`HTTP ${resp.status}`);
|
||||||
setActiveTabId(existing.id);
|
const data = await resp.json();
|
||||||
setCurrentPage("workflow-editor");
|
setTabs([
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Otherwise, create a new workflow tab
|
|
||||||
const newId = "flow_" + (tabs.length + 1);
|
|
||||||
setTabs((prev) => [
|
|
||||||
...prev,
|
|
||||||
{
|
{
|
||||||
id: newId,
|
id: newId,
|
||||||
tab_name: workflow?.name || `Flow ${tabs.length + 1}`,
|
tab_name:
|
||||||
nodes: [],
|
data.tab_name || workflow.name || workflow.file_name || "Workflow",
|
||||||
edges: []
|
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: [] }]);
|
||||||
|
}
|
||||||
setActiveTabId(newId);
|
setActiveTabId(newId);
|
||||||
setCurrentPage("workflow-editor");
|
setCurrentPage("workflow-editor");
|
||||||
}}
|
}}
|
||||||
@@ -196,7 +241,7 @@ export default function App() {
|
|||||||
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) => (
|
||||||
@@ -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>
|
||||||
);
|
);
|
||||||
|
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
@@ -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
|
||||||
# ---------------------------------------------
|
# ---------------------------------------------
|
||||||
|
Reference in New Issue
Block a user