diff --git a/Data/WebUI/package.json b/Data/WebUI/package.json
index e5784a8..4b83d42 100644
--- a/Data/WebUI/package.json
+++ b/Data/WebUI/package.json
@@ -9,7 +9,6 @@
"dependencies": {
"@mui/material": "7.0.2",
"@mui/icons-material": "7.0.2",
- "@mui/x-tree-view": "7.28.1",
"@emotion/react": "11.14.0",
"@emotion/styled": "11.14.0",
"react-resizable": "3.0.5",
diff --git a/Data/WebUI/public/favicon.ico b/Data/WebUI/public/favicon.ico
new file mode 100644
index 0000000..901a213
Binary files /dev/null and b/Data/WebUI/public/favicon.ico differ
diff --git a/Data/WebUI/src/App.jsx b/Data/WebUI/src/App.jsx
index 6cd4b11..5d792b3 100644
--- a/Data/WebUI/src/App.jsx
+++ b/Data/WebUI/src/App.jsx
@@ -19,14 +19,7 @@ import React, {
Button,
CssBaseline,
ThemeProvider,
- createTheme,
- Dialog,
- DialogTitle,
- DialogContent,
- DialogContentText,
- DialogActions,
- Divider,
- TextField
+ createTheme
} from "@mui/material";
// Material UI - Icons
@@ -43,10 +36,17 @@ import React, {
// Styles
import "reactflow/dist/style.css";
- // Import our new components
+ // Import Borealis Modules
import FlowTabs from "./Flow_Tabs";
import FlowEditor from "./Flow_Editor";
import NodeSidebar from "./Node_Sidebar";
+ import {
+ CloseAllDialog,
+ CreditsDialog,
+ RenameTabDialog,
+ TabContextMenu
+ } from "./Dialogs";
+ import StatusBar from "./Status_Bar";
// Global Node Update Timer Variable
if (!window.BorealisUpdateRate) {
@@ -99,26 +99,16 @@ import React, {
]);
const [activeTabId, setActiveTabId] = useState("flow_1");
- // About menu
const [aboutAnchorEl, setAboutAnchorEl] = useState(null);
const [creditsDialogOpen, setCreditsDialogOpen] = useState(false);
-
- // Close all flows
const [confirmCloseOpen, setConfirmCloseOpen] = useState(false);
-
- // Rename tab
const [renameDialogOpen, setRenameDialogOpen] = useState(false);
const [renameTabId, setRenameTabId] = useState(null);
const [renameValue, setRenameValue] = useState("");
-
- // Right-click tab menu
const [tabMenuAnchor, setTabMenuAnchor] = useState(null);
const [tabMenuTabId, setTabMenuTabId] = useState(null);
-
- // File input ref (for imports on older browsers)
const fileInputRef = useRef(null);
- // Setup callbacks to update nodes/edges in the currently active tab
const handleSetNodes = useCallback(
(callbackOrArray, tId) => {
const targetId = tId || activeTabId;
@@ -153,17 +143,13 @@ import React, {
[activeTabId]
);
- // About menu
const handleAboutMenuOpen = (event) => setAboutAnchorEl(event.currentTarget);
const handleAboutMenuClose = () => setAboutAnchorEl(null);
-
- // Credits
const openCreditsDialog = () => {
handleAboutMenuClose();
setCreditsDialogOpen(true);
};
- // Close all dialog
const handleOpenCloseAllDialog = () => setConfirmCloseOpen(true);
const handleCloseDialog = () => setConfirmCloseOpen(false);
const handleConfirmCloseAll = () => {
@@ -179,7 +165,6 @@ import React, {
setConfirmCloseOpen(false);
};
- // Create new tab
const createNewTab = () => {
const nextIndex = tabs.length + 1;
const newId = "flow_" + nextIndex;
@@ -195,23 +180,21 @@ import React, {
setActiveTabId(newId);
};
- // Handle user clicking on a tab
const handleTabChange = (newActiveTabId) => {
setActiveTabId(newActiveTabId);
};
- // Right-click tab menu
const handleTabRightClick = (evt, tabId) => {
evt.preventDefault();
setTabMenuAnchor({ x: evt.clientX, y: evt.clientY });
setTabMenuTabId(tabId);
};
+
const handleCloseTabMenu = () => {
setTabMenuAnchor(null);
setTabMenuTabId(null);
};
- // Rename / close tab
const handleRenameTab = () => {
setRenameDialogOpen(true);
setRenameTabId(tabMenuTabId);
@@ -219,6 +202,7 @@ import React, {
setRenameValue(t ? t.tab_name : "");
handleCloseTabMenu();
};
+
const handleCloseTab = () => {
setTabs((old) => {
const idx = old.findIndex((t) => t.id === tabMenuTabId);
@@ -227,11 +211,9 @@ import React, {
const newList = [...old];
newList.splice(idx, 1);
- // If we closed the current tab, pick a new active tab
if (tabMenuTabId === activeTabId && newList.length > 0) {
setActiveTabId(newList[0].id);
} else if (newList.length === 0) {
- // If we closed the only tab, create a fresh one
newList.push({
id: "flow_1",
tab_name: "Flow 1",
@@ -260,12 +242,10 @@ import React, {
setRenameDialogOpen(false);
};
- // Export current tab
const handleExportFlow = async () => {
const activeTab = tabs.find((x) => x.id === activeTabId);
if (!activeTab) return;
- // Build JSON data from the active tab
const data = JSON.stringify(
{
nodes: activeTab.nodes,
@@ -276,15 +256,9 @@ import React, {
2
);
const blob = new Blob([data], { type: "application/json" });
-
- // Suggested filename based on the tab name
- // e.g. "Nicole Work Flow" => "nicole_work_flow_workflow.json"
- const sanitizedTabName = activeTab.tab_name
- .replace(/\s+/g, "_")
- .toLowerCase();
+ const sanitizedTabName = activeTab.tab_name.replace(/\s+/g, "_").toLowerCase();
const suggestedFilename = sanitizedTabName + "_workflow.json";
- // Check if showSaveFilePicker is available (Chrome/Edge)
if (window.showSaveFilePicker) {
try {
const fileHandle = await window.showSaveFilePicker({
@@ -304,21 +278,17 @@ import React, {
console.error("Save cancelled or failed:", err);
}
} else {
- // Fallback for browsers like Firefox
- // (Relies on browser settings to ask user where to save)
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
- a.download = suggestedFilename; // e.g. nicole_work_flow_workflow.json
+ a.download = suggestedFilename;
a.style.display = "none";
document.body.appendChild(a);
a.click();
- // Cleanup
URL.revokeObjectURL(a.href);
document.body.removeChild(a);
}
};
- // Import flow -> new tab
const handleImportFlow = async () => {
if (window.showOpenFilePicker) {
try {
@@ -349,12 +319,10 @@ import React, {
console.error("Import cancelled or failed:", err);
}
} else {
- // Fallback for older browsers
fileInputRef.current?.click();
}
};
- // Fallback import
const handleFileInputChange = async (e) => {
const file = e.target.files[0];
if (!file) return;
@@ -382,35 +350,11 @@ import React, {
-
+
- {/* Logo */}
-
-
-
- {/* Additional Title/Info if desired */}
-
-
+
+
-
-
- {/* Sidebar */}
- {/* Right content: tab bar + flow editors */}
-
- {/* Tab bar */}
+
- {/* The flow editors themselves */}
{tabs.map((tab) => (
- {/* Bottom status bar */}
-
- Nodes: 0
-
- Update Rate (ms):
-
-
-
+
- {/* Close All Dialog */}
-
-
- {/* Credits */}
-
-
- {/* Tab Context Menu */}
-
-
-
-
-
- {/* Rename Tab Dialog */}
-
+ setCreditsDialogOpen(false)} />
+ setRenameDialogOpen(false)}
- PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}
- >
- Rename Tab
-
- setRenameValue(e.target.value)}
- sx={{
- "& .MuiOutlinedInput-root": {
- backgroundColor: "#2a2a2a",
- color: "#ccc",
- "& fieldset": {
- borderColor: "#444"
- },
- "&:hover fieldset": {
- borderColor: "#666"
- }
- },
- label: { color: "#aaa" },
- mt: 1
- }}
- />
-
-
-
-
-
-
+ value={renameValue}
+ onChange={setRenameValue}
+ onCancel={() => setRenameDialogOpen(false)}
+ onSave={handleRenameDialogSave}
+ />
+
);
}
diff --git a/Data/WebUI/src/Dialogs.jsx b/Data/WebUI/src/Dialogs.jsx
new file mode 100644
index 0000000..f19ef3d
--- /dev/null
+++ b/Data/WebUI/src/Dialogs.jsx
@@ -0,0 +1,105 @@
+////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Dialogs.jsx
+
+import React from "react";
+import {
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogContentText,
+ DialogActions,
+ Button,
+ Menu,
+ MenuItem,
+ TextField
+} from "@mui/material";
+
+export function CloseAllDialog({ open, onClose, onConfirm }) {
+ return (
+
+ );
+}
+
+export function CreditsDialog({ open, onClose }) {
+ return (
+
+ );
+}
+
+export function RenameTabDialog({ open, value, onChange, onCancel, onSave }) {
+ return (
+
+ );
+}
+
+export function TabContextMenu({ anchor, onClose, onRename, onCloseTab }) {
+ return (
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/Data/WebUI/src/Node_Sidebar.jsx b/Data/WebUI/src/Node_Sidebar.jsx
index 59a080d..794bd38 100644
--- a/Data/WebUI/src/Node_Sidebar.jsx
+++ b/Data/WebUI/src/Node_Sidebar.jsx
@@ -6,7 +6,6 @@ import {
AccordionSummary,
AccordionDetails,
Button,
- Divider,
Tooltip,
Typography
} from "@mui/material";
diff --git a/Data/WebUI/src/Status_Bar.jsx b/Data/WebUI/src/Status_Bar.jsx
new file mode 100644
index 0000000..2f31d41
--- /dev/null
+++ b/Data/WebUI/src/Status_Bar.jsx
@@ -0,0 +1,71 @@
+////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: /Data/WebUI/src/Status_Bar.jsx
+
+import React from "react";
+import { Box, Button, Divider } from "@mui/material";
+
+export default function StatusBar() {
+ const applyRate = () => {
+ const val = parseInt(
+ document.getElementById("updateRateInput")?.value
+ );
+ if (!isNaN(val) && val >= 50) {
+ window.BorealisUpdateRate = val;
+ console.log("Global update rate set to", val + "ms");
+ } else {
+ alert("Please enter a valid number (min 50).");
+ }
+ };
+
+ return (
+
+ Nodes: 0
+
+ Update Rate (ms):
+
+
+
+ );
+}