Fix frontend HMR so the page doesn't break upon saving TS files and restructure frontend architecture (#3871)

* Clean up component setup/tear-down side effects

* Clean up more component setup/tear-down side effects

* Remove nonfunctional debouncer

* Clean up even more component setup/tear-down side effects

* Reuse backend state

* Fix HMR for IO Managers and for State Providers

* Rename IO Managers -> Managers and State Providers -> Stores

* Restructure and partially flatten managers/stores

* Code review fixes

* Review fixes
This commit is contained in:
Keavon Chambers
2026-03-19 13:38:41 -07:00
committed by GitHub
parent eb562146ce
commit 124b17f609
54 changed files with 1385 additions and 931 deletions
+36 -40
View File
@@ -2,20 +2,20 @@
import { onMount, onDestroy, setContext } from "svelte";
import type { Editor } from "@graphite/editor";
import { createClipboardManager } from "@graphite/io-managers/clipboard";
import { createHyperlinkManager } from "@graphite/io-managers/hyperlink";
import { createInputManager } from "@graphite/io-managers/input";
import { createLocalizationManager } from "@graphite/io-managers/localization";
import { createPanicManager } from "@graphite/io-managers/panic";
import { createPersistenceManager } from "@graphite/io-managers/persistence";
import { createAppWindowState } from "@graphite/state-providers/app-window";
import { createDialogState } from "@graphite/state-providers/dialog";
import { createDocumentState } from "@graphite/state-providers/document";
import { createFontsManager } from "/src/io-managers/fonts";
import { createFullscreenState } from "@graphite/state-providers/fullscreen";
import { createNodeGraphState } from "@graphite/state-providers/node-graph";
import { createPortfolioState } from "@graphite/state-providers/portfolio";
import { createTooltipState } from "@graphite/state-providers/tooltip";
import { createClipboardManager } from "@graphite/managers/clipboard";
import { createFontsManager } from "@graphite/managers/fonts";
import { createHyperlinkManager } from "@graphite/managers/hyperlink";
import { createInputManager } from "@graphite/managers/input";
import { createLocalizationManager } from "@graphite/managers/localization";
import { createPanicManager } from "@graphite/managers/panic";
import { createPersistenceManager } from "@graphite/managers/persistence";
import { createAppWindowStore } from "@graphite/stores/app-window";
import { createDialogStore } from "@graphite/stores/dialog";
import { createDocumentStore } from "@graphite/stores/document";
import { createFullscreenStore } from "@graphite/stores/fullscreen";
import { createNodeGraphStore } from "@graphite/stores/node-graph";
import { createPortfolioStore } from "@graphite/stores/portfolio";
import { createTooltipStore } from "@graphite/stores/tooltip";
import MainWindow from "@graphite/components/window/MainWindow.svelte";
@@ -23,39 +23,35 @@
export let editor: Editor;
setContext("editor", editor);
// State provider systems
let dialog = createDialogState(editor);
setContext("dialog", dialog);
let tooltip = createTooltipState(editor);
setContext("tooltip", tooltip);
let document = createDocumentState(editor);
setContext("document", document);
let fullscreen = createFullscreenState(editor);
setContext("fullscreen", fullscreen);
let nodeGraph = createNodeGraphState(editor);
setContext("nodeGraph", nodeGraph);
let portfolio = createPortfolioState(editor);
setContext("portfolio", portfolio);
let appWindow = createAppWindowState(editor);
setContext("appWindow", appWindow);
const stores = {
dialog: createDialogStore(editor),
tooltip: createTooltipStore(editor),
document: createDocumentStore(editor),
fullscreen: createFullscreenStore(editor),
nodeGraph: createNodeGraphStore(editor),
portfolio: createPortfolioStore(editor),
appWindow: createAppWindowStore(editor),
};
Object.entries(stores).forEach(([key, store]) => setContext(key, store));
// Initialize managers, which are isolated systems that subscribe to backend messages to link them to browser API functionality (like JS events, IndexedDB, etc.)
createClipboardManager(editor);
createHyperlinkManager(editor);
createLocalizationManager(editor);
createPanicManager(editor, dialog);
createPersistenceManager(editor, portfolio);
createFontsManager(editor);
let inputManagerDestructor = createInputManager(editor, dialog, portfolio, document, fullscreen);
const managers = {
clipboard: createClipboardManager(editor),
hyperlink: createHyperlinkManager(editor),
localization: createLocalizationManager(editor),
panic: createPanicManager(editor),
persistence: createPersistenceManager(editor, stores.portfolio),
fonts: createFontsManager(editor),
input: createInputManager(editor, stores.dialog, stores.portfolio, stores.document, stores.fullscreen),
};
onMount(() => {
// Initialize certain setup tasks required by the editor backend to be ready for the user now that the frontend is ready
// Initialize certain setup tasks required by the editor backend to be ready for the user now that the frontend is ready.
// The backend handles idempotency, so this is safe to call again during HMR re-mounts.
editor.handle.initAfterFrontendReady();
});
onDestroy(() => {
// Call the destructor for each manager
inputManagerDestructor();
[...Object.values(stores), ...Object.values(managers)].forEach(({ destroy }) => destroy());
});
</script>