From ca1348fcb8873ab674405ea138843fd3f23d1cba Mon Sep 17 00:00:00 2001 From: Nicole Rappe Date: Tue, 6 May 2025 05:27:42 -0600 Subject: [PATCH] Introduced Refresh-Resistant Session Persistence --- Data/Server/WebUI/src/App.jsx | 44 ++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/Data/Server/WebUI/src/App.jsx b/Data/Server/WebUI/src/App.jsx index 96a9347..733638b 100644 --- a/Data/Server/WebUI/src/App.jsx +++ b/Data/Server/WebUI/src/App.jsx @@ -57,12 +57,10 @@ if (!window.BorealisSocket) { }); } -// Global Node Update Timer Variable if (!window.BorealisUpdateRate) { window.BorealisUpdateRate = 200; } -// Dynamically load all node components via Vite const modules = import.meta.glob('./nodes/**/*.jsx', { eager: true }); const nodeTypes = {}; const categorizedNodes = {}; @@ -72,11 +70,8 @@ Object.entries(modules).forEach(([path, mod]) => { if (!comp) return; const { type, component } = comp; if (!type || !component) return; - - // derive category folder name from path: "./nodes//File.jsx" const parts = path.replace('./nodes/', '').split('/'); const category = parts[0]; - if (!categorizedNodes[category]) { categorizedNodes[category] = []; } @@ -112,6 +107,7 @@ const darkTheme = createTheme({ } }); +const LOCAL_STORAGE_KEY = "borealis_persistent_state"; export default function App() { const [tabs, setTabs] = useState([ @@ -134,6 +130,29 @@ export default function App() { const [tabMenuTabId, setTabMenuTabId] = useState(null); const fileInputRef = useRef(null); + useEffect(() => { + const saved = localStorage.getItem(LOCAL_STORAGE_KEY); + if (saved) { + try { + const parsed = JSON.parse(saved); + if (Array.isArray(parsed.tabs) && parsed.activeTabId) { + setTabs(parsed.tabs); + setActiveTabId(parsed.activeTabId); + } + } catch (err) { + console.warn("Failed to parse saved state:", err); + } + } + }, []); + + useEffect(() => { + const timeout = setTimeout(() => { + const data = JSON.stringify({ tabs, activeTabId }); + localStorage.setItem(LOCAL_STORAGE_KEY, data); + }, 1000); + return () => clearTimeout(timeout); + }, [tabs, activeTabId]); + const handleSetNodes = useCallback( (callbackOrArray, tId) => { const targetId = tId || activeTabId; @@ -232,10 +251,8 @@ export default function App() { setTabs((old) => { const idx = old.findIndex((t) => t.id === tabMenuTabId); if (idx === -1) return old; - const newList = [...old]; newList.splice(idx, 1); - if (tabMenuTabId === activeTabId && newList.length > 0) { setActiveTabId(newList[0].id); } else if (newList.length === 0) { @@ -270,7 +287,6 @@ export default function App() { const handleExportFlow = async () => { const activeTab = tabs.find((x) => x.id === activeTabId); if (!activeTab) return; - const data = JSON.stringify( { nodes: activeTab.nodes, @@ -283,7 +299,6 @@ export default function App() { const blob = new Blob([data], { type: "application/json" }); const sanitizedTabName = activeTab.tab_name.replace(/\s+/g, "_").toLowerCase(); const suggestedFilename = sanitizedTabName + "_workflow.json"; - if (window.showSaveFilePicker) { try { const fileHandle = await window.showSaveFilePicker({ @@ -295,7 +310,6 @@ export default function App() { } ] }); - const writable = await fileHandle.createWritable(); await writable.write(blob); await writable.close(); @@ -328,7 +342,6 @@ export default function App() { const file = await fileHandle.getFile(); const text = await file.text(); const json = JSON.parse(text); - const newId = "flow_" + (tabs.length + 1); setTabs((prev) => [ ...prev, @@ -354,7 +367,6 @@ export default function App() { try { const text = await file.text(); const json = JSON.parse(text); - const newId = "flow_" + (tabs.length + 1); setTabs((prev) => [ ...prev, @@ -374,7 +386,6 @@ export default function App() { return ( - @@ -399,7 +410,6 @@ export default function App() { - - - {tabs.map((tab) => ( handleSetNodes(val, tab.id)} @@ -448,10 +456,8 @@ export default function App() { - -