From ff8c6637f819a8b96df5fdf38e69eac761a223ce Mon Sep 17 00:00:00 2001 From: Max Richter Date: Thu, 22 Jan 2026 15:54:08 +0100 Subject: [PATCH] feat: add "*"/any type input for dev page --- .../graph-interface/graph-manager.svelte.ts | 47 +++++++++++----- .../lib/graph-interface/graph-state.svelte.ts | 2 +- .../lib/graph-interface/graph/Wrapper.svelte | 1 + .../lib/node-registry/node-registry-client.ts | 10 +++- app/src/lib/runtime/runtime-executor.ts | 13 +++-- app/src/routes/+page.svelte | 5 -- app/src/routes/dev/+layout.svelte | 2 +- app/src/routes/dev/+page.svelte | 19 +++---- app/src/routes/dev/dev-graph.json | 53 +++++++++++++++++++ 9 files changed, 113 insertions(+), 39 deletions(-) create mode 100644 app/src/routes/dev/dev-graph.json diff --git a/app/src/lib/graph-interface/graph-manager.svelte.ts b/app/src/lib/graph-interface/graph-manager.svelte.ts index 1a8907f..bbd709c 100644 --- a/app/src/lib/graph-interface/graph-manager.svelte.ts +++ b/app/src/lib/graph-interface/graph-manager.svelte.ts @@ -25,7 +25,7 @@ const clone = 'structuredClone' in self ? self.structuredClone : (args: unknown) => JSON.parse(JSON.stringify(args)); -function areSocketsCompatible( +export function areSocketsCompatible( output: string | undefined, inputs: string | (string | undefined)[] | undefined ) { @@ -33,7 +33,7 @@ function areSocketsCompatible( if (Array.isArray(inputs) && output) { return inputs.includes('*') || inputs.includes(output); } - return inputs === output; + return inputs === output || inputs === '*'; } function areEdgesEqual(firstEdge: Edge, secondEdge: Edge) { @@ -269,14 +269,7 @@ export class GraphManager extends EventEmitter<{ private _init(graph: Graph) { const nodes = new SvelteMap( graph.nodes.map((node) => { - const nodeType = this.registry.getNode(node.type); - const n = node as NodeInstance; - if (nodeType) { - n.state = { - type: nodeType - }; - } - return [node.id, n]; + return [node.id, node as NodeInstance]; }) ); @@ -301,6 +294,30 @@ export class GraphManager extends EventEmitter<{ this.execute(); } + private async loadAllCollections() { + // Fetch all nodes from all collections of the loaded nodes + const nodeIds = Array.from(new Set([...this.graph.nodes.map((n) => n.type)])); + const allCollections = new Set<`${string}/${string}`>(); + for (const id of nodeIds) { + const [user, collection] = id.split('/'); + allCollections.add(`${user}/${collection}`); + } + + const allCollectionIds = await Promise + .all([...allCollections] + .map(async (collection) => + remoteRegistry + .fetchCollection(collection) + .then((collection: { nodes: { id: NodeId }[] }) => { + return collection.nodes.map(n => n.id.replace(/\.wasm$/, '') as NodeId); + }) + )); + + const missingNodeIds = [...new Set(allCollectionIds.flat())]; + + this.registry.load(missingNodeIds); + } + async load(graph: Graph) { const a = performance.now(); @@ -385,7 +402,9 @@ export class GraphManager extends EventEmitter<{ this.loaded = true; logger.log(`Graph loaded in ${performance.now() - a}ms`); + setTimeout(() => this.execute(), 100); + this.loadAllCollections(); // lazily load all nodes from all collections } getAllNodes() { @@ -492,10 +511,10 @@ export class GraphManager extends EventEmitter<{ const inputs = Object.entries(to.state?.type?.inputs ?? {}); const outputs = from.state?.type?.outputs ?? []; for (let i = 0; i < inputs.length; i++) { - const [inputName, input] = inputs[0]; + const [inputName, input] = inputs[i]; for (let o = 0; o < outputs.length; o++) { - const output = outputs[0]; - if (input.type === output) { + const output = outputs[o]; + if (input.type === output || input.type === '*') { return this.createEdge(from, o, to, inputName); } } @@ -725,6 +744,7 @@ export class GraphManager extends EventEmitter<{ getPossibleSockets({ node, index }: Socket): [NodeInstance, string | number][] { const nodeType = node?.state?.type; + console.log({ node: $state.snapshot(node), index, nodeType }); if (!nodeType) return []; const sockets: [NodeInstance, string | number][] = []; @@ -788,6 +808,7 @@ export class GraphManager extends EventEmitter<{ } } + console.log(`Found ${sockets.length} possible sockets`, sockets); return sockets; } diff --git a/app/src/lib/graph-interface/graph-state.svelte.ts b/app/src/lib/graph-interface/graph-state.svelte.ts index 50018d0..42a50ff 100644 --- a/app/src/lib/graph-interface/graph-state.svelte.ts +++ b/app/src/lib/graph-interface/graph-state.svelte.ts @@ -259,7 +259,7 @@ export class GraphState { let { node, index, position } = socket; - // remove existing edge + // if the socket is an input socket -> remove existing edges if (typeof index === 'string') { const edges = this.graph.getEdgesToNode(node); for (const edge of edges) { diff --git a/app/src/lib/graph-interface/graph/Wrapper.svelte b/app/src/lib/graph-interface/graph/Wrapper.svelte index 73a08ac..57433c7 100644 --- a/app/src/lib/graph-interface/graph/Wrapper.svelte +++ b/app/src/lib/graph-interface/graph/Wrapper.svelte @@ -33,6 +33,7 @@ backgroundType = $bindable('grid'), snapToGrid = $bindable(true), showHelp = $bindable(false), + settings = $bindable(), settingTypes = $bindable(), onsave, onresult diff --git a/app/src/lib/node-registry/node-registry-client.ts b/app/src/lib/node-registry/node-registry-client.ts index b3561bf..74caf5a 100644 --- a/app/src/lib/node-registry/node-registry-client.ts +++ b/app/src/lib/node-registry/node-registry-client.ts @@ -2,6 +2,7 @@ import { type AsyncCache, type NodeDefinition, NodeDefinitionSchema, + type NodeId, type NodeRegistry } from '@nodarium/types'; import { createLogger, createWasmWrapper } from '@nodarium/utils'; @@ -170,6 +171,13 @@ export class RemoteNodeRegistry implements NodeRegistry { } getAllNodes() { - return [...this.nodes.values()]; + const allNodes = [...this.nodes.values()]; + log.info('getting all nodes', allNodes); + return allNodes; + } + + async overwriteNode(nodeId: NodeId, node: NodeDefinition) { + log.info('Overwritten node', { nodeId, node }); + this.nodes.set(nodeId, node); } } diff --git a/app/src/lib/runtime/runtime-executor.ts b/app/src/lib/runtime/runtime-executor.ts index bf9d3bc..5039d55 100644 --- a/app/src/lib/runtime/runtime-executor.ts +++ b/app/src/lib/runtime/runtime-executor.ts @@ -52,6 +52,7 @@ function getValue(input: NodeInput, value?: unknown) { return value; } + console.error({ input, value }); throw new Error(`Unknown input type ${input.type}`); } @@ -63,6 +64,8 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { perf?: PerformanceStore; + private results: Record = {}; + constructor( private registry: NodeRegistry, public cache?: SyncCache @@ -186,7 +189,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { ); // here we store the intermediate results of the nodes - const results: Record = {}; + this.results = {}; if (settings['randomSeed']) { this.seed = Math.floor(Math.random() * 100000000); @@ -217,12 +220,12 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { // check if the input is connected to another node const inputNode = node.state.inputNodes[key]; if (inputNode) { - if (results[inputNode.id] === undefined) { + if (this.results[inputNode.id] === undefined) { throw new Error( `Node ${node.type} is missing input from node ${inputNode.type}` ); } - return results[inputNode.id]; + return this.results[inputNode.id]; } // If the value is stored in the node itself, we use that value @@ -277,7 +280,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { b = performance.now(); if (this.cache && node.id !== outputNode.id) { - this.cache.set(inputHash, results[node.id]); + this.cache.set(inputHash, this.results[node.id]); } this.perf?.addPoint('node/' + node_type.id, b - a); @@ -290,7 +293,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor { } // return the result of the parent of the output node - const res = results[outputNode.id]; + const res = this.results[outputNode.id]; if (this.cache) { this.cache.size = sortedNodes.length * 2; diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index 515dd8e..ded7053 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -90,11 +90,6 @@ let graphSettingTypes = $state({ randomSeed: { type: 'boolean', value: false } }); - $effect(() => { - if (graphSettings && graphSettingTypes) { - manager?.setSettings($state.snapshot(graphSettings)); - } - }); async function update( g: Graph, diff --git a/app/src/routes/dev/+layout.svelte b/app/src/routes/dev/+layout.svelte index f3366bc..dc17e62 100644 --- a/app/src/routes/dev/+layout.svelte +++ b/app/src/routes/dev/+layout.svelte @@ -3,6 +3,6 @@ const { children } = $props<{ children?: Snippet }>(); -
+
{@render children()}
diff --git a/app/src/routes/dev/+page.svelte b/app/src/routes/dev/+page.svelte index c0cb132..4a46cdc 100644 --- a/app/src/routes/dev/+page.svelte +++ b/app/src/routes/dev/+page.svelte @@ -44,8 +44,9 @@ } } - $effect(() => { - fetchNodeData(activeNode.value); + let graphSettings = $state>({}); + let graphSettingTypes = $state({ + randomSeed: { type: "boolean", value: false }, }); $effect(() => { @@ -61,19 +62,11 @@ }); -
- {#if nodeInstance} - - {/if} -
- -
-      
-        {JSON.stringify(nodeInstance?.props)}
-      
-    
+ {#if result} +
{JSON.stringify(decodeNestedArray(result))}
+ {/if}
diff --git a/app/src/routes/dev/dev-graph.json b/app/src/routes/dev/dev-graph.json new file mode 100644 index 0000000..689f65a --- /dev/null +++ b/app/src/routes/dev/dev-graph.json @@ -0,0 +1,53 @@ +{ + "settings": { + "resolution.circle": 26, + "resolution.curve": 39 + }, + "nodes": [ + { + "id": 9, + "position": [ + 220, + 80 + ], + "type": "max/plantarium/output", + "props": {} + }, + { + "id": 10, + "position": [ + 195, + 80 + ], + "type": "max/plantarium/math", + "props": { + "op_type": 0, + "a": 2, + "b": 2 + } + }, + { + "id": 11, + "position": [ + 170, + 80 + ], + "type": "max/plantarium/float", + "props": { + "value": 0.1 + } + }, + { + "id": 12, + "position": [ + 170, + 100 + ], + "type": "max/plantarium/float", + "props": { + "value": 0.1 + } + } + ], + "edges": [] +}