Modularized Dialogs and Status Bar
This commit is contained in:
parent
e8f9894e05
commit
ed7f7d1021
@ -9,7 +9,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mui/material": "7.0.2",
|
"@mui/material": "7.0.2",
|
||||||
"@mui/icons-material": "7.0.2",
|
"@mui/icons-material": "7.0.2",
|
||||||
"@mui/x-tree-view": "7.28.1",
|
|
||||||
"@emotion/react": "11.14.0",
|
"@emotion/react": "11.14.0",
|
||||||
"@emotion/styled": "11.14.0",
|
"@emotion/styled": "11.14.0",
|
||||||
"react-resizable": "3.0.5",
|
"react-resizable": "3.0.5",
|
||||||
|
BIN
Data/WebUI/public/favicon.ico
Normal file
BIN
Data/WebUI/public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
@ -19,14 +19,7 @@ import React, {
|
|||||||
Button,
|
Button,
|
||||||
CssBaseline,
|
CssBaseline,
|
||||||
ThemeProvider,
|
ThemeProvider,
|
||||||
createTheme,
|
createTheme
|
||||||
Dialog,
|
|
||||||
DialogTitle,
|
|
||||||
DialogContent,
|
|
||||||
DialogContentText,
|
|
||||||
DialogActions,
|
|
||||||
Divider,
|
|
||||||
TextField
|
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
|
||||||
// Material UI - Icons
|
// Material UI - Icons
|
||||||
@ -43,10 +36,17 @@ import React, {
|
|||||||
// Styles
|
// Styles
|
||||||
import "reactflow/dist/style.css";
|
import "reactflow/dist/style.css";
|
||||||
|
|
||||||
// Import our new components
|
// Import Borealis Modules
|
||||||
import FlowTabs from "./Flow_Tabs";
|
import FlowTabs from "./Flow_Tabs";
|
||||||
import FlowEditor from "./Flow_Editor";
|
import FlowEditor from "./Flow_Editor";
|
||||||
import NodeSidebar from "./Node_Sidebar";
|
import NodeSidebar from "./Node_Sidebar";
|
||||||
|
import {
|
||||||
|
CloseAllDialog,
|
||||||
|
CreditsDialog,
|
||||||
|
RenameTabDialog,
|
||||||
|
TabContextMenu
|
||||||
|
} from "./Dialogs";
|
||||||
|
import StatusBar from "./Status_Bar";
|
||||||
|
|
||||||
// Global Node Update Timer Variable
|
// Global Node Update Timer Variable
|
||||||
if (!window.BorealisUpdateRate) {
|
if (!window.BorealisUpdateRate) {
|
||||||
@ -99,26 +99,16 @@ import React, {
|
|||||||
]);
|
]);
|
||||||
const [activeTabId, setActiveTabId] = useState("flow_1");
|
const [activeTabId, setActiveTabId] = useState("flow_1");
|
||||||
|
|
||||||
// About menu
|
|
||||||
const [aboutAnchorEl, setAboutAnchorEl] = useState(null);
|
const [aboutAnchorEl, setAboutAnchorEl] = useState(null);
|
||||||
const [creditsDialogOpen, setCreditsDialogOpen] = useState(false);
|
const [creditsDialogOpen, setCreditsDialogOpen] = useState(false);
|
||||||
|
|
||||||
// Close all flows
|
|
||||||
const [confirmCloseOpen, setConfirmCloseOpen] = useState(false);
|
const [confirmCloseOpen, setConfirmCloseOpen] = useState(false);
|
||||||
|
|
||||||
// Rename tab
|
|
||||||
const [renameDialogOpen, setRenameDialogOpen] = useState(false);
|
const [renameDialogOpen, setRenameDialogOpen] = useState(false);
|
||||||
const [renameTabId, setRenameTabId] = useState(null);
|
const [renameTabId, setRenameTabId] = useState(null);
|
||||||
const [renameValue, setRenameValue] = useState("");
|
const [renameValue, setRenameValue] = useState("");
|
||||||
|
|
||||||
// Right-click tab menu
|
|
||||||
const [tabMenuAnchor, setTabMenuAnchor] = useState(null);
|
const [tabMenuAnchor, setTabMenuAnchor] = useState(null);
|
||||||
const [tabMenuTabId, setTabMenuTabId] = useState(null);
|
const [tabMenuTabId, setTabMenuTabId] = useState(null);
|
||||||
|
|
||||||
// File input ref (for imports on older browsers)
|
|
||||||
const fileInputRef = useRef(null);
|
const fileInputRef = useRef(null);
|
||||||
|
|
||||||
// Setup callbacks to update nodes/edges in the currently active tab
|
|
||||||
const handleSetNodes = useCallback(
|
const handleSetNodes = useCallback(
|
||||||
(callbackOrArray, tId) => {
|
(callbackOrArray, tId) => {
|
||||||
const targetId = tId || activeTabId;
|
const targetId = tId || activeTabId;
|
||||||
@ -153,17 +143,13 @@ import React, {
|
|||||||
[activeTabId]
|
[activeTabId]
|
||||||
);
|
);
|
||||||
|
|
||||||
// About menu
|
|
||||||
const handleAboutMenuOpen = (event) => setAboutAnchorEl(event.currentTarget);
|
const handleAboutMenuOpen = (event) => setAboutAnchorEl(event.currentTarget);
|
||||||
const handleAboutMenuClose = () => setAboutAnchorEl(null);
|
const handleAboutMenuClose = () => setAboutAnchorEl(null);
|
||||||
|
|
||||||
// Credits
|
|
||||||
const openCreditsDialog = () => {
|
const openCreditsDialog = () => {
|
||||||
handleAboutMenuClose();
|
handleAboutMenuClose();
|
||||||
setCreditsDialogOpen(true);
|
setCreditsDialogOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Close all dialog
|
|
||||||
const handleOpenCloseAllDialog = () => setConfirmCloseOpen(true);
|
const handleOpenCloseAllDialog = () => setConfirmCloseOpen(true);
|
||||||
const handleCloseDialog = () => setConfirmCloseOpen(false);
|
const handleCloseDialog = () => setConfirmCloseOpen(false);
|
||||||
const handleConfirmCloseAll = () => {
|
const handleConfirmCloseAll = () => {
|
||||||
@ -179,7 +165,6 @@ import React, {
|
|||||||
setConfirmCloseOpen(false);
|
setConfirmCloseOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create new tab
|
|
||||||
const createNewTab = () => {
|
const createNewTab = () => {
|
||||||
const nextIndex = tabs.length + 1;
|
const nextIndex = tabs.length + 1;
|
||||||
const newId = "flow_" + nextIndex;
|
const newId = "flow_" + nextIndex;
|
||||||
@ -195,23 +180,21 @@ import React, {
|
|||||||
setActiveTabId(newId);
|
setActiveTabId(newId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Handle user clicking on a tab
|
|
||||||
const handleTabChange = (newActiveTabId) => {
|
const handleTabChange = (newActiveTabId) => {
|
||||||
setActiveTabId(newActiveTabId);
|
setActiveTabId(newActiveTabId);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Right-click tab menu
|
|
||||||
const handleTabRightClick = (evt, tabId) => {
|
const handleTabRightClick = (evt, tabId) => {
|
||||||
evt.preventDefault();
|
evt.preventDefault();
|
||||||
setTabMenuAnchor({ x: evt.clientX, y: evt.clientY });
|
setTabMenuAnchor({ x: evt.clientX, y: evt.clientY });
|
||||||
setTabMenuTabId(tabId);
|
setTabMenuTabId(tabId);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseTabMenu = () => {
|
const handleCloseTabMenu = () => {
|
||||||
setTabMenuAnchor(null);
|
setTabMenuAnchor(null);
|
||||||
setTabMenuTabId(null);
|
setTabMenuTabId(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Rename / close tab
|
|
||||||
const handleRenameTab = () => {
|
const handleRenameTab = () => {
|
||||||
setRenameDialogOpen(true);
|
setRenameDialogOpen(true);
|
||||||
setRenameTabId(tabMenuTabId);
|
setRenameTabId(tabMenuTabId);
|
||||||
@ -219,6 +202,7 @@ import React, {
|
|||||||
setRenameValue(t ? t.tab_name : "");
|
setRenameValue(t ? t.tab_name : "");
|
||||||
handleCloseTabMenu();
|
handleCloseTabMenu();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleCloseTab = () => {
|
const handleCloseTab = () => {
|
||||||
setTabs((old) => {
|
setTabs((old) => {
|
||||||
const idx = old.findIndex((t) => t.id === tabMenuTabId);
|
const idx = old.findIndex((t) => t.id === tabMenuTabId);
|
||||||
@ -227,11 +211,9 @@ import React, {
|
|||||||
const newList = [...old];
|
const newList = [...old];
|
||||||
newList.splice(idx, 1);
|
newList.splice(idx, 1);
|
||||||
|
|
||||||
// If we closed the current tab, pick a new active tab
|
|
||||||
if (tabMenuTabId === activeTabId && newList.length > 0) {
|
if (tabMenuTabId === activeTabId && newList.length > 0) {
|
||||||
setActiveTabId(newList[0].id);
|
setActiveTabId(newList[0].id);
|
||||||
} else if (newList.length === 0) {
|
} else if (newList.length === 0) {
|
||||||
// If we closed the only tab, create a fresh one
|
|
||||||
newList.push({
|
newList.push({
|
||||||
id: "flow_1",
|
id: "flow_1",
|
||||||
tab_name: "Flow 1",
|
tab_name: "Flow 1",
|
||||||
@ -260,12 +242,10 @@ import React, {
|
|||||||
setRenameDialogOpen(false);
|
setRenameDialogOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Export current tab
|
|
||||||
const handleExportFlow = async () => {
|
const handleExportFlow = async () => {
|
||||||
const activeTab = tabs.find((x) => x.id === activeTabId);
|
const activeTab = tabs.find((x) => x.id === activeTabId);
|
||||||
if (!activeTab) return;
|
if (!activeTab) return;
|
||||||
|
|
||||||
// Build JSON data from the active tab
|
|
||||||
const data = JSON.stringify(
|
const data = JSON.stringify(
|
||||||
{
|
{
|
||||||
nodes: activeTab.nodes,
|
nodes: activeTab.nodes,
|
||||||
@ -276,15 +256,9 @@ import React, {
|
|||||||
2
|
2
|
||||||
);
|
);
|
||||||
const blob = new Blob([data], { type: "application/json" });
|
const blob = new Blob([data], { type: "application/json" });
|
||||||
|
const sanitizedTabName = activeTab.tab_name.replace(/\s+/g, "_").toLowerCase();
|
||||||
// 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 suggestedFilename = sanitizedTabName + "_workflow.json";
|
const suggestedFilename = sanitizedTabName + "_workflow.json";
|
||||||
|
|
||||||
// Check if showSaveFilePicker is available (Chrome/Edge)
|
|
||||||
if (window.showSaveFilePicker) {
|
if (window.showSaveFilePicker) {
|
||||||
try {
|
try {
|
||||||
const fileHandle = await window.showSaveFilePicker({
|
const fileHandle = await window.showSaveFilePicker({
|
||||||
@ -304,21 +278,17 @@ import React, {
|
|||||||
console.error("Save cancelled or failed:", err);
|
console.error("Save cancelled or failed:", err);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback for browsers like Firefox
|
|
||||||
// (Relies on browser settings to ask user where to save)
|
|
||||||
const a = document.createElement("a");
|
const a = document.createElement("a");
|
||||||
a.href = URL.createObjectURL(blob);
|
a.href = URL.createObjectURL(blob);
|
||||||
a.download = suggestedFilename; // e.g. nicole_work_flow_workflow.json
|
a.download = suggestedFilename;
|
||||||
a.style.display = "none";
|
a.style.display = "none";
|
||||||
document.body.appendChild(a);
|
document.body.appendChild(a);
|
||||||
a.click();
|
a.click();
|
||||||
// Cleanup
|
|
||||||
URL.revokeObjectURL(a.href);
|
URL.revokeObjectURL(a.href);
|
||||||
document.body.removeChild(a);
|
document.body.removeChild(a);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Import flow -> new tab
|
|
||||||
const handleImportFlow = async () => {
|
const handleImportFlow = async () => {
|
||||||
if (window.showOpenFilePicker) {
|
if (window.showOpenFilePicker) {
|
||||||
try {
|
try {
|
||||||
@ -349,12 +319,10 @@ import React, {
|
|||||||
console.error("Import cancelled or failed:", err);
|
console.error("Import cancelled or failed:", err);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Fallback for older browsers
|
|
||||||
fileInputRef.current?.click();
|
fileInputRef.current?.click();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Fallback <input> import
|
|
||||||
const handleFileInputChange = async (e) => {
|
const handleFileInputChange = async (e) => {
|
||||||
const file = e.target.files[0];
|
const file = e.target.files[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
@ -382,35 +350,11 @@ import React, {
|
|||||||
<ThemeProvider theme={darkTheme}>
|
<ThemeProvider theme={darkTheme}>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
|
|
||||||
<Box
|
<Box sx={{ width: "100vw", height: "100vh", display: "flex", flexDirection: "column", overflow: "hidden" }}>
|
||||||
sx={{
|
|
||||||
width: "100vw",
|
|
||||||
height: "100vh",
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
overflow: "hidden"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<AppBar position="static" sx={{ bgcolor: "#16191d" }}>
|
<AppBar position="static" sx={{ bgcolor: "#16191d" }}>
|
||||||
<Toolbar sx={{ minHeight: "36px" }}>
|
<Toolbar sx={{ minHeight: "36px" }}>
|
||||||
{/* Logo */}
|
<Box component="img" src="/Borealis_Logo_Full.png" alt="Borealis Logo" sx={{ height: "52px", marginRight: "8px" }} />
|
||||||
<Box
|
<Typography variant="h6" sx={{ flexGrow: 1, fontSize: "1rem" }}></Typography>
|
||||||
component="img"
|
|
||||||
src="/Borealis_Logo_Full.png"
|
|
||||||
alt="Borealis Logo"
|
|
||||||
sx={{
|
|
||||||
height: "52px",
|
|
||||||
marginRight: "8px"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Typography
|
|
||||||
variant="h6"
|
|
||||||
sx={{ flexGrow: 1, fontSize: "1rem" }}
|
|
||||||
>
|
|
||||||
{/* Additional Title/Info if desired */}
|
|
||||||
</Typography>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
color="inherit"
|
color="inherit"
|
||||||
onClick={handleAboutMenuOpen}
|
onClick={handleAboutMenuOpen}
|
||||||
@ -420,43 +364,18 @@ import React, {
|
|||||||
>
|
>
|
||||||
About
|
About
|
||||||
</Button>
|
</Button>
|
||||||
|
<Menu anchorEl={aboutAnchorEl} open={Boolean(aboutAnchorEl)} onClose={handleAboutMenuClose}>
|
||||||
<Menu
|
<MenuItem onClick={() => { handleAboutMenuClose(); window.open("https://git.bunny-lab.io/Borealis", "_blank"); }}>
|
||||||
anchorEl={aboutAnchorEl}
|
<MergeTypeIcon sx={{ fontSize: 18, color: "#58a6ff", mr: 1 }} /> Gitea Project
|
||||||
open={Boolean(aboutAnchorEl)}
|
|
||||||
onClose={handleAboutMenuClose}
|
|
||||||
>
|
|
||||||
<MenuItem
|
|
||||||
onClick={() => {
|
|
||||||
handleAboutMenuClose();
|
|
||||||
window.open("https://git.bunny-lab.io/Borealis", "_blank");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MergeTypeIcon
|
|
||||||
sx={{
|
|
||||||
fontSize: 18,
|
|
||||||
color: "#58a6ff",
|
|
||||||
mr: 1
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
Gitea Project
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem onClick={openCreditsDialog}>
|
<MenuItem onClick={openCreditsDialog}>
|
||||||
<PeopleIcon
|
<PeopleIcon sx={{ fontSize: 18, color: "#58a6ff", mr: 1 }} /> Credits
|
||||||
sx={{
|
|
||||||
fontSize: 18,
|
|
||||||
color: "#58a6ff",
|
|
||||||
mr: 1
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
Credits
|
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</Menu>
|
</Menu>
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
</AppBar>
|
</AppBar>
|
||||||
|
|
||||||
<Box sx={{ display: "flex", flexGrow: 1, overflow: "hidden" }}>
|
<Box sx={{ display: "flex", flexGrow: 1, overflow: "hidden" }}>
|
||||||
{/* Sidebar */}
|
|
||||||
<NodeSidebar
|
<NodeSidebar
|
||||||
categorizedNodes={categorizedNodes}
|
categorizedNodes={categorizedNodes}
|
||||||
handleExportFlow={handleExportFlow}
|
handleExportFlow={handleExportFlow}
|
||||||
@ -466,16 +385,7 @@ import React, {
|
|||||||
onFileInputChange={handleFileInputChange}
|
onFileInputChange={handleFileInputChange}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Right content: tab bar + flow editors */}
|
<Box sx={{ display: "flex", flexDirection: "column", flexGrow: 1, overflow: "hidden" }}>
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
flexGrow: 1,
|
|
||||||
overflow: "hidden"
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{/* Tab bar */}
|
|
||||||
<FlowTabs
|
<FlowTabs
|
||||||
tabs={tabs}
|
tabs={tabs}
|
||||||
activeTabId={activeTabId}
|
activeTabId={activeTabId}
|
||||||
@ -484,7 +394,6 @@ import React, {
|
|||||||
onTabRightClick={handleTabRightClick}
|
onTabRightClick={handleTabRightClick}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* The flow editors themselves */}
|
|
||||||
<Box sx={{ flexGrow: 1, position: "relative" }}>
|
<Box sx={{ flexGrow: 1, position: "relative" }}>
|
||||||
{tabs.map((tab) => (
|
{tabs.map((tab) => (
|
||||||
<Box
|
<Box
|
||||||
@ -514,188 +423,28 @@ import React, {
|
|||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Bottom status bar */}
|
<StatusBar />
|
||||||
<Box
|
|
||||||
component="footer"
|
|
||||||
sx={{
|
|
||||||
bgcolor: "#1e1e1e",
|
|
||||||
color: "white",
|
|
||||||
px: 2,
|
|
||||||
py: 1,
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: 2
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<b>Nodes</b>: <span id="nodeCount">0</span>
|
|
||||||
<Divider
|
|
||||||
orientation="vertical"
|
|
||||||
flexItem
|
|
||||||
sx={{ borderColor: "#444" }}
|
|
||||||
/>
|
|
||||||
<b>Update Rate (ms):</b>
|
|
||||||
<input
|
|
||||||
id="updateRateInput"
|
|
||||||
type="number"
|
|
||||||
min="50"
|
|
||||||
step="50"
|
|
||||||
defaultValue={window.BorealisUpdateRate}
|
|
||||||
style={{
|
|
||||||
width: "80px",
|
|
||||||
background: "#121212",
|
|
||||||
color: "#fff",
|
|
||||||
border: "1px solid #444",
|
|
||||||
borderRadius: "3px",
|
|
||||||
padding: "3px",
|
|
||||||
fontSize: "0.8rem"
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
variant="outlined"
|
|
||||||
size="small"
|
|
||||||
onClick={() => {
|
|
||||||
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).");
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
sx={{
|
|
||||||
color: "#58a6ff",
|
|
||||||
borderColor: "#58a6ff",
|
|
||||||
fontSize: "0.75rem",
|
|
||||||
textTransform: "none",
|
|
||||||
px: 1.5
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
Apply Rate
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{/* Close All Dialog */}
|
<CloseAllDialog
|
||||||
<Dialog
|
|
||||||
open={confirmCloseOpen}
|
open={confirmCloseOpen}
|
||||||
onClose={handleCloseDialog}
|
onClose={handleCloseDialog}
|
||||||
PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}
|
onConfirm={handleConfirmCloseAll}
|
||||||
>
|
/>
|
||||||
<DialogTitle>Close All Flow Tabs?</DialogTitle>
|
<CreditsDialog open={creditsDialogOpen} onClose={() => setCreditsDialogOpen(false)} />
|
||||||
<DialogContent>
|
<RenameTabDialog
|
||||||
<DialogContentText sx={{ color: "#ccc" }}>
|
|
||||||
This will remove all existing flow tabs and create a fresh tab named Flow 1.
|
|
||||||
</DialogContentText>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
onClick={handleCloseDialog}
|
|
||||||
sx={{ color: "#58a6ff" }}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={handleConfirmCloseAll}
|
|
||||||
sx={{ color: "#ff4f4f" }}
|
|
||||||
>
|
|
||||||
Close All
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
{/* Credits */}
|
|
||||||
<Dialog
|
|
||||||
open={creditsDialogOpen}
|
|
||||||
onClose={() => setCreditsDialogOpen(false)}
|
|
||||||
PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}
|
|
||||||
>
|
|
||||||
<DialogTitle>Borealis Workflow Automation Tool</DialogTitle>
|
|
||||||
<DialogContent>
|
|
||||||
<DialogContentText sx={{ color: "#ccc" }}>
|
|
||||||
Designed by Nicole Rappe @ Bunny Lab
|
|
||||||
</DialogContentText>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
onClick={() => setCreditsDialogOpen(false)}
|
|
||||||
sx={{ color: "#58a6ff" }}
|
|
||||||
>
|
|
||||||
Close
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
|
|
||||||
{/* Tab Context Menu */}
|
|
||||||
<Menu
|
|
||||||
open={Boolean(tabMenuAnchor)}
|
|
||||||
onClose={handleCloseTabMenu}
|
|
||||||
anchorReference="anchorPosition"
|
|
||||||
anchorPosition={
|
|
||||||
tabMenuAnchor
|
|
||||||
? { top: tabMenuAnchor.y, left: tabMenuAnchor.x }
|
|
||||||
: undefined
|
|
||||||
}
|
|
||||||
PaperProps={{
|
|
||||||
sx: {
|
|
||||||
bgcolor: "#1e1e1e",
|
|
||||||
color: "#fff",
|
|
||||||
fontSize: "13px"
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<MenuItem onClick={handleRenameTab}>Rename</MenuItem>
|
|
||||||
<MenuItem onClick={handleCloseTab}>Close</MenuItem>
|
|
||||||
</Menu>
|
|
||||||
|
|
||||||
{/* Rename Tab Dialog */}
|
|
||||||
<Dialog
|
|
||||||
open={renameDialogOpen}
|
open={renameDialogOpen}
|
||||||
onClose={() => setRenameDialogOpen(false)}
|
value={renameValue}
|
||||||
PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}
|
onChange={setRenameValue}
|
||||||
>
|
onCancel={() => setRenameDialogOpen(false)}
|
||||||
<DialogTitle>Rename Tab</DialogTitle>
|
onSave={handleRenameDialogSave}
|
||||||
<DialogContent>
|
/>
|
||||||
<TextField
|
<TabContextMenu
|
||||||
autoFocus
|
anchor={tabMenuAnchor}
|
||||||
margin="dense"
|
onClose={handleCloseTabMenu}
|
||||||
label="Tab Name"
|
onRename={handleRenameTab}
|
||||||
fullWidth
|
onCloseTab={handleCloseTab}
|
||||||
variant="outlined"
|
/>
|
||||||
value={renameValue}
|
|
||||||
onChange={(e) => setRenameValue(e.target.value)}
|
|
||||||
sx={{
|
|
||||||
"& .MuiOutlinedInput-root": {
|
|
||||||
backgroundColor: "#2a2a2a",
|
|
||||||
color: "#ccc",
|
|
||||||
"& fieldset": {
|
|
||||||
borderColor: "#444"
|
|
||||||
},
|
|
||||||
"&:hover fieldset": {
|
|
||||||
borderColor: "#666"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
label: { color: "#aaa" },
|
|
||||||
mt: 1
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</DialogContent>
|
|
||||||
<DialogActions>
|
|
||||||
<Button
|
|
||||||
onClick={() => setRenameDialogOpen(false)}
|
|
||||||
sx={{ color: "#58a6ff" }}
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={handleRenameDialogSave}
|
|
||||||
sx={{ color: "#58a6ff" }}
|
|
||||||
>
|
|
||||||
Save
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
105
Data/WebUI/src/Dialogs.jsx
Normal file
105
Data/WebUI/src/Dialogs.jsx
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/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 (
|
||||||
|
<Dialog open={open} onClose={onClose} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}>
|
||||||
|
<DialogTitle>Close All Flow Tabs?</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText sx={{ color: "#ccc" }}>
|
||||||
|
This will remove all existing flow tabs and create a fresh tab named Flow 1.
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={onClose} sx={{ color: "#58a6ff" }}>Cancel</Button>
|
||||||
|
<Button onClick={onConfirm} sx={{ color: "#ff4f4f" }}>Close All</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CreditsDialog({ open, onClose }) {
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onClose={onClose} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}>
|
||||||
|
<DialogTitle>Borealis Workflow Automation Tool</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText sx={{ color: "#ccc" }}>
|
||||||
|
Designed by Nicole Rappe @ Bunny Lab
|
||||||
|
</DialogContentText>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={onClose} sx={{ color: "#58a6ff" }}>Close</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RenameTabDialog({ open, value, onChange, onCancel, onSave }) {
|
||||||
|
return (
|
||||||
|
<Dialog open={open} onClose={onCancel} PaperProps={{ sx: { bgcolor: "#121212", color: "#fff" } }}>
|
||||||
|
<DialogTitle>Rename Tab</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<TextField
|
||||||
|
autoFocus
|
||||||
|
margin="dense"
|
||||||
|
label="Tab Name"
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => onChange(e.target.value)}
|
||||||
|
sx={{
|
||||||
|
"& .MuiOutlinedInput-root": {
|
||||||
|
backgroundColor: "#2a2a2a",
|
||||||
|
color: "#ccc",
|
||||||
|
"& fieldset": {
|
||||||
|
borderColor: "#444"
|
||||||
|
},
|
||||||
|
"&:hover fieldset": {
|
||||||
|
borderColor: "#666"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
label: { color: "#aaa" },
|
||||||
|
mt: 1
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={onCancel} sx={{ color: "#58a6ff" }}>Cancel</Button>
|
||||||
|
<Button onClick={onSave} sx={{ color: "#58a6ff" }}>Save</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function TabContextMenu({ anchor, onClose, onRename, onCloseTab }) {
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
open={Boolean(anchor)}
|
||||||
|
onClose={onClose}
|
||||||
|
anchorReference="anchorPosition"
|
||||||
|
anchorPosition={anchor ? { top: anchor.y, left: anchor.x } : undefined}
|
||||||
|
PaperProps={{
|
||||||
|
sx: {
|
||||||
|
bgcolor: "#1e1e1e",
|
||||||
|
color: "#fff",
|
||||||
|
fontSize: "13px"
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MenuItem onClick={onRename}>Rename</MenuItem>
|
||||||
|
<MenuItem onClick={onCloseTab}>Close</MenuItem>
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
}
|
@ -6,7 +6,6 @@ import {
|
|||||||
AccordionSummary,
|
AccordionSummary,
|
||||||
AccordionDetails,
|
AccordionDetails,
|
||||||
Button,
|
Button,
|
||||||
Divider,
|
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography
|
Typography
|
||||||
} from "@mui/material";
|
} from "@mui/material";
|
||||||
|
71
Data/WebUI/src/Status_Bar.jsx
Normal file
71
Data/WebUI/src/Status_Bar.jsx
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
////////// PROJECT FILE SEPARATION LINE ////////// CODE AFTER THIS LINE ARE FROM: <ProjectRoot>/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 (
|
||||||
|
<Box
|
||||||
|
component="footer"
|
||||||
|
sx={{
|
||||||
|
bgcolor: "#1e1e1e",
|
||||||
|
color: "white",
|
||||||
|
px: 2,
|
||||||
|
py: 1,
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 2
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<b>Nodes</b>: <span id="nodeCount">0</span>
|
||||||
|
<Divider
|
||||||
|
orientation="vertical"
|
||||||
|
flexItem
|
||||||
|
sx={{ borderColor: "#444" }}
|
||||||
|
/>
|
||||||
|
<b>Update Rate (ms):</b>
|
||||||
|
<input
|
||||||
|
id="updateRateInput"
|
||||||
|
type="number"
|
||||||
|
min="50"
|
||||||
|
step="50"
|
||||||
|
defaultValue={window.BorealisUpdateRate}
|
||||||
|
style={{
|
||||||
|
width: "80px",
|
||||||
|
background: "#121212",
|
||||||
|
color: "#fff",
|
||||||
|
border: "1px solid #444",
|
||||||
|
borderRadius: "3px",
|
||||||
|
padding: "3px",
|
||||||
|
fontSize: "0.8rem"
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
size="small"
|
||||||
|
onClick={applyRate}
|
||||||
|
sx={{
|
||||||
|
color: "#58a6ff",
|
||||||
|
borderColor: "#58a6ff",
|
||||||
|
fontSize: "0.75rem",
|
||||||
|
textTransform: "none",
|
||||||
|
px: 1.5
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Apply Rate
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user