From ed7987c8818b8a3851fe257faabbe0dd7ff8338d Mon Sep 17 00:00:00 2001 From: Keavon Chambers Date: Fri, 20 Mar 2026 23:34:13 -0700 Subject: [PATCH] Split apart the frontend Editor type into SubscriptionsRouter and EditorHandle (#3923) * Remove the unused Editor.raw/wasmMemory/wasmImport * Split out Editor.subscriptions * Replace editor.handle.* with editor.* (1 of 2) * Replace editor.handle.* with editor.* (2 of 2) * Replace Editor typedef with EditorHandle import * Pluralize subscription-router and rename subscriptionsRef->subscriptionsRouter and editorRef->editorHandle * Remove editor.ts * Update the readme * Fix demo art loading bug --- frontend/src/App.svelte | 34 ++++++-- frontend/src/README.md | 28 +++--- frontend/src/components/Editor.svelte | 41 +++++---- .../components/floating-menus/Tooltip.svelte | 7 +- frontend/src/components/panels/Data.svelte | 8 +- .../src/components/panels/Document.svelte | 85 +++++++++--------- frontend/src/components/panels/Layers.svelte | 45 +++++----- .../src/components/panels/Properties.svelte | 8 +- frontend/src/components/panels/Welcome.svelte | 11 +-- frontend/src/components/views/Graph.svelte | 35 ++++---- .../components/widgets/WidgetSection.svelte | 11 ++- .../src/components/widgets/WidgetSpan.svelte | 11 ++- .../widgets/inputs/NumberInput.svelte | 7 +- .../widgets/inputs/WorkingColorsInput.svelte | 9 +- frontend/src/components/window/Panel.svelte | 6 +- .../src/components/window/StatusBar.svelte | 13 +-- .../src/components/window/TitleBar.svelte | 21 ++--- .../src/components/window/Workspace.svelte | 13 ++- frontend/src/editor.ts | 86 ------------------- frontend/src/managers/clipboard.ts | 31 ++++--- frontend/src/managers/fonts.ts | 29 ++++--- frontend/src/managers/hyperlink.ts | 18 ++-- frontend/src/managers/input.ts | 44 +++++----- frontend/src/managers/localization.ts | 23 ++--- frontend/src/managers/panic.ts | 18 ++-- frontend/src/managers/persistence.ts | 49 ++++++----- frontend/src/stores/app-window.ts | 32 +++---- frontend/src/stores/dialog.ts | 40 ++++----- frontend/src/stores/document.ts | 40 ++++----- frontend/src/stores/fullscreen.ts | 16 ++-- frontend/src/stores/node-graph.ts | 86 +++++++++---------- frontend/src/stores/portfolio.ts | 68 +++++++-------- frontend/src/stores/tooltip.ts | 24 +++--- ...tion-router.ts => subscriptions-router.ts} | 4 +- frontend/src/utility-functions/clipboard.ts | 12 +-- frontend/src/utility-functions/files.ts | 12 +-- frontend/src/utility-functions/input.ts | 50 +++++------ frontend/src/utility-functions/network.ts | 22 +++++ frontend/src/utility-functions/persistence.ts | 30 +++---- frontend/src/utility-functions/viewports.ts | 6 +- 40 files changed, 549 insertions(+), 584 deletions(-) delete mode 100644 frontend/src/editor.ts rename frontend/src/{subscription-router.ts => subscriptions-router.ts} (97%) diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 55ff5d4d9d..4ad22865d1 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -1,24 +1,42 @@ -{#if editor !== undefined} - +{#if subscriptions !== undefined && editor !== undefined} + {/if} diff --git a/frontend/src/README.md b/frontend/src/README.md index 46e25d223f..100b10db8e 100644 --- a/frontend/src/README.md +++ b/frontend/src/README.md @@ -2,45 +2,39 @@ ## Svelte components: `components/` -Svelte components that build the Graphite editor GUI. These each contain a TypeScript section, a Svelte-templated HTML template section, and an SCSS stylesheet section. The aim is to avoid implementing much editor business logic here, just enough to make things interactive and communicate to the backend where the real business logic should occur. +Svelte components that build the Graphite editor GUI from layouts, panels, widgets, and menus. These each contain a TypeScript section, a Svelte-templated HTML template section, and an SCSS stylesheet section. The aim is to avoid implementing much editor business logic here, just enough to make things interactive and communicate to the backend where the real business logic should occur. ## Managers: `managers/` -TypeScript files which manage the input/output of browser APIs and link this functionality with the editor backend. These files subscribe to backend messages to execute JS APIs, and in response to these APIs or user interactions, they may call functions into the backend (defined in `/frontend/wasm/editor_api.rs`). +TypeScript files, constructed by the editor frontend, which manage the input/output of browser APIs and link this functionality with the editor backend. These files subscribe to frontend messages to execute JS APIs, and in response to these APIs or user interactions, they may call functions in the backend (defined in `/frontend/wasm/editor_api.rs`). -Each manager module exports a factory function (e.g. `createClipboardManager(editor)`) that sets up message subscriptions and returns a `{ destroy }` object. In `Editor.svelte`, each manager is created at startup and its `destroy()` method is called on unmount to clean up subscriptions and side-effects (e.g. event listeners). Managers use self-accepting HMR to tear down and re-create with updated code during development. +Each manager module stores its dependencies (like `subscriptionsRouter` and `editorHandle`) in module-level variables and exports a `create*()` and `destroy*()` function pair. `Editor.svelte` calls each `create*()` constructor in its `onMount` and calls each `destroy*()` in its `onDestroy`. Managers replace themselves during HMR updates if they are modified live during development. ## Stores: `stores/` -TypeScript files which provide reactive state to Svelte components. Each module persists a Svelte writable store at module level (surviving HMR via `import.meta.hot.data`) and exports a factory function (e.g. `createDialogStore(editor)`) that sets up backend message subscriptions and returns an object containing the store's `subscribe` method, any action methods for components to call, and a `destroy` method. +TypeScript files, constructed by the editor frontend, which provide reactive state to Svelte components. Each module persists a Svelte writable store at module level (surviving HMR via `import.meta.hot.data`) and exports a `create*()` function that sets up frontend message subscriptions and returns `{ subscribe }` (the shape required by Svelte's custom store contract). A corresponding `destroy*()` function is also exported. Some stores also export standalone action functions (like `createCrashDialog()` or `toggleFullscreen()`) as module-level exports. -In `Editor.svelte`, each store is created and passed to Svelte's `setContext()`. Components access stores via `getContext("dialog")` and use the `subscribe` method for reactive state and action methods (like `createCrashDialog()`) to trigger state changes. +In `Editor.svelte`, each store is created synchronously during component initialization (not in `onMount`, since child components need `getContext` access during their own initialization) and passed to Svelte's `setContext()`. Components access stores via calls like `getContext("dialog")`. Unlike managers, stores do not replace themselves during HMR; instead, `Editor.svelte` is remounted to replace them entirely. ## *Managers vs. stores* -*Both managers and stores subscribe to backend messages and may interact with browser APIs. The difference is that stores expose reactive state to components via `setContext()`/`getContext()`, while managers are self-contained systems that operate for the lifetime of the application and aren't accessed by Svelte components.* +*Both managers and stores subscribe to frontend messages and may interact with browser APIs. The difference is that stores expose reactive state to components via `setContext()`/`getContext()`, while managers are self-contained systems that operate for the lifetime of the application and aren't accessed by Svelte components.* ## Utility functions: `utility-functions/` TypeScript files which define and `export` individual helper functions for use elsewhere in the codebase. These files should not persist state outside each function. -## Wasm editor: `editor.ts` +## Subscriptions router: `subscriptions-router.ts` -Instantiates the Wasm and editor backend instances. The function `initWasm()` asynchronously constructs and initializes an instance of the Wasm bindings JS module provided by wasm-bindgen/wasm-pack. The function `createEditor()` constructs an instance of the editor backend. In theory there could be multiple editor instances sharing the same Wasm module instance. The function returns an object where `raw` is the Wasm memory, `handle` provides access to callable backend functions, and `subscriptions` is the subscription router (described below). - -`initWasm()` occurs in `main.ts` right before the Svelte application is mounted, then `createEditor()` is run in `Editor.svelte` during the Svelte app's creation. Similarly to the stores described above, the editor is given via `setContext()` so other components can get it via `getContext` and call functions on `editor.handle` or `editor.subscriptions`. - -## Subscription router: `subscription-router.ts` - -Associates messages from the backend with subscribers in the frontend, and routes messages to subscriber callbacks. This module provides a `subscribeFrontendMessage(messageType, callback)` function which JS code throughout the frontend can call to be registered as the exclusive handler for a chosen message type. The router's other function, `handleFrontendMessage(messageType, messageData)`, is called via the callback passed to `EditorHandle.create()` in `editor.ts` when the backend sends a `FrontendMessage`. When this occurs, the subscription router delivers the message to the subscriber by executing its registered `callback` function. +Associates messages from the backend with subscribers in the frontend, and routes messages to subscriber callbacks. This module provides a `subscribeFrontendMessage(messageType, callback)` function which JS code throughout the frontend can call to be registered as the exclusive handler for a chosen message type. The router's other function, `handleFrontendMessage(messageType, messageData)`, is called via the callback passed to `EditorHandle.create()` in `App.svelte` when the backend sends a `FrontendMessage`. When this occurs, the subscriptions router delivers the message to the subscriber by executing its registered `callback` function. ## Svelte app entry point: `App.svelte` -The entry point for the Svelte application. +The entry point for the Svelte application. Initializes the Wasm module, creates the `EditorHandle` backend instance and the subscriptions router, and renders `Editor.svelte` once both are ready. The `EditorHandle` is the wasm-bindgen interface to the Rust editor backend (defined in `/frontend/wasm/editor_api.rs`), providing access to callable backend functions. Both the editor and subscriptions router are passed as props to `Editor.svelte` and set as Svelte contexts for use throughout the component tree. ## Editor base instance: `Editor.svelte` -This is where we define global CSS style rules, construct all stores and managers with the editor instance, set store contexts for component access, and clean up all `destroy()` methods on unmount. +This is where we define global CSS style rules, construct all stores and managers, set store contexts for component access, and call each module's `destroy*()` function on unmount (on HMR during development). ## Global type augmentations: `global.d.ts` @@ -48,4 +42,4 @@ Extends built-in browser type definitions using TypeScript's interface merging. ## JS bundle entry point: `main.ts` -The entry point for the entire project's code bundle. Here we simply mount the Svelte application with `export default mount(App, { target: document.body });`. +The entry point for the entire project's code bundle. Mounts the Svelte application with `export default mount(App, { target: document.body })`. diff --git a/frontend/src/components/Editor.svelte b/frontend/src/components/Editor.svelte index e914bdabe8..a92d6c39b1 100644 --- a/frontend/src/components/Editor.svelte +++ b/frontend/src/components/Editor.svelte @@ -1,7 +1,7 @@ diff --git a/frontend/src/components/panels/Document.svelte b/frontend/src/components/panels/Document.svelte index a2c2fbbc60..881cca656a 100644 --- a/frontend/src/components/panels/Document.svelte +++ b/frontend/src/components/panels/Document.svelte @@ -1,11 +1,11 @@ @@ -628,7 +629,7 @@ open={Boolean(gradientStopPickerPosition && gradientStopPickerColor)} on:open={({ detail }) => { if (!detail) { - editor.handle.closeGradientStopColorPicker(); + editor.closeGradientStopColorPicker(); gradientStopPickerPosition = undefined; gradientStopPickerColor = undefined; } @@ -636,10 +637,10 @@ colorOrGradient={{ Solid: gradientStopPickerColor || createColor(0, 0, 0, 1) }} on:colorOrGradient={({ detail }) => { const color = fillChoiceColor(detail); - if (color) editor.handle.updateGradientStopColor(color.red, color.green, color.blue, color.alpha); + if (color) editor.updateGradientStopColor(color.red, color.green, color.blue, color.alpha); }} - on:startHistoryTransaction={() => editor.handle.startGradientStopColorTransaction()} - on:commitHistoryTransaction={() => editor.handle.commitGradientStopColorTransaction()} + on:startHistoryTransaction={() => editor.startGradientStopColorTransaction()} + on:commitHistoryTransaction={() => editor.commitGradientStopColorTransaction()} bind:this={gradientStopPicker} /> @@ -682,10 +683,10 @@ direction="Vertical" thumbLength={scrollbarSize.y} thumbPosition={scrollbarPos.y} - on:trackShift={({ detail }) => editor.handle.panCanvasByFraction(0, detail)} + on:trackShift={({ detail }) => editor.panCanvasByFraction(0, detail)} on:thumbPosition={({ detail }) => panCanvasY(detail)} - on:thumbDragStart={() => editor.handle.panCanvasAbortPrepare(false)} - on:thumbDragAbort={() => editor.handle.panCanvasAbort(false)} + on:thumbDragStart={() => editor.panCanvasAbortPrepare(false)} + on:thumbDragAbort={() => editor.panCanvasAbort(false)} /> @@ -694,10 +695,10 @@ direction="Horizontal" thumbLength={scrollbarSize.x} thumbPosition={scrollbarPos.x} - on:trackShift={({ detail }) => editor.handle.panCanvasByFraction(detail, 0)} + on:trackShift={({ detail }) => editor.panCanvasByFraction(detail, 0)} on:thumbPosition={({ detail }) => panCanvasX(detail)} - on:thumbDragStart={() => editor.handle.panCanvasAbortPrepare(true)} - on:thumbDragAbort={() => editor.handle.panCanvasAbort(true)} + on:thumbDragStart={() => editor.panCanvasAbortPrepare(true)} + on:thumbDragAbort={() => editor.panCanvasAbort(true)} /> diff --git a/frontend/src/components/panels/Layers.svelte b/frontend/src/components/panels/Layers.svelte index 03709fec0f..6ea82ecfc4 100644 --- a/frontend/src/components/panels/Layers.svelte +++ b/frontend/src/components/panels/Layers.svelte @@ -2,10 +2,10 @@ import { getContext, onMount, onDestroy, tick } from "svelte"; import { SvelteMap } from "svelte/reactivity"; - import type { LayerPanelEntry, LayerStructureEntry, Layout } from "@graphite/../wasm/pkg/graphite_wasm"; - import type { Editor } from "@graphite/editor"; + import type { EditorHandle, LayerPanelEntry, LayerStructureEntry, Layout } from "@graphite/../wasm/pkg/graphite_wasm"; import type { NodeGraphStore } from "@graphite/stores/node-graph"; import type { TooltipStore } from "@graphite/stores/tooltip"; + import type { SubscriptionsRouter } from "/src/subscriptions-router"; import { pasteFile } from "@graphite/utility-functions/files"; import { operatingSystem } from "@graphite/utility-functions/platform"; import { patchLayout } from "@graphite/utility-functions/widgets"; @@ -41,7 +41,8 @@ startY: number; }; - const editor = getContext("editor"); + const subscriptions = getContext("subscriptions"); + const editor = getContext("editor"); const nodeGraph = getContext("nodeGraph"); const tooltip = getContext("tooltip"); @@ -69,26 +70,26 @@ let layersPanelBottomBarLayout: Layout = []; onMount(() => { - editor.subscriptions.subscribeLayoutUpdate("LayersPanelControlLeftBar", (data) => { + subscriptions.subscribeLayoutUpdate("LayersPanelControlLeftBar", (data) => { patchLayout(layersPanelControlBarLeftLayout, data); layersPanelControlBarLeftLayout = layersPanelControlBarLeftLayout; }); - editor.subscriptions.subscribeLayoutUpdate("LayersPanelControlRightBar", (data) => { + subscriptions.subscribeLayoutUpdate("LayersPanelControlRightBar", (data) => { patchLayout(layersPanelControlBarRightLayout, data); layersPanelControlBarRightLayout = layersPanelControlBarRightLayout; }); - editor.subscriptions.subscribeLayoutUpdate("LayersPanelBottomBar", (data) => { + subscriptions.subscribeLayoutUpdate("LayersPanelBottomBar", (data) => { patchLayout(layersPanelBottomBarLayout, data); layersPanelBottomBarLayout = layersPanelBottomBarLayout; }); - editor.subscriptions.subscribeFrontendMessage("UpdateDocumentLayerStructure", (data) => { + subscriptions.subscribeFrontendMessage("UpdateDocumentLayerStructure", (data) => { rebuildLayerHierarchy(data.layerStructure); }); - editor.subscriptions.subscribeFrontendMessage("UpdateDocumentLayerDetails", (data) => { + subscriptions.subscribeFrontendMessage("UpdateDocumentLayerDetails", (data) => { const targetLayer = data.data; const targetId = targetLayer.id; @@ -107,11 +108,11 @@ }); onDestroy(() => { - editor.subscriptions.unsubscribeLayoutUpdate("LayersPanelControlLeftBar"); - editor.subscriptions.unsubscribeLayoutUpdate("LayersPanelControlRightBar"); - editor.subscriptions.unsubscribeLayoutUpdate("LayersPanelBottomBar"); - editor.subscriptions.unsubscribeFrontendMessage("UpdateDocumentLayerStructure"); - editor.subscriptions.unsubscribeFrontendMessage("UpdateDocumentLayerDetails"); + subscriptions.unsubscribeLayoutUpdate("LayersPanelControlLeftBar"); + subscriptions.unsubscribeLayoutUpdate("LayersPanelControlRightBar"); + subscriptions.unsubscribeLayoutUpdate("LayersPanelBottomBar"); + subscriptions.unsubscribeFrontendMessage("UpdateDocumentLayerStructure"); + subscriptions.unsubscribeFrontendMessage("UpdateDocumentLayerDetails"); removeEventListener("pointerup", draggingPointerUp); removeEventListener("pointermove", draggingPointerMove); @@ -125,17 +126,17 @@ }); function toggleNodeVisibilityLayerPanel(id: bigint) { - editor.handle.toggleNodeVisibilityLayerPanel(id); + editor.toggleNodeVisibilityLayerPanel(id); } function toggleLayerLock(id: bigint) { - editor.handle.toggleLayerLock(id); + editor.toggleLayerLock(id); } function handleExpandArrowClickWithModifiers(e: MouseEvent, id: bigint) { const accel = operatingSystem() === "Mac" ? e.metaKey : e.ctrlKey; const collapseRecursive = e.altKey || accel; - editor.handle.toggleLayerExpansion(id, collapseRecursive); + editor.toggleLayerExpansion(id, collapseRecursive); e.stopPropagation(); } @@ -162,7 +163,7 @@ layers = layers; const name = (e.target instanceof HTMLInputElement && e.target.value) || ""; - editor.handle.setLayerName(listing.entry.id, name); + editor.setLayerName(listing.entry.id, name); listing.entry.alias = name; } @@ -200,7 +201,7 @@ } function clipLayer(listing: LayerListingInfo) { - editor.handle.clipLayer(listing.entry.id); + editor.clipLayer(listing.entry.id); } function clippingKeyPress(e: KeyboardEvent) { @@ -247,7 +248,7 @@ // Don't select while we are entering text to rename the layer if (listing.editingName) return; - editor.handle.selectLayer(listing.entry.id, accel, shift); + editor.selectLayer(listing.entry.id, accel, shift); } async function deselectAllLayers() { @@ -256,7 +257,7 @@ return; } - editor.handle.deselectAllLayers(); + editor.deselectAllLayers(); } function calculateDragIndex(tree: LayoutCol, clientY: number, select?: () => void): DraggingData { @@ -389,7 +390,7 @@ // Commit the move select?.(); - editor.handle.moveLayerInTree(insertParentId, insertIndex); + editor.moveLayerInTree(insertParentId, insertIndex); // Prevent the subsequent click event from processing justFinishedDrag = true; @@ -445,7 +446,7 @@ const inputElement = document.activeElement; if (inputElement instanceof HTMLInputElement) { const name = inputElement.value || ""; - editor.handle.setLayerName(currentListing.entry.id, name); + editor.setLayerName(currentListing.entry.id, name); currentListing.entry.alias = name; } diff --git a/frontend/src/components/panels/Properties.svelte b/frontend/src/components/panels/Properties.svelte index 666a7030a6..cd551a7cea 100644 --- a/frontend/src/components/panels/Properties.svelte +++ b/frontend/src/components/panels/Properties.svelte @@ -2,25 +2,25 @@ import { getContext, onMount, onDestroy } from "svelte"; import type { Layout } from "@graphite/../wasm/pkg/graphite_wasm"; - import type { Editor } from "@graphite/editor"; + import type { SubscriptionsRouter } from "/src/subscriptions-router"; import { patchLayout } from "@graphite/utility-functions/widgets"; import LayoutCol from "@graphite/components/layout/LayoutCol.svelte"; import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte"; - const editor = getContext("editor"); + const subscriptions = getContext("subscriptions"); let propertiesPanelLayout: Layout = []; onMount(() => { - editor.subscriptions.subscribeLayoutUpdate("PropertiesPanel", (data) => { + subscriptions.subscribeLayoutUpdate("PropertiesPanel", (data) => { patchLayout(propertiesPanelLayout, data); propertiesPanelLayout = propertiesPanelLayout; }); }); onDestroy(() => { - editor.subscriptions.unsubscribeLayoutUpdate("PropertiesPanel"); + subscriptions.unsubscribeLayoutUpdate("PropertiesPanel"); }); diff --git a/frontend/src/components/panels/Welcome.svelte b/frontend/src/components/panels/Welcome.svelte index 49101abc37..d89fd637c7 100644 --- a/frontend/src/components/panels/Welcome.svelte +++ b/frontend/src/components/panels/Welcome.svelte @@ -2,8 +2,8 @@ import { getContext, onMount, onDestroy } from "svelte"; import { isPlatformNative } from "@graphite/../wasm/pkg/graphite_wasm"; - import type { Layout } from "@graphite/../wasm/pkg/graphite_wasm"; - import type { Editor } from "@graphite/editor"; + import type { EditorHandle, Layout } from "@graphite/../wasm/pkg/graphite_wasm"; + import type { SubscriptionsRouter } from "/src/subscriptions-router"; import { pasteFile } from "@graphite/utility-functions/files"; import { patchLayout } from "@graphite/utility-functions/widgets"; @@ -13,19 +13,20 @@ import TextLabel from "@graphite/components/widgets/labels/TextLabel.svelte"; import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte"; - const editor = getContext("editor"); + const subscriptions = getContext("subscriptions"); + const editor = getContext("editor"); let welcomePanelButtonsLayout: Layout = []; onMount(() => { - editor.subscriptions.subscribeLayoutUpdate("WelcomeScreenButtons", (data) => { + subscriptions.subscribeLayoutUpdate("WelcomeScreenButtons", (data) => { patchLayout(welcomePanelButtonsLayout, data); welcomePanelButtonsLayout = welcomePanelButtonsLayout; }); }); onDestroy(() => { - editor.subscriptions.unsubscribeLayoutUpdate("WelcomeScreenButtons"); + subscriptions.unsubscribeLayoutUpdate("WelcomeScreenButtons"); }); function dropFile(e: DragEvent) { diff --git a/frontend/src/components/views/Graph.svelte b/frontend/src/components/views/Graph.svelte index 7668eb0b46..705226d0c0 100644 --- a/frontend/src/components/views/Graph.svelte +++ b/frontend/src/components/views/Graph.svelte @@ -3,8 +3,7 @@ import { cubicInOut } from "svelte/easing"; import { fade } from "svelte/transition"; - import type { FrontendGraphInput, FrontendGraphOutput, FrontendNode } from "@graphite/../wasm/pkg/graphite_wasm"; - import type { Editor } from "@graphite/editor"; + import type { EditorHandle, FrontendGraphInput, FrontendGraphOutput, FrontendNode } from "@graphite/../wasm/pkg/graphite_wasm"; import type { DocumentStore } from "@graphite/stores/document"; import { closeContextMenu } from "@graphite/stores/node-graph"; import type { NodeGraphStore } from "@graphite/stores/node-graph"; @@ -20,7 +19,7 @@ const GRID_SIZE = 24; const FADE_TRANSITION = { duration: 200, easing: cubicInOut }; - const editor = getContext("editor"); + const editor = getContext("editor"); const nodeGraph = getContext("nodeGraph"); const documentState = getContext("document"); @@ -83,7 +82,7 @@ if (editingNameImportIndex !== undefined) { if (!(event.target instanceof HTMLInputElement)) return; let text = event.target.value; - editor.handle.setImportName(editingNameImportIndex, text); + editor.setImportName(editingNameImportIndex, text); editingNameImportIndex = undefined; } } @@ -92,7 +91,7 @@ if (editingNameExportIndex !== undefined) { if (!(event.target instanceof HTMLInputElement)) return; let text = event.target.value; - editor.handle.setExportName(editingNameExportIndex, text); + editor.setExportName(editingNameExportIndex, text); editingNameExportIndex = undefined; } } @@ -111,7 +110,7 @@ function createNode(identifier: string) { if ($nodeGraph.contextMenuInformation === undefined) return; - editor.handle.createNode(identifier, $nodeGraph.contextMenuInformation.contextMenuCoordinates[0], $nodeGraph.contextMenuInformation.contextMenuCoordinates[1]); + editor.createNode(identifier, $nodeGraph.contextMenuInformation.contextMenuCoordinates[0], $nodeGraph.contextMenuInformation.contextMenuCoordinates[1]); } function nodeBorderMask(nodeWidth: number, primaryInputExists: boolean, exposedSecondaryInputs: number, primaryOutputExists: boolean, exposedSecondaryOutputs: number): string { @@ -174,11 +173,11 @@ } function outputConnectedToText(output: FrontendGraphOutput): string { - return editor.handle.inDevelopmentMode() ? output.connectedTo.join("\n") : ""; + return editor.inDevelopmentMode() ? output.connectedTo.join("\n") : ""; } function inputConnectedToText(input: FrontendGraphInput): string { - return editor.handle.inDevelopmentMode() ? input.connectedTo : ""; + return editor.inDevelopmentMode() ? input.connectedTo : ""; } function zipWithUndefined(arr1: FrontendGraphInput[], arr2: FrontendGraphOutput[]) { @@ -220,7 +219,7 @@ { - editor.handle.mergeSelectedNodes(); + editor.mergeSelectedNodes(); closeContextMenu(); }} flush={true} @@ -230,7 +229,7 @@ label={currentlyIsNode ? "Display as Layer" : "Display as Node"} action={() => { if ($nodeGraph.contextMenuInformation?.contextMenuData.type === "ModifyNode") { - editor.handle.setToNodeOrLayer($nodeGraph.contextMenuInformation.contextMenuData.data.nodeId, currentlyIsNode); + editor.setToNodeOrLayer($nodeGraph.contextMenuInformation.contextMenuData.data.nodeId, currentlyIsNode); } closeContextMenu(); }} @@ -244,9 +243,9 @@ label={allLocked ? "Unlock" : "Lock"} action={() => { if ($nodeGraph.selected.includes(nodeId)) { - editor.handle.toggleSelectedLocked(); + editor.toggleSelectedLocked(); } else { - editor.handle.toggleLayerLock(nodeId); + editor.toggleLayerLock(nodeId); } closeContextMenu(); }} @@ -383,7 +382,7 @@ style:--offset-top={($nodeGraph.updateImportsExports.importPosition[1] - 12) / 24} style:--offset-left={($nodeGraph.updateImportsExports.importPosition[0] - 12) / 24} > - editor.handle.addPrimaryImport()} /> + editor.addPrimaryImport()} /> {/if} {/each} @@ -454,7 +453,7 @@ style:--offset-left={($nodeGraph.updateImportsExports.exportPosition[0] - 12) / 24} style:--offset-top={($nodeGraph.updateImportsExports.exportPosition[1] - 12) / 24} > - editor.handle.addPrimaryExport()} /> + editor.addPrimaryExport()} /> {/if} {/each} @@ -465,14 +464,14 @@ style:--offset-left={($nodeGraph.updateImportsExports.importPosition[0] - 12) / 24} style:--offset-top={($nodeGraph.updateImportsExports.importPosition[1] - 12) / 24 + $nodeGraph.updateImportsExports.imports.length} > - editor.handle.addSecondaryImport()} /> + editor.addSecondaryImport()} />
- editor.handle.addSecondaryExport()} /> + editor.addSecondaryExport()} />
{/if} @@ -522,7 +521,7 @@ style:--node-chain-area-left-extension={layerChainWidth !== 0 ? layerChainWidth + 0.5 : 0} data-tooltip-label={nodeNameTooltipLabel(node)} data-tooltip-description={` - ${(description || "").trim()}${editor.handle.inDevelopmentMode() ? `\n\nID: ${node.id}. Position: (${node.position[0]}, ${node.position[1]}).` : ""} + ${(description || "").trim()}${editor.inDevelopmentMode() ? `\n\nID: ${node.id}. Position: (${node.position[0]}, ${node.position[1]}).` : ""} `.trim()} data-node={node.id} > @@ -685,7 +684,7 @@ style:--data-color-dim={`var(--color-data-${(node.primaryOutput?.dataType || "General").toLowerCase()}-dim)`} data-tooltip-label={nodeNameTooltipLabel(node)} data-tooltip-description={` - ${(description || "").trim()}${editor.handle.inDevelopmentMode() ? `\n\nID: ${node.id}. Position: (${node.position[0]}, ${node.position[1]}).` : ""} + ${(description || "").trim()}${editor.inDevelopmentMode() ? `\n\nID: ${node.id}. Position: (${node.position[0]}, ${node.position[1]}).` : ""} `.trim()} data-node={node.id} > diff --git a/frontend/src/components/widgets/WidgetSection.svelte b/frontend/src/components/widgets/WidgetSection.svelte index 0314eb3114..db82e9515e 100644 --- a/frontend/src/components/widgets/WidgetSection.svelte +++ b/frontend/src/components/widgets/WidgetSection.svelte @@ -1,8 +1,7 @@ @@ -31,7 +30,7 @@ tooltipDescription={widgetData.pinned ? "Unpin this node so it's no longer shown here when nothing is selected." : "Pin this node so it's shown here when nothing is selected."} size={24} action={(e) => { - editor.handle.setNodePinned(widgetData.id, !widgetData.pinned); + editor.setNodePinned(widgetData.id, !widgetData.pinned); e?.stopPropagation(); }} class="show-only-on-hover" @@ -41,7 +40,7 @@ tooltipDescription="Delete this node from the layer chain." size={24} action={(e) => { - editor.handle.deleteNode(widgetData.id); + editor.deleteNode(widgetData.id); e?.stopPropagation(); }} class="show-only-on-hover" @@ -52,7 +51,7 @@ tooltipDescription={widgetData.visible ? "Hide this node." : "Show this node."} size={24} action={(e) => { - editor.handle.toggleNodeVisibilityLayerPanel(widgetData.id); + editor.toggleNodeVisibilityLayerPanel(widgetData.id); e?.stopPropagation(); }} class={widgetData.visible ? "show-only-on-hover" : ""} diff --git a/frontend/src/components/widgets/WidgetSpan.svelte b/frontend/src/components/widgets/WidgetSpan.svelte index 43aba10b9b..3feaebe11d 100644 --- a/frontend/src/components/widgets/WidgetSpan.svelte +++ b/frontend/src/components/widgets/WidgetSpan.svelte @@ -1,8 +1,7 @@ diff --git a/frontend/src/components/window/Panel.svelte b/frontend/src/components/window/Panel.svelte index 839b871e1d..c10a555d38 100644 --- a/frontend/src/components/window/Panel.svelte +++ b/frontend/src/components/window/Panel.svelte @@ -1,7 +1,7 @@ - panelType && editor.handle.setActivePanel(panelType)} class={`panel ${className}`.trim()} {classes} style={styleName} {styles}> + panelType && editor.setActivePanel(panelType)} class={`panel ${className}`.trim()} {classes} style={styleName} {styles}> {#each tabLabels as tabLabel, tabIndex} diff --git a/frontend/src/components/window/StatusBar.svelte b/frontend/src/components/window/StatusBar.svelte index a246d3ef2f..1b8f53ed0b 100644 --- a/frontend/src/components/window/StatusBar.svelte +++ b/frontend/src/components/window/StatusBar.svelte @@ -2,32 +2,33 @@ import { getContext, onMount, onDestroy } from "svelte"; import type { Layout } from "@graphite/../wasm/pkg/graphite_wasm"; - import type { Editor } from "@graphite/editor"; + import type { SubscriptionsRouter } from "/src/subscriptions-router"; import { patchLayout } from "@graphite/utility-functions/widgets"; import LayoutRow from "@graphite/components/layout/LayoutRow.svelte"; import Separator from "@graphite/components/widgets/labels/Separator.svelte"; import WidgetLayout from "@graphite/components/widgets/WidgetLayout.svelte"; - const editor = getContext("editor"); + const subscriptions = getContext("subscriptions"); let statusBarHintsLayout: Layout = []; let statusBarInfoLayout: Layout = []; onMount(() => { - editor.subscriptions.subscribeLayoutUpdate("StatusBarHints", (data) => { + subscriptions.subscribeLayoutUpdate("StatusBarHints", (data) => { patchLayout(statusBarHintsLayout, data); statusBarHintsLayout = statusBarHintsLayout; }); - editor.subscriptions.subscribeLayoutUpdate("StatusBarInfo", (data) => { + + subscriptions.subscribeLayoutUpdate("StatusBarInfo", (data) => { patchLayout(statusBarInfoLayout, data); statusBarInfoLayout = statusBarInfoLayout; }); }); onDestroy(() => { - editor.subscriptions.unsubscribeLayoutUpdate("StatusBarHints"); - editor.subscriptions.unsubscribeLayoutUpdate("StatusBarInfo"); + subscriptions.unsubscribeLayoutUpdate("StatusBarHints"); + subscriptions.unsubscribeLayoutUpdate("StatusBarInfo"); }); diff --git a/frontend/src/components/window/TitleBar.svelte b/frontend/src/components/window/TitleBar.svelte index 618057805e..e0650734cb 100644 --- a/frontend/src/components/window/TitleBar.svelte +++ b/frontend/src/components/window/TitleBar.svelte @@ -2,12 +2,12 @@ import { getContext, onMount, onDestroy } from "svelte"; import { isPlatformNative } from "@graphite/../wasm/pkg/graphite_wasm"; - import type { Layout } from "@graphite/../wasm/pkg/graphite_wasm"; - import type { Editor } from "@graphite/editor"; + import type { EditorHandle, Layout } from "@graphite/../wasm/pkg/graphite_wasm"; import type { AppWindowStore } from "@graphite/stores/app-window"; import { enterFullscreen, exitFullscreen } from "@graphite/stores/fullscreen"; import type { FullscreenStore } from "@graphite/stores/fullscreen"; import type { TooltipStore } from "@graphite/stores/tooltip"; + import type { SubscriptionsRouter } from "/src/subscriptions-router"; import { patchLayout } from "@graphite/utility-functions/widgets"; import LayoutRow from "@graphite/components/layout/LayoutRow.svelte"; @@ -16,8 +16,9 @@ const keyboardLockApiSupported = navigator.keyboard !== undefined && "lock" in navigator.keyboard; + const editor = getContext("editor"); + const subscriptions = getContext("subscriptions"); const appWindow = getContext("appWindow"); - const editor = getContext("editor"); const fullscreen = getContext("fullscreen"); const tooltip = getContext("tooltip"); @@ -29,14 +30,14 @@ $: height = $appWindow.platform === "Mac" ? 28 * (1 / $appWindow.uiScale) : 28; onMount(() => { - editor.subscriptions.subscribeLayoutUpdate("MenuBar", (data) => { + subscriptions.subscribeLayoutUpdate("MenuBar", (data) => { patchLayout(menuBarLayout, data); menuBarLayout = menuBarLayout; }); }); onDestroy(() => { - editor.subscriptions.unsubscribeLayoutUpdate("MenuBar"); + subscriptions.unsubscribeLayoutUpdate("MenuBar"); }); @@ -48,7 +49,7 @@ {/if} - !isFullscreen && editor.handle.appWindowDrag()} on:dblclick={() => !isFullscreen && editor.handle.appWindowMaximize()} /> + !isFullscreen && editor.appWindowDrag()} on:dblclick={() => !isFullscreen && editor.appWindowMaximize()} /> {#if $appWindow.platform !== "Mac"} @@ -60,20 +61,20 @@ : undefined} tooltipShortcut={$tooltip.fullscreenShortcut} on:click={() => { - if (isPlatformNative()) editor.handle.appWindowFullscreen(); + if (isPlatformNative()) editor.appWindowFullscreen(); else ($fullscreen.windowFullscreen ? exitFullscreen : enterFullscreen)(); }} > {:else} - editor.handle.appWindowMinimize()}> + editor.appWindowMinimize()}> - editor.handle.appWindowMaximize()}> + editor.appWindowMaximize()}> - editor.handle.appWindowClose()}> + editor.appWindowClose()}> {/if} diff --git a/frontend/src/components/window/Workspace.svelte b/frontend/src/components/window/Workspace.svelte index f19de9737b..6a57f98e93 100644 --- a/frontend/src/components/window/Workspace.svelte +++ b/frontend/src/components/window/Workspace.svelte @@ -1,8 +1,7 @@