diff --git a/app/src/lib/graph-interface/graph-manager.ts b/app/src/lib/graph-interface/graph-manager.ts index 4c6636b..2f8604f 100644 --- a/app/src/lib/graph-interface/graph-manager.ts +++ b/app/src/lib/graph-interface/graph-manager.ts @@ -36,7 +36,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, " history: HistoryManager = new HistoryManager(); - constructor(private nodeRegistry: NodeRegistry) { + constructor(public registry: NodeRegistry) { super(); this.nodes.subscribe((nodes) => { this._nodes = nodes; @@ -82,7 +82,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, " } getNodeTypes() { - return this.nodeRegistry.getAllNodes(); + return this.registry.getAllNodes(); } getLinkedNodes(node: Node) { @@ -122,7 +122,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, " private _init(graph: Graph) { const nodes = new Map(graph.nodes.map(node => { - const nodeType = this.nodeRegistry.getNode(node.type); + const nodeType = this.registry.getNode(node.type); if (nodeType) { node.tmp = { random: (Math.random() - 0.5) * 2, @@ -164,10 +164,10 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, " this.id.set(graph.id); const nodeIds = Array.from(new Set([...graph.nodes.map(n => n.type)])); - await this.nodeRegistry.load(nodeIds); + await this.registry.load(nodeIds); for (const node of this.graph.nodes) { - const nodeType = this.nodeRegistry.getNode(node.type); + const nodeType = this.registry.getNode(node.type); if (!nodeType) { logger.error(`Node type not found: ${node.type}`); this.status.set("error"); @@ -222,7 +222,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, " } getNodeType(id: string) { - return this.nodeRegistry.getNode(id); + return this.registry.getNode(id); } getChildrenOfNode(node: Node) { @@ -303,7 +303,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, " nodes = nodes.map((node, i) => { const id = startId + i; idMap.set(node.id, id); - const type = this.nodeRegistry.getNode(node.type); + const type = this.registry.getNode(node.type); if (!type) { throw new Error(`Node type not found: ${node.type}`); } @@ -343,7 +343,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, " createNode({ type, position, props = {} }: { type: Node["type"], position: Node["position"], props: Node["props"] }) { - const nodeType = this.nodeRegistry.getNode(type); + const nodeType = this.registry.getNode(type); if (!nodeType) { logger.error(`Node type not found: ${type}`); return; diff --git a/app/src/lib/graph-interface/graph/Graph.svelte b/app/src/lib/graph-interface/graph/Graph.svelte index dcde950..6496788 100644 --- a/app/src/lib/graph-interface/graph/Graph.svelte +++ b/app/src/lib/graph-interface/graph/Graph.svelte @@ -20,6 +20,7 @@ import { createKeyMap } from "../../helpers/createKeyMap"; import BoxSelection from "../BoxSelection.svelte"; import AddMenu from "../AddMenu.svelte"; + import { get } from "svelte/store"; export let graph: GraphManager; @@ -78,7 +79,7 @@ } function updateNodePosition(node: NodeType) { - if (node?.tmp?.ref) { + if (node?.tmp?.ref && node?.tmp?.mesh) { if (node.tmp["x"] !== undefined && node.tmp["y"] !== undefined) { node.tmp.ref.style.setProperty("--nx", `${node.tmp.x * 10}px`); node.tmp.ref.style.setProperty("--ny", `${node.tmp.y * 10}px`); @@ -758,6 +759,34 @@ addMenuPosition = null; } + function handleDrop(event: DragEvent) { + if (!event.dataTransfer) return; + const nodeId = event.dataTransfer.getData("data/node-id"); + let mx = event.clientX - rect.x; + let my = event.clientY - rect.y; + + let nodeOffsetX = event.dataTransfer.getData("data/node-offset-x"); + let nodeOffsetY = event.dataTransfer.getData("data/node-offset-y"); + if (nodeOffsetX && nodeOffsetY) { + mx += parseInt(nodeOffsetX); + my += parseInt(nodeOffsetY); + } + + const pos = projectScreenToWorld(mx, my); + graph.registry.load([nodeId]).then(() => { + graph.createNode({ + type: nodeId, + props: {}, + position: pos, + }); + }); + console.log({ nodeId }); + } + + function handlerDragOver(e: DragEvent) { + e.preventDefault(); + } + onMount(() => { if (localStorage.getItem("cameraPosition")) { const cPosition = JSON.parse(localStorage.getItem("cameraPosition")!); @@ -779,6 +808,8 @@ tabindex="0" bind:clientWidth={width} bind:clientHeight={height} + on:dragover={handlerDragOver} + on:drop={handleDrop} on:keydown={keymap.handleKeyboardEvent} on:mousedown={handleMouseDown} > diff --git a/app/src/lib/graph-interface/graph/Wrapper.svelte b/app/src/lib/graph-interface/graph/Wrapper.svelte index 27d3380..9adf484 100644 --- a/app/src/lib/graph-interface/graph/Wrapper.svelte +++ b/app/src/lib/graph-interface/graph/Wrapper.svelte @@ -11,7 +11,7 @@ export let graph: Graph; export let settings: Writable> | undefined; - const manager = new GraphManager(registry); + export const manager = new GraphManager(registry); export const status = manager.status; diff --git a/app/src/lib/graph-interface/node/Node.svelte b/app/src/lib/graph-interface/node/Node.svelte index dd4f57f..037d6c6 100644 --- a/app/src/lib/graph-interface/node/Node.svelte +++ b/app/src/lib/graph-interface/node/Node.svelte @@ -9,6 +9,7 @@ import { Color, type Mesh } from "three"; import NodeFrag from "./Node.frag"; import NodeVert from "./Node.vert"; + import NodeHtml from "./NodeHTML.svelte"; export let node: Node; export let inView = true; @@ -22,32 +23,20 @@ const getNodeHeight = getContext<(n: string) => number>("getNodeHeight"); - const type = node?.tmp?.type; - - const zOffset = (node.tmp?.random || 0) * 0.5; - const zLimit = 2 - zOffset; - - const parameters = Object.entries(type?.inputs || {}) - .filter((p) => p[1].type !== "seed") - .filter((p) => !("setting" in p[1])); - - let ref: HTMLDivElement; let meshRef: Mesh; - const height = getNodeHeight(node.type); + const height = getNodeHeight?.(node.type); - $: if (node && ref && meshRef) { + $: if (node && meshRef) { node.tmp = node.tmp || {}; - node.tmp.ref = ref; node.tmp.mesh = meshRef; - updateNodePosition(node); + updateNodePosition?.(node); } onMount(() => { node.tmp = node.tmp || {}; - node.tmp.ref = ref; node.tmp.mesh = meshRef; - updateNodePosition(node); + updateNodePosition?.(node); }); @@ -83,51 +72,4 @@ /> -
zLimit ? "block" : "none"} - class:selected={isSelected} - class:out-of-view={!inView} - data-node-id={node.id} - bind:this={ref} -> - - - {#each parameters as [key, value], i} - - {/each} -
- - + diff --git a/app/src/lib/graph-interface/node/NodeHTML.svelte b/app/src/lib/graph-interface/node/NodeHTML.svelte new file mode 100644 index 0000000..34f68a6 --- /dev/null +++ b/app/src/lib/graph-interface/node/NodeHTML.svelte @@ -0,0 +1,86 @@ + + +
zLimit ? "block" : "none"} + class:selected={isSelected} + class:out-of-view={!inView} + data-node-id={node.id} + bind:this={ref} +> + + + {#each parameters as [key, value], i} + + {/each} +
+ + diff --git a/app/src/lib/graph-interface/node/NodeHeader.svelte b/app/src/lib/graph-interface/node/NodeHeader.svelte index 95987da..182a8de 100644 --- a/app/src/lib/graph-interface/node/NodeHeader.svelte +++ b/app/src/lib/graph-interface/node/NodeHeader.svelte @@ -14,10 +14,10 @@ function handleMouseDown(event: MouseEvent) { event.stopPropagation(); event.preventDefault(); - setDownSocket({ + setDownSocket?.({ node, index: 0, - position: getSocketPosition(node, 0), + position: getSocketPosition?.(node, 0), }); } diff --git a/app/src/lib/graph-interface/node/NodeInput.svelte b/app/src/lib/graph-interface/node/NodeInput.svelte index ebc6a32..fad3fb8 100644 --- a/app/src/lib/graph-interface/node/NodeInput.svelte +++ b/app/src/lib/graph-interface/node/NodeInput.svelte @@ -15,8 +15,10 @@ $: if (node?.props?.[id] !== value) { node.props = { ...node.props, [id]: value }; - graph.save(); - graph.execute(); + if (graph) { + graph.save(); + graph.execute(); + } } diff --git a/app/src/lib/graph-interface/node/NodeParameter.svelte b/app/src/lib/graph-interface/node/NodeParameter.svelte index 720f796..46d5772 100644 --- a/app/src/lib/graph-interface/node/NodeParameter.svelte +++ b/app/src/lib/graph-interface/node/NodeParameter.svelte @@ -20,8 +20,8 @@ const socketId = `${node.id}-${id}`; const graph = getGraphManager(); - const graphId = graph.id; - const inputSockets = graph.inputSockets; + const graphId = graph?.id; + const inputSockets = graph?.inputSockets; const elementId = `input-${Math.random().toString(36).substring(7)}`; @@ -34,10 +34,10 @@ function handleMouseDown(ev: MouseEvent) { ev.preventDefault(); ev.stopPropagation(); - setDownSocket({ + setDownSocket?.({ node, index: id, - position: getSocketPosition(node, id), + position: getSocketPosition?.(node, id), }); } @@ -76,7 +76,7 @@ class:disabled={$possibleSocketIds && !$possibleSocketIds.has(socketId)} > {#key id && graphId} -
+
{#if inputType.label !== false} {/if} diff --git a/app/src/lib/helpers/createKeyMap.ts b/app/src/lib/helpers/createKeyMap.ts index 00e5846..f9cc3c9 100644 --- a/app/src/lib/helpers/createKeyMap.ts +++ b/app/src/lib/helpers/createKeyMap.ts @@ -1,4 +1,4 @@ -import { get, writable } from "svelte/store"; +import { derived, get, writable } from "svelte/store"; type Shortcut = { key: string | string[], @@ -9,35 +9,45 @@ type Shortcut = { callback: (event: KeyboardEvent) => void } +function getShortcutId(shortcut: Shortcut) { + return `${shortcut.key}${shortcut.shift ? "+shift" : ""}${shortcut.ctrl ? "+ctrl" : ""}${shortcut.alt ? "+alt" : ""}`; +} + export function createKeyMap(keys: Shortcut[]) { - const store = writable(keys); + const store = writable(new Map(keys.map(k => [getShortcutId(k), k]))); return { handleKeyboardEvent: (event: KeyboardEvent) => { - const key = get(store).find(k => { + const key = [...get(store).values()].find(k => { if (Array.isArray(k.key) ? !k.key.includes(event.key) : k.key !== event.key) return false; if ("shift" in k && k.shift !== event.shiftKey) return false; if ("ctrl" in k && k.ctrl !== event.ctrlKey) return false; if ("alt" in k && k.alt !== event.altKey) return false; return true; }); - console.log({ keys: get(store), out: key, key: event.key }); key?.callback(event); }, addShortcut: (shortcut: Shortcut) => { if (Array.isArray(shortcut.key)) { for (const k of shortcut.key) { - store.update(keys => { - if (keys.find(kk => kk.key === k)) return keys; - return [...keys, { ...shortcut, key: k }]; + store.update(shortcuts => { + const id = getShortcutId({ ...shortcut, key: k }); + shortcuts.delete(id); + shortcuts.set(id, { ...shortcut, key: k }); + return shortcuts; }); } } else { - store.update(keys => [...keys, shortcut]); + store.update(shortcuts => { + const id = getShortcutId(shortcut); + shortcuts.delete(id); + shortcuts.set(id, shortcut); + return shortcuts; + }); } }, - keys: store + keys: derived(store, $store => Array.from($store.values())) } } diff --git a/app/src/lib/node-registry-client.ts b/app/src/lib/node-registry-client.ts new file mode 100644 index 0000000..35af521 --- /dev/null +++ b/app/src/lib/node-registry-client.ts @@ -0,0 +1,93 @@ +import type { NodeRegistry, NodeType } from "@nodes/types"; +import { createWasmWrapper } from "@nodes/utils"; +import { createLogger } from "./helpers"; + +const log = createLogger("node-registry"); +export class RemoteNodeRegistry implements NodeRegistry { + + status: "loading" | "ready" | "error" = "loading"; + private nodes: Map = new Map(); + + constructor(private url: string) { } + + private async loadNode(id: `${string}/${string}/${string}`) { + const wasmResponse = await this.fetchNode(id); + + // Setup Wasm wrapper + const wrapper = createWasmWrapper(); + const module = new WebAssembly.Module(wasmResponse); + const instance = new WebAssembly.Instance(module, { ["./index_bg.js"]: wrapper }); + wrapper.setInstance(instance); + + const definition = wrapper.get_definition(); + + return { + ...definition, + id, + execute: wrapper.execute + }; + } + + async fetchUsers() { + const response = await fetch(`${this.url}/nodes/users.json`); + if (!response.ok) { + throw new Error(`Failed to load users`); + } + return response.json(); + } + + async fetchUser(userId: `${string}`) { + const response = await fetch(`${this.url}/nodes/${userId}.json`); + if (!response.ok) { + throw new Error(`Failed to load user ${userId}`); + } + return response.json(); + } + + async fetchCollection(userCollectionId: `${string}/${string}`) { + const response = await fetch(`${this.url}/nodes/${userCollectionId}.json`); + if (!response.ok) { + throw new Error(`Failed to load collection ${userCollectionId}`); + } + return response.json(); + } + + async fetchNode(nodeId: `${string}/${string}/${string}`) { + const response = await fetch(`${this.url}/nodes/${nodeId}.wasm`); + if (!response.ok) { + throw new Error(`Failed to load node wasm ${nodeId}`); + } + return response.arrayBuffer(); + } + + async fetchNodeDefinition(nodeId: `${string}/${string}/${string}`) { + const response = await fetch(`${this.url}/nodes/${nodeId}.json`); + if (!response.ok) { + throw new Error(`Failed to load node definition ${nodeId}`); + } + return response.json() + } + + async load(nodeIds: `${string}/${string}/${string}`[]) { + const a = performance.now(); + + const nodes = await Promise.all(nodeIds.map(id => this.loadNode(id))); + + for (const node of nodes) { + this.nodes.set(node.id, node); + } + + const duration = performance.now() - a; + + log.log("loaded nodes in", duration, "ms"); + this.status = "ready"; + } + + getNode(id: string) { + return this.nodes.get(id); + } + + getAllNodes() { + return [...this.nodes.values()]; + } +} diff --git a/app/src/lib/node-registry.ts b/app/src/lib/node-registry.ts index 24b9dc8..4dab055 100644 --- a/app/src/lib/node-registry.ts +++ b/app/src/lib/node-registry.ts @@ -1,120 +1,101 @@ -import type { NodeRegistry, NodeType } from "@nodes/types"; -import { createWasmWrapper } from "@nodes/utils"; +import { createWasmWrapper } from "@nodes/utils" +import fs from "fs/promises" +import path from "path" -import { createLogger } from "./helpers"; +export async function getWasm(id: `${string}/${string}/${string}`) { + const filePath = path.resolve(`../nodes/${id}/pkg/index_bg.wasm`); -const nodeTypes: NodeType[] = [ - { - id: "max/plantarium/float", - inputs: { - "value": { type: "float", value: 0.1, internal: true }, - }, - outputs: ["float"], - execute: (value) => { return value; } - }, - { - id: "max/plantarium/math", - inputs: { - "op_type": { label: "type", type: "select", options: ["add", "subtract", "multiply", "divide"], value: 0 }, - "a": { type: "float" }, - "b": { type: "float" }, - }, - outputs: ["float"], - execute: ([op_type, a, b]: number[]) => { - switch (op_type) { - case 0: return a + b; - case 1: return a - b; - case 2: return a * b; - case 3: return a / b; + try { + await fs.access(filePath); + } catch (e) { + return null + } + + const file = await fs.readFile(filePath); + + const bytes = new Uint8Array(file); + + return bytes; + +} + +export async function getNodeWasm(id: `${string}/${string}/${string}`) { + + const wasmBytes = await getWasm(id); + if (!wasmBytes) return null; + + const wrapper = createWasmWrapper(); + const module = new WebAssembly.Module(wasmBytes); + const instance = new WebAssembly.Instance(module, { ["./index_bg.js"]: wrapper }); + wrapper.setInstance(instance) + + return wrapper; +} + + +export async function getNode(id: `${string}/${string}/${string}`) { + + const wrapper = await getNodeWasm(id); + + const definition = wrapper?.get_definition?.(); + + if (!definition) return null; + + const { inputs, outputs } = definition; + try { + return { id, inputs, outputs } + } catch (e) { + console.log("Failed to parse input types for node", { id }); + } + +} + +export async function getCollectionNodes(userId: `${string}/${string}`) { + const nodes = await fs.readdir(path.resolve(`../nodes/${userId}`)); + return nodes + .filter(n => n !== "pkg" && n !== ".template") + .map(n => { + return { + id: `${userId}/${n}`, } - } - }, - { - id: "max/plantarium/output", - inputs: { - "input": { type: "float" }, - }, - outputs: [], - } -] + }) +} - -const log = createLogger("node-registry"); -export class RemoteNodeRegistry implements NodeRegistry { - - - status: "loading" | "ready" | "error" = "loading"; - private nodes: Map = new Map(); - - constructor(private url: string) { } - - private async loadNode(id: string) { - const nodeUrl = `${this.url}/n/${id}`; - const [response, wasmResponse] = await Promise.all([fetch(nodeUrl), fetch(`${nodeUrl}/wasm`)]); - if (!wasmResponse.ok || !response.ok) { - this.status = "error"; - throw new Error(`Failed to load node ${id}`); - } - - // Setup Wasm wrapper - const wrapper = createWasmWrapper(); - const module = new WebAssembly.Module(await wasmResponse.arrayBuffer()); - const instance = new WebAssembly.Instance(module, { ["./index_bg.js"]: wrapper }); - wrapper.setInstance(instance); - - const node = await response.json(); - node.execute = wrapper.execute; - return node; - } - - async load(nodeIds: string[]) { - const a = performance.now(); - - nodeIds.push("max/plantarium/random"); - nodeIds.push("max/plantarium/float"); - nodeIds.push("max/plantarium/triangle"); - nodeIds.push("max/plantarium/vec3"); - nodeIds.push("max/plantarium/output"); - nodeIds.push("max/plantarium/array"); - nodeIds.push("max/plantarium/sum"); - nodeIds.push("max/plantarium/stem"); - nodeIds.push("max/plantarium/box"); - nodeIds.push("max/plantarium/math"); - - const nodes = await Promise.all(nodeIds.map(id => this.loadNode(id))); - - for (const node of nodes) { - this.nodes.set(node.id, node); - } - - const duration = performance.now() - a; - - log.log("loaded nodes in", duration, "ms"); - this.status = "ready"; - } - - getNode(id: string) { - return this.nodes.get(id); - } - - getAllNodes() { - return [...this.nodes.values()]; +export async function getCollection(userId: `${string}/${string}`) { + const nodes = await getCollectionNodes(userId); + return { + id: userId, + nodes, } } -export class MemoryNodeRegistry implements NodeRegistry { +export async function getUserCollections(userId: string) { + const collections = await fs.readdir(path.resolve(`../nodes/${userId}`)); + return Promise.all(collections.map(async n => { + const nodes = await getCollectionNodes(`${userId}/${n}`); + return { + id: `${userId}/${n}`, + nodes, + } + })); +} - status: "loading" | "ready" | "error" = "ready"; - - async load(nodeIds: string[]) { - // Do nothing - } - - getNode(id: string) { - return nodeTypes.find((nodeType) => nodeType.id === id); - } - getAllNodes() { - return [...nodeTypes]; +export async function getUser(userId: string) { + const collections = await getUserCollections(userId); + return { + id: userId, + collections } } +export async function getUsers() { + const nodes = await fs.readdir(path.resolve("../nodes")); + const users = await Promise.all(nodes.map(async n => { + const collections = await getUserCollections(n); + return { + id: n, + collections + } + })) + return users; +} diff --git a/app/src/lib/node-store/BreadCrumbs.svelte b/app/src/lib/node-store/BreadCrumbs.svelte new file mode 100644 index 0000000..9e2c9fb --- /dev/null +++ b/app/src/lib/node-store/BreadCrumbs.svelte @@ -0,0 +1,81 @@ + + + + + diff --git a/app/src/lib/node-store/DraggableNode.svelte b/app/src/lib/node-store/DraggableNode.svelte new file mode 100644 index 0000000..7b77145 --- /dev/null +++ b/app/src/lib/node-store/DraggableNode.svelte @@ -0,0 +1,74 @@ + + +
+
{ + dragging = false; + }} + draggable={true} + role="button" + tabindex="0" + on:dragstart={handleDragStart} + > + +
+
+ + diff --git a/app/src/lib/node-store/NodeStore.svelte b/app/src/lib/node-store/NodeStore.svelte new file mode 100644 index 0000000..4e52b64 --- /dev/null +++ b/app/src/lib/node-store/NodeStore.svelte @@ -0,0 +1,92 @@ + + + + +
+ {#if !activeUser} +

Users

+ {#await nodeRegistry.fetchUsers()} +
Loading...
+ {:then users} + {#each users as user} + + {/each} + {:catch error} +
{error.message}
+ {/await} + {:else if !activeCollection} + {#await nodeRegistry.fetchUser(activeUser)} +
Loading...
+ {:then user} +

Collections

+ {#each user.collections as collection} + + {/each} + {:catch error} +
{error.message}
+ {/await} + {:else if !activeNode} +

Nodes

+ {#await nodeRegistry.fetchCollection(`${activeUser}/${activeCollection}`)} +
Loading...
+ {:then collection} + {#each collection.nodes as node} + + {/each} + {:catch error} +
{error.message}
+ {/await} + {:else} + {#await nodeRegistry.fetchNodeDefinition(`${activeUser}/${activeCollection}/${activeNode}`)} +
Loading...
+ {:then node} + + {:catch error} +
{error.message}
+ {/await} + {/if} +
+ + diff --git a/app/src/lib/node-store/Spinner.svelte b/app/src/lib/node-store/Spinner.svelte new file mode 100644 index 0000000..7b84e7b --- /dev/null +++ b/app/src/lib/node-store/Spinner.svelte @@ -0,0 +1,31 @@ + + + + + diff --git a/app/src/lib/viewer/Scene.svelte b/app/src/lib/result-viewer/Scene.svelte similarity index 100% rename from app/src/lib/viewer/Scene.svelte rename to app/src/lib/result-viewer/Scene.svelte diff --git a/app/src/lib/viewer/Viewer.svelte b/app/src/lib/result-viewer/Viewer.svelte similarity index 100% rename from app/src/lib/viewer/Viewer.svelte rename to app/src/lib/result-viewer/Viewer.svelte diff --git a/app/src/lib/settings/Keymap.svelte b/app/src/lib/settings/Keymap.svelte index eb1e6de..95b22de 100644 --- a/app/src/lib/settings/Keymap.svelte +++ b/app/src/lib/settings/Keymap.svelte @@ -14,15 +14,15 @@
{#if key.ctrl} - Ctrl + Ctrl + {/if} {#if key.shift} - Shift + Shift {/if} {#if key.alt} - Alt + Alt {/if} - {key.key} + {key.key}

{key.description}

@@ -51,21 +51,18 @@ .command-wrapper { display: flex; - justify-content: left; + justify-content: right; align-items: center; } .command { - background: var(--layer-3); + background: var(--outline); padding: 0.4em; + font-size: 0.8em; border-radius: 0.3em; white-space: nowrap; } - .command > * { - color: var(--layer-0); - } - p { font-size: 0.9em; margin: 0; diff --git a/app/src/lib/settings/NodeStore.svelte b/app/src/lib/settings/NodeStore.svelte deleted file mode 100644 index e69de29..0000000 diff --git a/app/src/lib/settings/Settings.svelte b/app/src/lib/settings/Settings.svelte index 22d217d..d147a17 100644 --- a/app/src/lib/settings/Settings.svelte +++ b/app/src/lib/settings/Settings.svelte @@ -113,7 +113,7 @@ transform 0.2s, background 0.2s ease; width: 30%; - min-width: 300px; + min-width: 350px; } h1 { diff --git a/app/src/routes/+layout.server.ts b/app/src/routes/+layout.server.ts new file mode 100644 index 0000000..189f71e --- /dev/null +++ b/app/src/routes/+layout.server.ts @@ -0,0 +1 @@ +export const prerender = true; diff --git a/app/src/routes/+layout.svelte b/app/src/routes/+layout.svelte index d92b1e7..f6a51ae 100644 --- a/app/src/routes/+layout.svelte +++ b/app/src/routes/+layout.svelte @@ -5,7 +5,3 @@ - -{#if false} - -{/if} diff --git a/app/src/routes/+page.svelte b/app/src/routes/+page.svelte index 8956baa..2403834 100644 --- a/app/src/routes/+page.svelte +++ b/app/src/routes/+page.svelte @@ -2,17 +2,20 @@ import Grid from "$lib/grid"; import GraphInterface from "$lib/graph-interface"; import { MemoryRuntimeExecutor } from "$lib/runtime-executor"; - import { RemoteNodeRegistry } from "$lib/node-registry"; + import { RemoteNodeRegistry } from "$lib/node-registry-client"; import * as templates from "$lib/graph-templates"; import type { Graph } from "@nodes/types"; - import Viewer from "$lib/viewer/Viewer.svelte"; + import Viewer from "$lib/result-viewer/Viewer.svelte"; import Settings from "$lib/settings/Settings.svelte"; import { AppSettings, AppSettingTypes } from "$lib/settings/app-settings"; import { get, writable, type Writable } from "svelte/store"; import Keymap from "$lib/settings/Keymap.svelte"; import type { createKeyMap } from "$lib/helpers/createKeyMap"; + import NodeStore from "$lib/node-store/NodeStore.svelte"; + import type { GraphManager } from "$lib/graph-interface/graph-manager"; + import { setContext } from "svelte"; - const nodeRegistry = new RemoteNodeRegistry("http://localhost:3001"); + const nodeRegistry = new RemoteNodeRegistry(""); const runtimeExecutor = new MemoryRuntimeExecutor(nodeRegistry); let res: Int32Array; @@ -21,7 +24,11 @@ ? JSON.parse(localStorage.getItem("graph")!) : templates.grid(3, 3); + let manager: GraphManager; let managerStatus: Writable<"loading" | "error" | "idle">; + $: if (manager) { + setContext("graphManager", manager); + } let keymap: ReturnType; @@ -41,6 +48,7 @@ definition: AppSettingTypes, }, shortcuts: {}, + nodeStore: {}, graph: {}, }; @@ -53,7 +61,16 @@ }; settings = settings; - console.log({ settings }); + } + + $: if (manager) { + settings.nodeStore = { + id: "Node Store", + icon: "i-tabler-database", + props: { nodeRegistry, manager }, + component: NodeStore, + }; + settings = settings; } function handleSettings( @@ -91,10 +108,10 @@ {#key graph} { + const users = await registry.getUsers(); + return users.map(user => { + return { user: user.id } + }).flat(2); +} + + +export const GET: RequestHandler = async function GET({ params }) { + + const namespaces = await registry.getUser(params.user) + + return json(namespaces); + +} diff --git a/app/src/routes/nodes/[user]/[collection].json/+server.ts b/app/src/routes/nodes/[user]/[collection].json/+server.ts new file mode 100644 index 0000000..7813fa2 --- /dev/null +++ b/app/src/routes/nodes/[user]/[collection].json/+server.ts @@ -0,0 +1,22 @@ +import { json } from "@sveltejs/kit"; +import type { EntryGenerator, RequestHandler } from "./$types"; +import * as registry from "$lib/node-registry"; + +export const prerender = true; + +export const entries: EntryGenerator = async () => { + const users = await registry.getUsers(); + return users.map(user => { + return user.collections.map(collection => { + return { user: user.id, collection: collection.id } + }) + }).flat(2); +} + +export const GET: RequestHandler = async function GET({ params }) { + + const namespaces = await registry.getCollection(`${params.user}/${params.collection}`); + + return json(namespaces); + +} diff --git a/app/src/routes/nodes/[user]/[collection]/[node].json/+server.ts b/app/src/routes/nodes/[user]/[collection]/[node].json/+server.ts new file mode 100644 index 0000000..e44d547 --- /dev/null +++ b/app/src/routes/nodes/[user]/[collection]/[node].json/+server.ts @@ -0,0 +1,31 @@ +import { json } from "@sveltejs/kit"; +import type { EntryGenerator, RequestHandler } from "./$types"; +import { getNode } from "$lib/node-registry"; +import * as registry from "$lib/node-registry"; + +export const prerender = true; + + +export const entries: EntryGenerator = async () => { + const users = await registry.getUsers(); + return users.map(user => { + return user.collections.map(collection => { + return collection.nodes.map(node => { + return { user: user.id, collection: collection.id, node: node.id } + }); + }) + }).flat(2); +} + +export const GET: RequestHandler = async function GET({ params }) { + + const nodeId = `${params.user}/${params.collection}/${params.node}` as const; + + try { + const node = await getNode(nodeId); + return json(node); + } catch (err) { + console.log(err) + return new Response("Not found", { status: 404 }); + } +} diff --git a/app/src/routes/nodes/[user]/[collection]/[node].wasm/+server.ts b/app/src/routes/nodes/[user]/[collection]/[node].wasm/+server.ts new file mode 100644 index 0000000..372f63c --- /dev/null +++ b/app/src/routes/nodes/[user]/[collection]/[node].wasm/+server.ts @@ -0,0 +1,27 @@ +import type { RequestHandler } from "./$types"; +import * as registry from "$lib/node-registry"; +import type { EntryGenerator } from "../$types"; + +export const prerender = true; + +export const entries: EntryGenerator = async () => { + const users = await registry.getUsers(); + return users.map(user => { + return user.collections.map(collection => { + return collection.nodes.map(node => { + return { user: user.id, collection: collection.id, node: node.id } + }); + }) + }).flat(2); +} + +export const GET: RequestHandler = async function GET({ params }) { + + const wasm = await registry.getWasm(`${params.user}/${params.collection}/${params.node}`); + + if (!wasm) { + return new Response("Not found", { status: 404 }); + } + + return new Response(wasm, { status: 200, headers: { "Content-Type": "application/wasm" } }); +} diff --git a/app/src/routes/nodes/users.json/+server.ts b/app/src/routes/nodes/users.json/+server.ts new file mode 100644 index 0000000..fe03ee0 --- /dev/null +++ b/app/src/routes/nodes/users.json/+server.ts @@ -0,0 +1,14 @@ +import { json } from "@sveltejs/kit"; +import type { RequestHandler } from "./$types"; + +import * as registry from "$lib/node-registry"; + +export const prerender = true; + +export const GET: RequestHandler = async function GET() { + + const users = await registry.getUsers(); + + return json(users); + +} diff --git a/packages/node-registry/.eslintignore b/packages/node-registry/.eslintignore deleted file mode 100644 index 3897265..0000000 --- a/packages/node-registry/.eslintignore +++ /dev/null @@ -1,13 +0,0 @@ -.DS_Store -node_modules -/build -/.svelte-kit -/package -.env -.env.* -!.env.example - -# Ignore files for PNPM, NPM and YARN -pnpm-lock.yaml -package-lock.json -yarn.lock diff --git a/packages/node-registry/.eslintrc.cjs b/packages/node-registry/.eslintrc.cjs deleted file mode 100644 index 0b75758..0000000 --- a/packages/node-registry/.eslintrc.cjs +++ /dev/null @@ -1,31 +0,0 @@ -/** @type { import("eslint").Linter.Config } */ -module.exports = { - root: true, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:svelte/recommended', - 'prettier' - ], - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - parserOptions: { - sourceType: 'module', - ecmaVersion: 2020, - extraFileExtensions: ['.svelte'] - }, - env: { - browser: true, - es2017: true, - node: true - }, - overrides: [ - { - files: ['*.svelte'], - parser: 'svelte-eslint-parser', - parserOptions: { - parser: '@typescript-eslint/parser' - } - } - ] -}; diff --git a/packages/node-registry/.gitignore b/packages/node-registry/.gitignore deleted file mode 100644 index 6635cf5..0000000 --- a/packages/node-registry/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -.DS_Store -node_modules -/build -/.svelte-kit -/package -.env -.env.* -!.env.example -vite.config.js.timestamp-* -vite.config.ts.timestamp-* diff --git a/packages/node-registry/.npmrc b/packages/node-registry/.npmrc deleted file mode 100644 index b6f27f1..0000000 --- a/packages/node-registry/.npmrc +++ /dev/null @@ -1 +0,0 @@ -engine-strict=true diff --git a/packages/node-registry/.prettierignore b/packages/node-registry/.prettierignore deleted file mode 100644 index cc41cea..0000000 --- a/packages/node-registry/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore files for PNPM, NPM and YARN -pnpm-lock.yaml -package-lock.json -yarn.lock diff --git a/packages/node-registry/.prettierrc b/packages/node-registry/.prettierrc deleted file mode 100644 index 9573023..0000000 --- a/packages/node-registry/.prettierrc +++ /dev/null @@ -1,8 +0,0 @@ -{ - "useTabs": true, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 100, - "plugins": ["prettier-plugin-svelte"], - "overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }] -} diff --git a/packages/node-registry/README.md b/packages/node-registry/README.md deleted file mode 100644 index 5ce6766..0000000 --- a/packages/node-registry/README.md +++ /dev/null @@ -1,38 +0,0 @@ -# create-svelte - -Everything you need to build a Svelte project, powered by [`create-svelte`](https://github.com/sveltejs/kit/tree/main/packages/create-svelte). - -## Creating a project - -If you're seeing this, you've probably already done this step. Congrats! - -```bash -# create a new project in the current directory -npm create svelte@latest - -# create a new project in my-app -npm create svelte@latest my-app -``` - -## Developing - -Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: - -```bash -npm run dev - -# or start the server and open the app in a new browser tab -npm run dev -- --open -``` - -## Building - -To create a production version of your app: - -```bash -npm run build -``` - -You can preview the production build with `npm run preview`. - -> To deploy your app, you may need to install an [adapter](https://kit.svelte.dev/docs/adapters) for your target environment. diff --git a/packages/node-registry/package.json b/packages/node-registry/package.json deleted file mode 100644 index 3d97ee4..0000000 --- a/packages/node-registry/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "node-registry", - "version": "0.0.1", - "private": true, - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "test": "vitest", - "lint": "prettier --check . && eslint .", - "format": "prettier --write ." - }, - "devDependencies": { - "@sveltejs/adapter-auto": "^3.2.0", - "@sveltejs/kit": "^2.5.6", - "@sveltejs/vite-plugin-svelte": "^3.1.0", - "@types/eslint": "^8.56.9", - "@typescript-eslint/eslint-plugin": "^7.7.0", - "@typescript-eslint/parser": "^7.7.0", - "eslint": "^9.0.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.37.0", - "prettier": "^3.2.5", - "prettier-plugin-svelte": "^3.2.3", - "svelte": "^4.2.15", - "svelte-check": "^3.6.9", - "tslib": "^2.6.2", - "typescript": "^5.4.5", - "vite": "^5.2.9", - "vite-plugin-wasm": "^3.3.0", - "vitest": "^1.5.0" - }, - "type": "module", - "dependencies": { - "@nodes/utils": "link:../utils", - "utils": "link:../utils" - } -} diff --git a/packages/node-registry/src/app.d.ts b/packages/node-registry/src/app.d.ts deleted file mode 100644 index 743f07b..0000000 --- a/packages/node-registry/src/app.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -// See https://kit.svelte.dev/docs/types#app -// for information about these interfaces -declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } -} - -export {}; diff --git a/packages/node-registry/src/app.html b/packages/node-registry/src/app.html deleted file mode 100644 index 77a5ff5..0000000 --- a/packages/node-registry/src/app.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - %sveltekit.head% - - -
%sveltekit.body%
- - diff --git a/packages/node-registry/src/lib/index.ts b/packages/node-registry/src/lib/index.ts deleted file mode 100644 index 856f2b6..0000000 --- a/packages/node-registry/src/lib/index.ts +++ /dev/null @@ -1 +0,0 @@ -// place files you want to import through the `$lib` alias in this folder. diff --git a/packages/node-registry/src/lib/registry.ts b/packages/node-registry/src/lib/registry.ts deleted file mode 100644 index 5dadd66..0000000 --- a/packages/node-registry/src/lib/registry.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { createWasmWrapper } from "@nodes/utils" - -export async function getNodeWasm(id: `${string}/${string}/${string}`) { - - const wasmResponse = await fetch(`/n/${id}/wasm`); - - if (!wasmResponse.ok) { - throw new Error(`Failed to load node ${id}`); - } - - const wrapper = createWasmWrapper(); - const module = new WebAssembly.Module(await wasmResponse.arrayBuffer()); - const instance = new WebAssembly.Instance(module, { ["./index_bg.js"]: wrapper }); - wrapper.setInstance(instance) - - return wrapper; -} - - -export async function getNode(id: `${string}/${string}/${string}`) { - - const wrapper = await getNodeWasm(id); - - const { inputs, outputs } = wrapper?.get_definition?.(); - try { - return { id, inputs, outputs } - } catch (e) { - console.log("Failed to parse input types for node", { id }); - } - - -} diff --git a/packages/node-registry/src/routes/+page.svelte b/packages/node-registry/src/routes/+page.svelte deleted file mode 100644 index 5982b0a..0000000 --- a/packages/node-registry/src/routes/+page.svelte +++ /dev/null @@ -1,2 +0,0 @@ -

Welcome to SvelteKit

-

Visit kit.svelte.dev to read the documentation

diff --git a/packages/node-registry/src/routes/n/[user]/[collection]/[node]/+server.ts b/packages/node-registry/src/routes/n/[user]/[collection]/[node]/+server.ts deleted file mode 100644 index 277846b..0000000 --- a/packages/node-registry/src/routes/n/[user]/[collection]/[node]/+server.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { json } from "@sveltejs/kit"; -import type { RequestHandler } from "./$types"; -import { getNode } from "$lib/registry"; - -export const GET: RequestHandler = async function GET({ fetch, params }) { - globalThis.fetch = fetch; - - const nodeId = `${params.user}/${params.collection}/${params.node}` as const; - - try { - const node = await getNode(nodeId); - return json(node); - } catch (err) { - console.log(err) - return new Response("Not found", { status: 404 }); - } -} diff --git a/packages/node-registry/src/routes/n/[user]/[collection]/[node]/test/+page.svelte b/packages/node-registry/src/routes/n/[user]/[collection]/[node]/test/+page.svelte deleted file mode 100644 index f4741ad..0000000 --- a/packages/node-registry/src/routes/n/[user]/[collection]/[node]/test/+page.svelte +++ /dev/null @@ -1,27 +0,0 @@ - - -

{data.params.user}/{data.params.collection}/{data.params.node}

- -

Node Definition

-{#if !node} -

Loading Node

-{:else} -
{JSON.stringify(node, null, 2)}
-{/if} diff --git a/packages/node-registry/src/routes/n/[user]/[collection]/[node]/test/+page.ts b/packages/node-registry/src/routes/n/[user]/[collection]/[node]/test/+page.ts deleted file mode 100644 index 3146b1d..0000000 --- a/packages/node-registry/src/routes/n/[user]/[collection]/[node]/test/+page.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { PageLoad } from "./$types"; - -export const load: PageLoad = ({ params }) => { - return { - params - } -}; diff --git a/packages/node-registry/src/routes/n/[user]/[collection]/[node]/wasm/+server.ts b/packages/node-registry/src/routes/n/[user]/[collection]/[node]/wasm/+server.ts deleted file mode 100644 index 23dc119..0000000 --- a/packages/node-registry/src/routes/n/[user]/[collection]/[node]/wasm/+server.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { RequestHandler } from "./$types"; -import fs from "fs/promises"; -import path from "path"; - -export const GET: RequestHandler = async function GET({ fetch, params }) { - - const filePath = path.resolve(`../../nodes/${params.user}/${params.collection}/${params.node}/pkg/index_bg.wasm`); - - try { - await fs.access(filePath); - } catch (e) { - return new Response("Not found", { status: 404 }); - } - - const file = await fs.readFile(filePath); - - const bytes = new Uint8Array(file); - - return new Response(bytes, { status: 200, headers: { "Content-Type": "application/wasm" } }); -} diff --git a/packages/node-registry/static/favicon.ico b/packages/node-registry/static/favicon.ico deleted file mode 100644 index 3a7e150..0000000 Binary files a/packages/node-registry/static/favicon.ico and /dev/null differ diff --git a/packages/node-registry/static/favicon.png b/packages/node-registry/static/favicon.png deleted file mode 100644 index 825b9e6..0000000 Binary files a/packages/node-registry/static/favicon.png and /dev/null differ diff --git a/packages/node-registry/svelte.config.js b/packages/node-registry/svelte.config.js deleted file mode 100644 index 4b6423e..0000000 --- a/packages/node-registry/svelte.config.js +++ /dev/null @@ -1,20 +0,0 @@ -import adapter from '@sveltejs/adapter-auto'; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - // Consult https://kit.svelte.dev/docs/integrations#preprocessors - // for more information about preprocessors - preprocess: vitePreprocess(), - - - - kit: { - // adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list. - // If your environment is not supported or you settled on a specific environment, switch out the adapter. - // See https://kit.svelte.dev/docs/adapters for more information about adapters. - adapter: adapter() - } -}; - -export default config; diff --git a/packages/node-registry/tsconfig.json b/packages/node-registry/tsconfig.json deleted file mode 100644 index fc93cbd..0000000 --- a/packages/node-registry/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "moduleResolution": "bundler" - } - // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias - // except $lib which is handled by https://kit.svelte.dev/docs/configuration#files - // - // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes - // from the referenced tsconfig.json - TypeScript does not merge them in -} diff --git a/packages/node-registry/vite.config.ts b/packages/node-registry/vite.config.ts deleted file mode 100644 index 9f4259e..0000000 --- a/packages/node-registry/vite.config.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { sveltekit } from '@sveltejs/kit/vite'; -import { defineConfig } from 'vitest/config'; -import wasm from 'vite-plugin-wasm'; - -export default defineConfig({ - plugins: [sveltekit(), wasm()], - - server: { - port: 3001, - }, - test: { - include: ['src/**/*.{test,spec}.{js,ts}'] - } -}); diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 2c6b6f6..477195f 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -35,7 +35,7 @@ export type NodeType = { meta?: { title?: string; }, - execute?: (args: number[]) => unknown; + execute?: (args: Int32Array) => Int32Array; } export type Socket = { @@ -44,7 +44,6 @@ export type Socket = { position: [number, number]; }; - export interface NodeRegistry { /** * The status of the node registry