From 7f082ad8f6f144b92947000b91ceddce3c52704b Mon Sep 17 00:00:00 2001 From: Max Richter Date: Tue, 5 May 2026 21:07:57 +0200 Subject: [PATCH] feat: implement node sockets ui --- .../graph-interface/graph-manager.svelte.ts | 10 +- .../lib/graph-interface/graph-state.svelte.ts | 3 +- app/src/lib/graph-interface/graph/colors.ts | 10 +- .../graph-interface/node/NodeHeader.svelte | 9 ++ .../lib/sidebar/panels/GroupSettings.svelte | 101 +++++++++++---- .../sidebar/panels/UnusedGroupsPanel.svelte | 5 +- app/src/routes/+page.svelte | 2 +- packages/ui/src/lib/app.css | 2 +- packages/ui/src/lib/index.ts | 1 + packages/ui/src/lib/inputs/SocketTable.svelte | 118 ++++++++++++++++++ packages/ui/src/routes/+page.svelte | 24 ++++ packages/utils/src/logger.ts | 86 ++++++++++++- 12 files changed, 331 insertions(+), 40 deletions(-) create mode 100644 packages/ui/src/lib/inputs/SocketTable.svelte diff --git a/app/src/lib/graph-interface/graph-manager.svelte.ts b/app/src/lib/graph-interface/graph-manager.svelte.ts index ea022f9..128d0d1 100644 --- a/app/src/lib/graph-interface/graph-manager.svelte.ts +++ b/app/src/lib/graph-interface/graph-manager.svelte.ts @@ -51,7 +51,7 @@ export class GraphManager extends EventEmitter<{ id: number; nodes: SerializedNode[]; edges: SerializedEdge[]; - cameraPosition: [number, number, number]; + nodeId: number; // group instance node id that was entered to reach the next level }[] = $state([]); // ID of the currently active group, or null when at the root graph. @@ -642,11 +642,11 @@ export class GraphManager extends EventEmitter<{ isInsideGroup = $derived(this.currentGroupId !== null); - enterGroup(nodeId: number, cameraPosition: [number, number, number]): boolean { + enterGroup(nodeId: number): boolean { const groupNode = this.getNode(nodeId); if (!groupNode || groupNode.type !== '__internal/group/instance') return false; - log.log('entering group', { nodeId, cameraPosition }); + log.log('entering group', { nodeId }); const groupId = groupNode.props?.groupId as number; const group = this.getGroup(groupId); @@ -657,7 +657,7 @@ export class GraphManager extends EventEmitter<{ id: this.currentGroupId ?? this.id, nodes: [...this.nodes.values()].map(n => serializeNode(n)), edges: [...this.edges.values()].map(e => serializeEdge(e)), - cameraPosition + nodeId }); this.currentGroupId = groupId; @@ -685,6 +685,8 @@ export class GraphManager extends EventEmitter<{ this.history.reset(); this.execute(); this.save(); + + return { nodeId: parent.nodeId }; } createNodeId() { diff --git a/app/src/lib/graph-interface/graph-state.svelte.ts b/app/src/lib/graph-interface/graph-state.svelte.ts index 1146e99..74092ef 100644 --- a/app/src/lib/graph-interface/graph-state.svelte.ts +++ b/app/src/lib/graph-interface/graph-state.svelte.ts @@ -365,7 +365,7 @@ export class GraphState { if (this.activeNodeId === -1) return; const node = this.graph.getNode(this.activeNodeId); if (!node || node.type !== '__internal/group/instance') return; - const ok = this.graph.enterGroup(this.activeNodeId, [...this.cameraPosition]); + const ok = this.graph.enterGroup(this.activeNodeId); if (ok) { this.activeNodeId = -1; this.clearSelection(); @@ -375,7 +375,6 @@ export class GraphState { exitGroupNode() { const result = this.graph.exitGroup(); if (!result) return; - this.cameraPosition = result.camera; this.activeNodeId = result.nodeId; this.clearSelection(); } diff --git a/app/src/lib/graph-interface/graph/colors.ts b/app/src/lib/graph-interface/graph/colors.ts index f42de67..d290d74 100644 --- a/app/src/lib/graph-interface/graph/colors.ts +++ b/app/src/lib/graph-interface/graph/colors.ts @@ -2,7 +2,7 @@ type Color = { hue: number; saturation: number; lightness: number }; export class ColorGenerator { private colors: Map = new Map(); - private lightnessLevels = [10, 60]; + // private lightnessLevels = [10, 60]; constructor(predefined: Record) { for (const [id, colorStr] of Object.entries(predefined)) { @@ -10,6 +10,14 @@ export class ColorGenerator { } } + public getColors() { + return Object.fromEntries( + this.colors.entries().map(([key, col]) => { + return [key, this.colorToHsl(col)]; + }) + ); + } + public getColor(id: string): string { if (this.colors.has(id)) { return this.colorToHsl(this.colors.get(id)!); diff --git a/app/src/lib/graph-interface/node/NodeHeader.svelte b/app/src/lib/graph-interface/node/NodeHeader.svelte index a292c74..6ec29be 100644 --- a/app/src/lib/graph-interface/node/NodeHeader.svelte +++ b/app/src/lib/graph-interface/node/NodeHeader.svelte @@ -26,6 +26,13 @@ const rightBump = $derived( !!nodeType?.outputs?.length && node.type !== '__internal/group/input' ); + const cornerBottom = $derived( + node.type === '__internal/group/input' + ? (Object.keys(nodeType?.inputs ?? {}).length ? 0 : 10) + : node.type === '__internal/group/output' + ? (nodeType?.outputs?.length ? 0 : 10) + : 0 + ); const aspectRatio = 0.25; @@ -35,6 +42,7 @@ height: 34, y: 49, cornerTop, + cornerBottom, rightBump, aspectRatio }) @@ -45,6 +53,7 @@ height: 40, y: 49, cornerTop, + cornerBottom, rightBump, aspectRatio }) diff --git a/app/src/lib/sidebar/panels/GroupSettings.svelte b/app/src/lib/sidebar/panels/GroupSettings.svelte index 2d3aeb7..bedb4df 100644 --- a/app/src/lib/sidebar/panels/GroupSettings.svelte +++ b/app/src/lib/sidebar/panels/GroupSettings.svelte @@ -1,26 +1,26 @@ {#if activeGroup} @@ -39,7 +80,7 @@ {#if activeGroup} {#key activeGroup.id} -
+
Group Inputs
- {#each Object.keys(activeGroup?.inputs ?? {}) as key (key)} -
- - - -
- {/each} + +
+ + +
+ +
{/key} @@ -77,10 +133,9 @@ display: flex; flex-direction: column; gap: 0.4em; - padding: 1em; } - .group-settings label { + label { font-size: 0.8em; opacity: 0.7; } diff --git a/app/src/lib/sidebar/panels/UnusedGroupsPanel.svelte b/app/src/lib/sidebar/panels/UnusedGroupsPanel.svelte index d2f2799..b217532 100644 --- a/app/src/lib/sidebar/panels/UnusedGroupsPanel.svelte +++ b/app/src/lib/sidebar/panels/UnusedGroupsPanel.svelte @@ -48,7 +48,7 @@ {#if unusedTree.length} -
+
Unused groups