From cfcb4477840449857e936f03ad097315f33bd65f Mon Sep 17 00:00:00 2001 From: Max Richter Date: Mon, 24 Nov 2025 21:11:16 +0100 Subject: [PATCH] feat: update some more components to svelte 5 --- app/src/lib/graph-interface/AddMenu.svelte | 41 +++++++++++-------- .../background/Background.svelte | 5 ++- app/src/lib/graph-interface/edges/Edge.svelte | 9 ++-- .../graph-interface/graph/GraphView.svelte | 2 +- .../graph-interface/graph/colors.svelte.ts | 10 +++-- app/src/lib/graph-interface/node/Node.svelte | 2 +- .../lib/graph-interface/node/NodeHTML.svelte | 2 +- .../graph-interface/node/NodeHeader.svelte | 2 +- .../graph-interface/node/NodeParameter.svelte | 4 +- app/src/lib/grid/Cell.svelte | 6 +-- app/src/lib/grid/Row.svelte | 6 +-- app/src/lib/helpers/localState.svelte.ts | 41 +++++++++++++++---- app/src/lib/result-viewer/Scene.svelte | 32 ++++++++++----- app/src/lib/result-viewer/Viewer.svelte | 4 +- app/src/lib/settings/NestedSettings.svelte | 10 ++--- app/src/lib/settings/app-settings.svelte.ts | 4 +- app/src/routes/+page.svelte | 30 ++++++++------ 17 files changed, 130 insertions(+), 80 deletions(-) diff --git a/app/src/lib/graph-interface/AddMenu.svelte b/app/src/lib/graph-interface/AddMenu.svelte index 87470e0..5056ae6 100644 --- a/app/src/lib/graph-interface/AddMenu.svelte +++ b/app/src/lib/graph-interface/AddMenu.svelte @@ -4,31 +4,36 @@ import { onMount } from "svelte"; import type { NodeType } from "@nodes/types"; - export let position: [x: number, y: number] | null; + type Props = { + position: [x: number, y: number] | null; + graph: GraphManager; + }; - export let graph: GraphManager; + let { position = $bindable(), graph }: Props = $props(); let input: HTMLInputElement; - let value: string = ""; - let activeNodeId: NodeType | undefined = undefined; + let value = $state(); + let activeNodeId = $state(); const allNodes = graph.getNodeDefinitions(); function filterNodes() { - return allNodes.filter((node) => node.id.includes(value)); + return allNodes.filter((node) => node.id.includes(value ?? "")); } - $: nodes = value === "" ? allNodes : filterNodes(); - $: if (nodes) { - if (activeNodeId === undefined) { - activeNodeId = nodes[0].id; - } else if (nodes.length) { - const node = nodes.find((node) => node.id === activeNodeId); - if (!node) { + const nodes = $derived(value === "" ? allNodes : filterNodes()); + $effect(() => { + if (nodes) { + if (activeNodeId === undefined) { activeNodeId = nodes[0].id; + } else if (nodes.length) { + const node = nodes.find((node) => node.id === activeNodeId); + if (!node) { + activeNodeId = nodes[0].id; + } } } - } + }); function handleKeyDown(event: KeyboardEvent) { event.stopImmediatePropagation(); @@ -75,7 +80,7 @@ role="searchbox" placeholder="Search..." disabled={false} - on:keydown={handleKeyDown} + onkeydown={handleKeyDown} bind:value bind:this={input} /> @@ -88,7 +93,7 @@ role="treeitem" tabindex="0" aria-selected={node.id === activeNodeId} - on:keydown={(event) => { + onkeydown={(event) => { if (event.key === "Enter") { if (position) { graph.createNode({ type: node.id, position, props: {} }); @@ -96,17 +101,17 @@ } } }} - on:mousedown={() => { + onmousedown={() => { if (position) { graph.createNode({ type: node.id, position, props: {} }); position = null; } }} - on:focus={() => { + onfocus={() => { activeNodeId = node.id; }} class:selected={node.id === activeNodeId} - on:mouseover={() => { + onmouseover={() => { activeNodeId = node.id; }} > diff --git a/app/src/lib/graph-interface/background/Background.svelte b/app/src/lib/graph-interface/background/Background.svelte index f90b7b4..96f7334 100644 --- a/app/src/lib/graph-interface/background/Background.svelte +++ b/app/src/lib/graph-interface/background/Background.svelte @@ -54,8 +54,9 @@ }, }} uniforms.camPos.value={cameraPosition} - uniforms.backgroundColor.value={appSettings.theme && colors["layer-0"]} - uniforms.lineColor.value={appSettings.theme && colors["outline"]} + uniforms.backgroundColor.value={appSettings.value.theme && + colors["layer-0"]} + uniforms.lineColor.value={appSettings.value.theme && colors["outline"]} uniforms.zoomLimits.value={[minZoom, maxZoom]} uniforms.dimensions.value={[width, height]} /> diff --git a/app/src/lib/graph-interface/edges/Edge.svelte b/app/src/lib/graph-interface/edges/Edge.svelte index 785ed3a..d58898d 100644 --- a/app/src/lib/graph-interface/edges/Edge.svelte +++ b/app/src/lib/graph-interface/edges/Edge.svelte @@ -7,7 +7,7 @@ }); $effect.root(() => { $effect(() => { - appSettings.theme; + appSettings.value.theme; circleMaterial.color = colors.edge.clone().convertSRGBToLinear(); }); }); @@ -42,7 +42,7 @@ let geometry: BufferGeometry | null = $state(null); const lineColor = $derived( - appSettings.theme && colors.edge.clone().convertSRGBToLinear(), + appSettings.value.theme && colors.edge.clone().convertSRGBToLinear(), ); let lastId: number | null = null; @@ -114,6 +114,9 @@ {#if geometry} - + {/if} diff --git a/app/src/lib/graph-interface/graph/GraphView.svelte b/app/src/lib/graph-interface/graph/GraphView.svelte index 345f4f0..242b12b 100644 --- a/app/src/lib/graph-interface/graph/GraphView.svelte +++ b/app/src/lib/graph-interface/graph/GraphView.svelte @@ -19,7 +19,7 @@ const { invalidate } = useThrelte(); $effect(() => { - appSettings.theme; + appSettings.value.theme; invalidate(); }); diff --git a/app/src/lib/graph-interface/graph/colors.svelte.ts b/app/src/lib/graph-interface/graph/colors.svelte.ts index 03a7ff8..586b4e9 100644 --- a/app/src/lib/graph-interface/graph/colors.svelte.ts +++ b/app/src/lib/graph-interface/graph/colors.svelte.ts @@ -12,21 +12,23 @@ const variables = [ "edge", ] as const; -function getColor(variable: typeof variables[number]) { +function getColor(variable: (typeof variables)[number]) { const style = getComputedStyle(document.body.parentElement!); let color = style.getPropertyValue(`--${variable}`); return new Color().setStyle(color, LinearSRGBColorSpace); } -export const colors = Object.fromEntries(variables.map(v => [v, getColor(v)])) as Record; +export const colors = Object.fromEntries( + variables.map((v) => [v, getColor(v)]), +) as Record<(typeof variables)[number], Color>; $effect.root(() => { $effect(() => { - if (!appSettings.theme || !("getComputedStyle" in globalThis)) return; + if (!appSettings.value.theme || !("getComputedStyle" in globalThis)) return; const style = getComputedStyle(document.body.parentElement!); for (const v of variables) { const hex = style.getPropertyValue(`--${v}`); colors[v].setStyle(hex, LinearSRGBColorSpace); } }); -}) +}); diff --git a/app/src/lib/graph-interface/node/Node.svelte b/app/src/lib/graph-interface/node/Node.svelte index 5ec7e71..b111102 100644 --- a/app/src/lib/graph-interface/node/Node.svelte +++ b/app/src/lib/graph-interface/node/Node.svelte @@ -23,7 +23,7 @@ const isSelected = $derived(graphState.selectedNodes.has(node.id)); let strokeColor = $state(colors.selected); $effect(() => { - appSettings.theme; + appSettings.value.theme; strokeColor = isSelected ? colors.selected : isActive diff --git a/app/src/lib/graph-interface/node/NodeHTML.svelte b/app/src/lib/graph-interface/node/NodeHTML.svelte index 73af6e2..f8f7bb2 100644 --- a/app/src/lib/graph-interface/node/NodeHTML.svelte +++ b/app/src/lib/graph-interface/node/NodeHTML.svelte @@ -57,7 +57,7 @@ {#each parameters as [key, value], i} () } = $props(); + const { node }: { node: Node } = $props(); const setDownSocket = getContext<(socket: Socket) => void>("setDownSocket"); const getSocketPosition = diff --git a/app/src/lib/graph-interface/node/NodeParameter.svelte b/app/src/lib/graph-interface/node/NodeParameter.svelte index badac93..b8f66db 100644 --- a/app/src/lib/graph-interface/node/NodeParameter.svelte +++ b/app/src/lib/graph-interface/node/NodeParameter.svelte @@ -17,7 +17,7 @@ isLast?: boolean; }; - const { node, input, id, isLast }: Props = $props(); + let { node = $bindable(), input, id, isLast }: Props = $props(); const inputType = node?.tmp?.type?.inputs?.[id]!; @@ -87,7 +87,7 @@ {/if} {#if inputType.external !== true} - + {/if} diff --git a/app/src/lib/grid/Cell.svelte b/app/src/lib/grid/Cell.svelte index c5af17f..6249c3c 100644 --- a/app/src/lib/grid/Cell.svelte +++ b/app/src/lib/grid/Cell.svelte @@ -8,7 +8,7 @@ index = getContext<() => number>("registerCell")(); } - const sizes = getContext("sizes"); + const sizes = getContext<{ value: string[] }>("sizes"); let downSizes: string[] = []; let downWidth = 0; @@ -16,7 +16,7 @@ let startX = 0; function handleMouseDown(event: MouseEvent) { - downSizes = [...sizes]; + downSizes = [...sizes.value]; mouseDown = true; startX = event.clientX; downWidth = wrapper.getBoundingClientRect().width; @@ -25,7 +25,7 @@ function handleMouseMove(event: MouseEvent) { if (mouseDown) { const width = downWidth + startX - event.clientX; - sizes[index] = `${width}px`; + sizes.value[index] = `${width}px`; } } diff --git a/app/src/lib/grid/Row.svelte b/app/src/lib/grid/Row.svelte index d54696d..fc6f652 100644 --- a/app/src/lib/grid/Row.svelte +++ b/app/src/lib/grid/Row.svelte @@ -11,8 +11,8 @@ setContext("registerCell", function () { let index = registerIndex; registerIndex++; - if (registerIndex > sizes.length) { - sizes = [...sizes, "1fr"]; + if (registerIndex > sizes.value.length) { + sizes.value = [...sizes.value, "1fr"]; } return index; }); @@ -20,7 +20,7 @@ setContext("sizes", sizes); const cols = $derived( - sizes.map((size, i) => `${i > 0 ? "1px " : ""}` + size).join(" "), + sizes.value.map((size, i) => `${i > 0 ? "1px " : ""}` + size).join(" "), ); diff --git a/app/src/lib/helpers/localState.svelte.ts b/app/src/lib/helpers/localState.svelte.ts index 4d9582b..8e2332b 100644 --- a/app/src/lib/helpers/localState.svelte.ts +++ b/app/src/lib/helpers/localState.svelte.ts @@ -1,11 +1,34 @@ -export function localState(key: string, defaultValue: T): T { - const stored = localStorage.getItem(key); - const state = $state(stored ? JSON.parse(stored) : defaultValue); - $effect.root(() => { - $effect(() => { - const value = $state.snapshot(state); - localStorage.setItem(key, JSON.stringify(value)); +import { browser } from "$app/environment"; + +export class LocalStore { + value = $state() as T; + key = ""; + + constructor(key: string, value: T) { + this.key = key; + this.value = value; + + if (browser) { + const item = localStorage.getItem(key); + if (item) this.value = this.deserialize(item); + } + + $effect.root(() => { + $effect(() => { + localStorage.setItem(this.key, this.serialize(this.value)); + }); }); - }); - return state; + } + + serialize(value: T): string { + return JSON.stringify(value); + } + + deserialize(item: string): T { + return JSON.parse(item); + } +} + +export function localState(key: string, value: T) { + return new LocalStore(key, value); } diff --git a/app/src/lib/result-viewer/Scene.svelte b/app/src/lib/result-viewer/Scene.svelte index 94f279b..7462cda 100644 --- a/app/src/lib/result-viewer/Scene.svelte +++ b/app/src/lib/result-viewer/Scene.svelte @@ -1,6 +1,11 @@ -{#if appSettings.debug.showPerformancePanel} +{#if appSettings.value.debug.showPerformancePanel} {/if} diff --git a/app/src/lib/settings/NestedSettings.svelte b/app/src/lib/settings/NestedSettings.svelte index 853e62d..8bb59c9 100644 --- a/app/src/lib/settings/NestedSettings.svelte +++ b/app/src/lib/settings/NestedSettings.svelte @@ -77,13 +77,13 @@ let internalValue = $state(getDefaultValue()); - let open = $state(openSections[id]); + let open = $state(openSections.value[id]); // Persist
open/closed state for groups if (depth > 0 && !isNodeInput(type[key!])) { $effect(() => { if (open !== undefined) { - openSections[id] = open; + openSections.value[id] = open; } }); } @@ -110,7 +110,7 @@
{#if type[key].type === "button"} - {:else} @@ -124,7 +124,7 @@ @@ -142,7 +142,7 @@ diff --git a/app/src/lib/settings/app-settings.svelte.ts b/app/src/lib/settings/app-settings.svelte.ts index ca9863f..349a82b 100644 --- a/app/src/lib/settings/app-settings.svelte.ts +++ b/app/src/lib/settings/app-settings.svelte.ts @@ -144,7 +144,7 @@ type ExtractSettingsValues = { : never; }; -function settingsToStore(settings: T): ExtractSettingsValues { +export function settingsToStore(settings: T): ExtractSettingsValues { const result = {} as any; for (const key in settings) { const value = settings[key]; @@ -166,7 +166,7 @@ export let appSettings = localState( $effect.root(() => { $effect(() => { - const theme = appSettings.theme; + const theme = appSettings.value.theme; const classes = document.documentElement.classList; const newClassName = `theme-${theme}`; if (classes) { diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index 6c59582..bc36f10 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -38,7 +38,7 @@ memoryRuntime.perf = performanceStore; const runtime = $derived( - appSettings.debug.useWorker ? workerRuntime : memoryRuntime, + appSettings.value.debug.useWorker ? workerRuntime : memoryRuntime, ); let activeNode = $state(undefined); @@ -69,6 +69,11 @@ }, ]); let graphSettings = $state>({}); + $effect(() => { + if (graphSettings) { + manager?.setSettings($state.snapshot(graphSettings)); + } + }); type BooleanSchema = { [key: string]: { type: "boolean"; @@ -92,7 +97,7 @@ ); let b = performance.now(); - if (appSettings.debug.useWorker) { + if (appSettings.value.debug.useWorker) { let perfData = await runtime.getPerformanceData(); let lastRun = perfData?.at(-1); if (lastRun?.total) { @@ -118,13 +123,13 @@ //@ts-ignore AppSettingTypes.debug.stressTest.loadGrid.callback = () => { graph = templates.grid( - appSettings.debug.amount.value, - appSettings.debug.amount.value, + appSettings.value.debug.amount.value, + appSettings.value.debug.amount.value, ); }; //@ts-ignore AppSettingTypes.debug.stressTest.loadTree.callback = () => { - graph = templates.tree(appSettings.debug.amount.value); + graph = templates.tree(appSettings.value.debug.amount.value); }; //@ts-ignore AppSettingTypes.debug.stressTest.lottaFaces.callback = () => { @@ -155,7 +160,7 @@ bind:scene bind:this={viewerComponent} perf={performanceStore} - centerCamera={appSettings.centerCamera} + centerCamera={appSettings.value.centerCamera} /> @@ -163,10 +168,10 @@ {graph} bind:this={graphInterface} registry={nodeRegistry} - showGrid={appSettings.nodeInterface.showNodeGrid} - snapToGrid={appSettings.nodeInterface.snapToGrid} + showGrid={appSettings.value.nodeInterface.showNodeGrid} + snapToGrid={appSettings.value.nodeInterface.snapToGrid} bind:activeNode - bind:showHelp={appSettings.nodeInterface.showHelp} + bind:showHelp={appSettings.value.nodeInterface.showHelp} bind:settings={graphSettings} bind:settingTypes={graphSettingTypes} onresult={(result) => handleUpdate(result)} @@ -176,7 +181,7 @@ @@ -207,7 +212,7 @@ id="performance" title="Performance" classes="text-red-400" - hidden={!appSettings.debug.showPerformancePanel} + hidden={!appSettings.value.debug.showPerformancePanel} icon="i-tabler-brand-speedtest" > {#if $performanceStore} @@ -218,7 +223,7 @@ id="benchmark" title="Benchmark" classes="text-red-400" - hidden={!appSettings.debug.showBenchmarkPanel} + hidden={!appSettings.value.debug.showBenchmarkPanel} icon="i-tabler-graph" > @@ -250,7 +255,6 @@