From 714d01da94e9e5cf2584e6ff920af232be962c27 Mon Sep 17 00:00:00 2001 From: Felix Hungenberg <30648730+shiftgeist@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:39:54 +0100 Subject: [PATCH 1/4] chore: move pnpm links to workspace (auto link) --- app/package.json | 2 +- packages/registry/package.json | 4 ++-- packages/ui/package.json | 2 +- packages/utils/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/package.json b/app/package.json index a40a4ea..cf6099a 100644 --- a/app/package.json +++ b/app/package.json @@ -28,7 +28,7 @@ "devDependencies": { "@iconify-json/tabler": "^1.2.26", "@iconify/tailwind4": "^1.2.1", - "@nodarium/types": "link:../packages/types", + "@nodarium/types": "workspace:", "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/vite-plugin-svelte": "^6.2.4", "@tsconfig/svelte": "^5.0.6", diff --git a/packages/registry/package.json b/packages/registry/package.json index ae89205..d81f6bd 100644 --- a/packages/registry/package.json +++ b/packages/registry/package.json @@ -10,8 +10,8 @@ "author": "", "license": "ISC", "dependencies": { - "@nodarium/types": "link:../types", - "@nodarium/utils": "link:../utils", + "@nodarium/types": "workspace:", + "@nodarium/utils": "workspace:", "idb": "^8.0.3" } } diff --git a/packages/ui/package.json b/packages/ui/package.json index 75ec7b2..ee8bf3e 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -30,7 +30,7 @@ "svelte": "^4.0.0" }, "devDependencies": { - "@nodarium/types": "link:../types", + "@nodarium/types": "workspace:", "@sveltejs/adapter-static": "^3.0.10", "@sveltejs/kit": "^2.50.0", "@sveltejs/package": "^2.5.7", diff --git a/packages/utils/package.json b/packages/utils/package.json index fd1bc04..57567a2 100644 --- a/packages/utils/package.json +++ b/packages/utils/package.json @@ -10,7 +10,7 @@ "author": "", "license": "ISC", "dependencies": { - "@nodarium/types": "link:../types" + "@nodarium/types": "workspace:" }, "devDependencies": { "vite": "^7.3.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6288630..bfd4cf8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -70,7 +70,7 @@ importers: specifier: ^1.2.1 version: 1.2.1(tailwindcss@4.1.18) '@nodarium/types': - specifier: link:../packages/types + specifier: 'workspace:' version: link:../packages/types '@sveltejs/adapter-static': specifier: ^3.0.10 @@ -118,10 +118,10 @@ importers: packages/registry: dependencies: '@nodarium/types': - specifier: link:../types + specifier: 'workspace:' version: link:../types '@nodarium/utils': - specifier: link:../utils + specifier: 'workspace:' version: link:../utils idb: specifier: ^8.0.3 @@ -155,7 +155,7 @@ importers: version: 4.1.18 devDependencies: '@nodarium/types': - specifier: link:../types + specifier: 'workspace:' version: link:../types '@sveltejs/adapter-static': specifier: ^3.0.10 @@ -215,7 +215,7 @@ importers: packages/utils: dependencies: '@nodarium/types': - specifier: link:../types + specifier: 'workspace:' version: link:../types devDependencies: vite: From 3e019e4e218134bd1eafd678e35cb503f5822d80 Mon Sep 17 00:00:00 2001 From: Felix Hungenberg <30648730+shiftgeist@users.noreply.github.com> Date: Thu, 22 Jan 2026 23:26:26 +0100 Subject: [PATCH 2/4] feat: don't move graph on right click drag --- .../lib/graph-interface/graph-state.svelte.ts | 22 +++-- .../lib/graph-interface/graph/Graph.svelte | 85 +++++++++---------- .../lib/graph-interface/graph/mouse.events.ts | 24 ++++-- app/src/lib/graph-interface/keymaps.ts | 4 +- 4 files changed, 74 insertions(+), 61 deletions(-) diff --git a/app/src/lib/graph-interface/graph-state.svelte.ts b/app/src/lib/graph-interface/graph-state.svelte.ts index c75d4f7..aaf1f7c 100644 --- a/app/src/lib/graph-interface/graph-state.svelte.ts +++ b/app/src/lib/graph-interface/graph-state.svelte.ts @@ -58,7 +58,9 @@ export class GraphState { wrapper = $state(null!); rect: DOMRect = $derived( - (this.wrapper && this.width && this.height) ? this.wrapper.getBoundingClientRect() : new DOMRect(0, 0, 0, 0) + (this.wrapper && this.width && this.height) + ? this.wrapper.getBoundingClientRect() + : new DOMRect(0, 0, 0, 0) ); camera = $state(null!); @@ -187,13 +189,13 @@ export class GraphState { } const height = 5 + 10 - * Object.keys(node.inputs).filter( - (p) => - p !== 'seed' - && node?.inputs - && !('setting' in node?.inputs?.[p]) - && node.inputs[p].hidden !== true - ).length; + * Object.keys(node.inputs).filter( + (p) => + p !== 'seed' + && node?.inputs + && !('setting' in node?.inputs?.[p]) + && node.inputs[p].hidden !== true + ).length; this.nodeHeightCache[nodeTypeId] = height; return height; } @@ -333,4 +335,8 @@ export class GraphState { && node.position[1] < this.cameraBounds[3] ); } + + openNodePalette() { + this.addMenuPosition = [this.mousePosition[0], this.mousePosition[1]]; + } } diff --git a/app/src/lib/graph-interface/graph/Graph.svelte b/app/src/lib/graph-interface/graph/Graph.svelte index 052af80..d0dbd5f 100644 --- a/app/src/lib/graph-interface/graph/Graph.svelte +++ b/app/src/lib/graph-interface/graph/Graph.svelte @@ -1,23 +1,23 @@ mouseEvents.handleMouseMove(ev)} - onmouseup={(ev) => mouseEvents.handleMouseUp(ev)} + onmousemove={(ev) => mouseEvents.handleWindowMouseMove(ev)} + onmouseup={(ev) => mouseEvents.handleWindowMouseUp(ev)} />
mouseEvents.handleMouseScroll(ev)} bind:this={graphState.wrapper} class="graph-wrapper" - style="height: 100%;" + style="height: 100%" class:is-panning={graphState.isPanning} class:is-hovering={graphState.hoveredNodeId !== -1} aria-label="Graph" @@ -115,6 +113,7 @@ bind:clientHeight={graphState.height} onkeydown={(ev) => keymap.handleKeyboardEvent(ev)} onmousedown={(ev) => mouseEvents.handleMouseDown(ev)} + oncontextmenu={(ev) => mouseEvents.handleContextMenu(ev)} {...fileDropEvents.getEventListenerProps()} > {/if} - {#if graph.status === "idle"} + {#if graph.status === 'idle'} {#if graphState.addMenuPosition} {/if} @@ -207,9 +204,9 @@ {/each}
- {:else if graph.status === "loading"} + {:else if graph.status === 'loading'} Loading - {:else if graph.status === "error"} + {:else if graph.status === 'error'} Error {/if} diff --git a/app/src/lib/graph-interface/graph/mouse.events.ts b/app/src/lib/graph-interface/graph/mouse.events.ts index c856a77..c70d36f 100644 --- a/app/src/lib/graph-interface/graph/mouse.events.ts +++ b/app/src/lib/graph-interface/graph/mouse.events.ts @@ -1,7 +1,7 @@ import { animate, lerp } from '$lib/helpers'; import { type NodeInstance } from '@nodarium/types'; import type { GraphManager } from '../graph-manager.svelte'; -import type { GraphState } from '../graph-state.svelte'; +import { type GraphState } from '../graph-state.svelte'; import { snapToGrid as snapPointToGrid } from '../helpers'; import { maxZoom, minZoom, zoomSpeed } from './constants'; import { EdgeInteractionManager } from './edge.events'; @@ -16,7 +16,7 @@ export class MouseEventManager { this.edgeInteractionManager = new EdgeInteractionManager(graph, state); } - handleMouseUp(event: MouseEvent) { + handleWindowMouseUp(event: MouseEvent) { this.edgeInteractionManager.handleMouseUp(); this.state.isPanning = false; if (!this.state.mouseDown) return; @@ -151,7 +151,19 @@ export class MouseEventManager { this.state.addMenuPosition = null; } + handleContextMenu(event: MouseEvent) { + if (!this.state.addMenuPosition) { + event.preventDefault(); + this.state.openNodePalette(); + } + } + handleMouseDown(event: MouseEvent) { + // Right click + if (event.button === 2) { + return; + } + if (this.state.mouseDown) return; this.state.edgeEndPosition = null; @@ -229,7 +241,7 @@ export class MouseEventManager { this.state.edgeEndPosition = null; } - handleMouseMove(event: MouseEvent) { + handleWindowMouseMove(event: MouseEvent) { let mx = event.clientX - this.state.rect.x; let my = event.clientY - this.state.rect.y; @@ -245,7 +257,7 @@ export class MouseEventManager { for (const socket of this.state.possibleSockets) { const dist = Math.sqrt( (socket.position[0] - this.state.mousePosition[0]) ** 2 - + (socket.position[1] - this.state.mousePosition[1]) ** 2 + + (socket.position[1] - this.state.mousePosition[1]) ** 2 ); if (dist < smallestDist) { smallestDist = dist; @@ -377,9 +389,9 @@ export class MouseEventManager { // Update camera position and zoom level this.state.cameraPosition[0] = this.state.mousePosition[0] - (this.state.mousePosition[0] - this.state.cameraPosition[0]) - / zoomRatio; + / zoomRatio; this.state.cameraPosition[1] = this.state.mousePosition[1] - (this.state.mousePosition[1] - this.state.cameraPosition[1]) - / zoomRatio, this.state.cameraPosition[2] = newZoom; + / zoomRatio, this.state.cameraPosition[2] = newZoom; } } diff --git a/app/src/lib/graph-interface/keymaps.ts b/app/src/lib/graph-interface/keymaps.ts index 9fd9b54..44d98f4 100644 --- a/app/src/lib/graph-interface/keymaps.ts +++ b/app/src/lib/graph-interface/keymaps.ts @@ -59,9 +59,7 @@ export function setupKeymaps(keymap: Keymap, graph: GraphManager, graphState: Gr key: 'A', shift: true, description: 'Add new Node', - callback: () => { - graphState.addMenuPosition = [graphState.mousePosition[0], graphState.mousePosition[1]]; - } + callback: () => graphState.openNodePalette() }); keymap.addShortcut({ From 8c1ba2ee652e52f09b1f6827b537ac6f16042436 Mon Sep 17 00:00:00 2001 From: Felix Hungenberg <30648730+shiftgeist@users.noreply.github.com> Date: Thu, 22 Jan 2026 23:26:56 +0100 Subject: [PATCH 3/4] feat: move add context menu within view if outside --- .../graph-interface/components/AddMenu.svelte | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/app/src/lib/graph-interface/components/AddMenu.svelte b/app/src/lib/graph-interface/components/AddMenu.svelte index 5346410..732becc 100644 --- a/app/src/lib/graph-interface/components/AddMenu.svelte +++ b/app/src/lib/graph-interface/components/AddMenu.svelte @@ -1,8 +1,8 @@ @@ -89,7 +100,7 @@ position.z={graphState.addMenuPosition?.[1]} transform={false} > -
+
{ - if (event.key === "Enter") { + if (event.key === 'Enter') { handleNodeCreation(node.id); } }} @@ -125,7 +136,7 @@ activeNodeId = node.id; }} > - {node.id.split("/").at(-1)} + {node.id.split('/').at(-1)}
{/each}
@@ -167,6 +178,8 @@ min-height: none; width: 100%; color: var(--text-color); + max-height: 300px; + overflow-y: auto; } .result { From 5570d975f599c14f3cf857faa8a1633ea9b7c899 Mon Sep 17 00:00:00 2001 From: Felix Hungenberg <30648730+shiftgeist@users.noreply.github.com> Date: Thu, 22 Jan 2026 23:57:56 +0100 Subject: [PATCH 4/4] feat: unmigrate number into universal float, inherit step if unset --- .../lib/sidebar/panels/BenchmarkPanel.svelte | 251 ++++++------ packages/ui/src/lib/Input.svelte | 7 +- packages/ui/src/lib/index.ts | 3 +- packages/ui/src/lib/inputs/Float.svelte | 27 +- packages/ui/src/lib/inputs/Integer.svelte | 359 +++++++++--------- packages/ui/src/lib/inputs/Number.svelte | 177 --------- packages/ui/src/lib/inputs/Vec3.svelte | 2 +- packages/ui/src/routes/+page.svelte | 26 +- 8 files changed, 345 insertions(+), 507 deletions(-) delete mode 100644 packages/ui/src/lib/inputs/Number.svelte diff --git a/app/src/lib/sidebar/panels/BenchmarkPanel.svelte b/app/src/lib/sidebar/panels/BenchmarkPanel.svelte index 06e9123..cd1cdb4 100644 --- a/app/src/lib/sidebar/panels/BenchmarkPanel.svelte +++ b/app/src/lib/sidebar/panels/BenchmarkPanel.svelte @@ -1,147 +1,148 @@ {status}
- {#if result} -

Finished ({humanizeDuration(result.duration)})

-
- -
- - - ev.key === "Enter" && copyContent(result?.avg)} - onclick={() => copyContent(result?.avg)}>(click to copy) - - - ev.key === "Enter" && copyContent(result?.avg)} - onclick={() => copyContent(result?.stdev + "")}>(click to copy) -
- -
- {:else if isRunning} -

WarmUp ({$warmUp}/{warmUpAmount})

- {Math.floor(($warmUp / warmUpAmount) * 100)}% -

Progress ({samples}/{amount.value})

- {Math.floor((samples / amount.value) * 100)}% - {:else} - - - - {/if} + {#if result} +

Finished ({humanizeDuration(result.duration)})

+
+ +
+ + + ev.key === 'Enter' && copyContent(result?.avg)} + onclick={() => copyContent(result?.avg)} + >(click to copy) + + + ev.key === 'Enter' && copyContent(result?.avg)} + onclick={() => copyContent(result?.stdev + '')} + >(click to copy) +
+ +
+ {:else if isRunning} +

WarmUp ({$warmUp}/{warmUpAmount})

+ + {Math.floor(($warmUp / warmUpAmount) * 100)}% + +

Progress ({samples}/{amount.value})

+ + {Math.floor((samples / amount.value) * 100)}% + + {:else} + + + + {/if}
diff --git a/packages/ui/src/lib/Input.svelte b/packages/ui/src/lib/Input.svelte index f916dd1..aa91cba 100644 --- a/packages/ui/src/lib/Input.svelte +++ b/packages/ui/src/lib/Input.svelte @@ -1,10 +1,7 @@ {#if input.type === 'float'} - + {:else if input.type === 'integer'} {:else if input.type === 'boolean'} diff --git a/packages/ui/src/lib/index.ts b/packages/ui/src/lib/index.ts index d0eb412..a827062 100644 --- a/packages/ui/src/lib/index.ts +++ b/packages/ui/src/lib/index.ts @@ -1,8 +1,7 @@ export { default as Input } from './Input.svelte'; export { default as Checkbox } from './inputs/Checkbox.svelte'; -export { default as Float } from './inputs/Float.svelte'; +export { default as Float, default as Number } from './inputs/Float.svelte'; export { default as Integer } from './inputs/Integer.svelte'; -export { default as Number } from './inputs/Number.svelte'; export { default as Select } from './inputs/Select.svelte'; export { default as Vec3 } from './inputs/Vec3.svelte'; diff --git a/packages/ui/src/lib/inputs/Float.svelte b/packages/ui/src/lib/inputs/Float.svelte index e3352ee..013afc9 100644 --- a/packages/ui/src/lib/inputs/Float.svelte +++ b/packages/ui/src/lib/inputs/Float.svelte @@ -9,7 +9,7 @@ let { value = $bindable(0.5), - step = 0.01, + step, min = $bindable(0), max = $bindable(1), id @@ -22,8 +22,11 @@ max = value; } + // svelte-ignore state_referenced_locally only use initial values + const precision = ((step || value).toString().split('.')[1] || '').length; + function strip(input: number) { - return +parseFloat(input + '').toPrecision(2); + return +parseFloat(input + '').toFixed(precision); } let inputEl: HTMLInputElement | undefined = $state(); @@ -94,14 +97,22 @@ } else { value = Math.max(Math.min(min + (max - min) * vx, max), min); } - (ev.target as HTMLElement)?.blur(); + + value = strip(value); + + // With ctrl + outside of input ev.target becomes HTMLDocument + if (ev.target instanceof HTMLElement) { + ev.target?.blur(); + } } + $effect(() => { - if ((value || 0).toString().length > 5) { - value = strip(value || 0); + if (value.toString().length > 5) { + value = strip(value); } value !== undefined && handleChange(); }); + let width = $derived( Number.isFinite(value) ? Math.max((value?.toString().length ?? 1) * 8, 50) + 'px' : '20px' ); @@ -137,12 +148,12 @@ border-radius: var(--border-radius, 2px); } - input[type='number']::-webkit-inner-spin-button, - input[type='number']::-webkit-outer-spin-button { + input[type="number"]::-webkit-inner-spin-button, + input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; } - input[type='number'] { + input[type="number"] { box-sizing: border-box; -webkit-appearance: textfield; -moz-appearance: textfield; diff --git a/packages/ui/src/lib/inputs/Integer.svelte b/packages/ui/src/lib/inputs/Integer.svelte index e7d0f2a..0437667 100644 --- a/packages/ui/src/lib/inputs/Integer.svelte +++ b/packages/ui/src/lib/inputs/Integer.svelte @@ -1,210 +1,215 @@ + +
- - + + - - {#if typeof min !== 'undefined' && typeof max !== 'undefined'} - - {/if} + + {#if typeof min !== 'undefined' && typeof max !== 'undefined'} + + {/if}
diff --git a/packages/ui/src/lib/inputs/Number.svelte b/packages/ui/src/lib/inputs/Number.svelte deleted file mode 100644 index a7b2bca..0000000 --- a/packages/ui/src/lib/inputs/Number.svelte +++ /dev/null @@ -1,177 +0,0 @@ - - -
-
- - - -
-
- -
-
- - diff --git a/packages/ui/src/lib/inputs/Vec3.svelte b/packages/ui/src/lib/inputs/Vec3.svelte index 690bc7d..96bcea9 100644 --- a/packages/ui/src/lib/inputs/Vec3.svelte +++ b/packages/ui/src/lib/inputs/Vec3.svelte @@ -1,5 +1,5 @@