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 */} - - + + - - - { - handleAboutMenuClose(); - window.open("https://git.bunny-lab.io/Borealis", "_blank"); - }} - > - - Gitea Project + + { handleAboutMenuClose(); window.open("https://git.bunny-lab.io/Borealis", "_blank"); }}> + Gitea Project - - Credits + Credits - {/* 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 */} - - Close All Flow Tabs? - - - This will remove all existing flow tabs and create a fresh tab named Flow 1. - - - - - - - - - {/* Credits */} - setCreditsDialogOpen(false)} - PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }} - > - Borealis Workflow Automation Tool - - - Designed by Nicole Rappe @ Bunny Lab - - - - - - - - {/* Tab Context Menu */} - - Rename - Close - - - {/* 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 ( + + Close All Flow Tabs? + + + This will remove all existing flow tabs and create a fresh tab named Flow 1. + + + + + + + + ); +} + +export function CreditsDialog({ open, onClose }) { + return ( + + Borealis Workflow Automation Tool + + + Designed by Nicole Rappe @ Bunny Lab + + + + + + + ); +} + +export function RenameTabDialog({ open, value, onChange, onCancel, onSave }) { + return ( + + Rename Tab + + onChange(e.target.value)} + sx={{ + "& .MuiOutlinedInput-root": { + backgroundColor: "#2a2a2a", + color: "#ccc", + "& fieldset": { + borderColor: "#444" + }, + "&:hover fieldset": { + borderColor: "#666" + } + }, + label: { color: "#aaa" }, + mt: 1 + }} + /> + + + + + + + ); +} + +export function TabContextMenu({ anchor, onClose, onRename, onCloseTab }) { + return ( + + Rename + Close + + ); +} \ 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): + + + + ); +}