From ed7f7d10210a4432232150a8dc916146c4a86c98 Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Tue, 15 Apr 2025 03:16:17 -0600 Subject: [PATCH] Modularized Dialogs and Status Bar --- Data/WebUI/package.json | 1 - Data/WebUI/public/favicon.ico | Bin 0 -> 15406 bytes Data/WebUI/src/App.jsx | 327 ++++---------------------------- Data/WebUI/src/Dialogs.jsx | 105 ++++++++++ Data/WebUI/src/Node_Sidebar.jsx | 1 - Data/WebUI/src/Status_Bar.jsx | 71 +++++++ 6 files changed, 214 insertions(+), 291 deletions(-) create mode 100644 Data/WebUI/public/favicon.ico create mode 100644 Data/WebUI/src/Dialogs.jsx create mode 100644 Data/WebUI/src/Status_Bar.jsx 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 0000000000000000000000000000000000000000..901a213f51c7c26115c6ad26a6c5d7b89076eccb GIT binary patch literal 15406 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 { 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): + + + + ); +}