feat: add add menu
This commit is contained in:
		
							
								
								
									
										164
									
								
								frontend/src/lib/components/AddMenu.svelte
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								frontend/src/lib/components/AddMenu.svelte
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,164 @@ | ||||
| <script lang="ts"> | ||||
|   import type { GraphManager } from "$lib/graph-manager"; | ||||
|   import { HTML } from "@threlte/extras"; | ||||
|   import { onMount } from "svelte"; | ||||
|  | ||||
|   export let position: [x: number, y: number] | null; | ||||
|  | ||||
|   export let graph: GraphManager; | ||||
|  | ||||
|   let input: HTMLInputElement; | ||||
|   let value: string = ""; | ||||
|   let activeNodeId: string = ""; | ||||
|  | ||||
|   const allNodes = graph.getNodeTypes(); | ||||
|  | ||||
|   function filterNodes() { | ||||
|     return allNodes.filter((node) => node.id.includes(value)); | ||||
|   } | ||||
|  | ||||
|   $: nodes = value === "" ? allNodes : filterNodes(); | ||||
|   $: if (nodes) { | ||||
|     if (activeNodeId === "") { | ||||
|       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(); | ||||
|     const value = (event.target as HTMLInputElement).value; | ||||
|  | ||||
|     if (event.key === "Escape") { | ||||
|       position = null; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (event.key === "ArrowDown") { | ||||
|       const index = nodes.findIndex((node) => node.id === activeNodeId); | ||||
|       activeNodeId = nodes[(index + 1) % nodes.length].id; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (event.key === "ArrowUp") { | ||||
|       const index = nodes.findIndex((node) => node.id === activeNodeId); | ||||
|       activeNodeId = nodes[(index - 1 + nodes.length) % nodes.length].id; | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (event.key === "Enter") { | ||||
|       if (activeNodeId && position) { | ||||
|         graph.createNode({ type: activeNodeId, position }); | ||||
|         position = null; | ||||
|       } | ||||
|       return; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   onMount(() => { | ||||
|     input.disabled = false; | ||||
|     setTimeout(() => input.focus(), 50); | ||||
|   }); | ||||
| </script> | ||||
|  | ||||
| <HTML position.x={position?.[0]} position.z={position?.[1]} transform={false}> | ||||
|   <div class="wrapper"> | ||||
|     <div class="header"> | ||||
|       <input | ||||
|         id="add-menu" | ||||
|         type="text" | ||||
|         aria-label="Search for a node type" | ||||
|         role="searchbox" | ||||
|         placeholder="Search..." | ||||
|         disabled={false} | ||||
|         on:keydown={handleKeyDown} | ||||
|         bind:value | ||||
|         bind:this={input} | ||||
|       /> | ||||
|     </div> | ||||
|  | ||||
|     <div class="content"> | ||||
|       {#each nodes as node} | ||||
|         <div | ||||
|           class="result" | ||||
|           role="treeitem" | ||||
|           tabindex="0" | ||||
|           aria-selected={node.id === activeNodeId} | ||||
|           on:keydown={(event) => { | ||||
|             if (event.key === "Enter") { | ||||
|               if (position) { | ||||
|                 graph.createNode({ type: node.id, position }); | ||||
|                 position = null; | ||||
|               } | ||||
|             } | ||||
|           }} | ||||
|           on:mousedown={() => { | ||||
|             if (position) { | ||||
|               graph.createNode({ type: node.id, position }); | ||||
|               position = null; | ||||
|             } | ||||
|           }} | ||||
|           on:focus={() => { | ||||
|             activeNodeId = node.id; | ||||
|           }} | ||||
|           class:selected={node.id === activeNodeId} | ||||
|           on:mouseover={() => { | ||||
|             activeNodeId = node.id; | ||||
|           }} | ||||
|         > | ||||
|           {node.id} | ||||
|         </div> | ||||
|       {/each} | ||||
|     </div> | ||||
|   </div> | ||||
| </HTML> | ||||
|  | ||||
| <style> | ||||
|   input { | ||||
|     background: var(--background-color-lighter); | ||||
|     font-family: var(--font-family); | ||||
|     border: none; | ||||
|     color: var(--text-color); | ||||
|     padding: 0.8em; | ||||
|     width: calc(100% - 2px); | ||||
|     box-sizing: border-box; | ||||
|     font-size: 1em; | ||||
|     margin-left: 1px; | ||||
|     margin-top: 1px; | ||||
|   } | ||||
|  | ||||
|   input:focus { | ||||
|     outline: solid 2px rgba(255, 255, 255, 0.2); | ||||
|   } | ||||
|  | ||||
|   .wrapper { | ||||
|     position: absolute; | ||||
|     background: var(--background-color); | ||||
|     border-radius: 7px; | ||||
|     overflow: hidden; | ||||
|     border: solid 2px var(--background-color-lighter); | ||||
|     width: 150px; | ||||
|   } | ||||
|   .content { | ||||
|     min-height: none; | ||||
|     width: 100%; | ||||
|     color: var(--text-color); | ||||
|   } | ||||
|  | ||||
|   .result { | ||||
|     padding: 1em 0.9em; | ||||
|     border-bottom: solid 1px var(--background-color-lighter); | ||||
|     opacity: 0.7; | ||||
|     font-size: 0.9em; | ||||
|     cursor: pointer; | ||||
|   } | ||||
|  | ||||
|   .result[aria-selected="true"] { | ||||
|     background: var(--background-color-lighter); | ||||
|     opacity: 1; | ||||
|   } | ||||
| </style> | ||||
| @@ -18,6 +18,7 @@ | ||||
|     selectedNodes, | ||||
|   } from "./stores"; | ||||
|   import BoxSelection from "../BoxSelection.svelte"; | ||||
|   import AddMenu from "../AddMenu.svelte"; | ||||
|  | ||||
|   export let graph: GraphManager; | ||||
|   setContext("graphManager", graph); | ||||
| @@ -36,6 +37,7 @@ | ||||
|   let loaded = false; | ||||
|   const cameraDown = [0, 0]; | ||||
|   let cameraPosition: [number, number, number] = [0, 0, 4]; | ||||
|   let addMenuPosition: [number, number] | null = null; | ||||
|  | ||||
|   $: if (cameraPosition && loaded) { | ||||
|     localStorage.setItem("cameraPosition", JSON.stringify(cameraPosition)); | ||||
| @@ -79,16 +81,19 @@ | ||||
|         node.tmp.ref.style.setProperty("--ny", `${node.tmp.y * 10}px`); | ||||
|         node.tmp.mesh.position.x = node.tmp.x + 10; | ||||
|         node.tmp.mesh.position.z = node.tmp.y + getNodeHeight(node.type) / 2; | ||||
|         if (node.tmp.x === node.position.x && node.tmp.y === node.position.y) { | ||||
|         if ( | ||||
|           node.tmp.x === node.position[0] && | ||||
|           node.tmp.y === node.position[1] | ||||
|         ) { | ||||
|           delete node.tmp.x; | ||||
|           delete node.tmp.y; | ||||
|         } | ||||
|       } else { | ||||
|         node.tmp.ref.style.setProperty("--nx", `${node.position.x * 10}px`); | ||||
|         node.tmp.ref.style.setProperty("--ny", `${node.position.y * 10}px`); | ||||
|         node.tmp.mesh.position.x = node.position.x + 10; | ||||
|         node.tmp.ref.style.setProperty("--nx", `${node.position[0] * 10}px`); | ||||
|         node.tmp.ref.style.setProperty("--ny", `${node.position[1] * 10}px`); | ||||
|         node.tmp.mesh.position.x = node.position[0] + 10; | ||||
|         node.tmp.mesh.position.z = | ||||
|           node.position.y + getNodeHeight(node.type) / 2; | ||||
|           node.position[1] + getNodeHeight(node.type) / 2; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -113,10 +118,10 @@ | ||||
|     const height = getNodeHeight(node.type); | ||||
|     const width = 20; | ||||
|     return ( | ||||
|       node.position.x > cameraBounds[0] - width && | ||||
|       node.position.x < cameraBounds[1] && | ||||
|       node.position.y > cameraBounds[2] - height && | ||||
|       node.position.y < cameraBounds[3] | ||||
|       node.position[0] > cameraBounds[0] - width && | ||||
|       node.position[0] < cameraBounds[1] && | ||||
|       node.position[1] > cameraBounds[2] - height && | ||||
|       node.position[1] < cameraBounds[3] | ||||
|     ); | ||||
|   }); | ||||
|  | ||||
| @@ -141,8 +146,8 @@ | ||||
|           event.clientY, | ||||
|         ); | ||||
|         for (const node of $nodes.values()) { | ||||
|           const x = node.position.x; | ||||
|           const y = node.position.y; | ||||
|           const x = node.position[0]; | ||||
|           const y = node.position[1]; | ||||
|           const height = getNodeHeight(node.type); | ||||
|           if (downX > x && downX < x + 20 && downY > y && downY < y + height) { | ||||
|             clickedNodeId = node.id; | ||||
| @@ -213,14 +218,14 @@ | ||||
|   ): [number, number] { | ||||
|     if (typeof index === "number") { | ||||
|       return [ | ||||
|         (node?.tmp?.x ?? node.position.x) + 20, | ||||
|         (node?.tmp?.y ?? node.position.y) + 2.5 + 10 * index, | ||||
|         (node?.tmp?.x ?? node.position[0]) + 20, | ||||
|         (node?.tmp?.y ?? node.position[1]) + 2.5 + 10 * index, | ||||
|       ]; | ||||
|     } else { | ||||
|       const _index = Object.keys(node.tmp?.type?.inputs || {}).indexOf(index); | ||||
|       return [ | ||||
|         node?.tmp?.x ?? node.position.x, | ||||
|         (node?.tmp?.y ?? node.position.y) + 10 + 10 * _index, | ||||
|         node?.tmp?.x ?? node.position[0], | ||||
|         (node?.tmp?.y ?? node.position[1]) + 10 + 10 * _index, | ||||
|       ]; | ||||
|     } | ||||
|   } | ||||
| @@ -273,8 +278,8 @@ | ||||
|       const y2 = Math.max(mouseD[1], mousePosition[1]); | ||||
|       for (const node of $nodes.values()) { | ||||
|         if (!node?.tmp) continue; | ||||
|         const x = node.position.x; | ||||
|         const y = node.position.y; | ||||
|         const x = node.position[0]; | ||||
|         const y = node.position[1]; | ||||
|         const height = getNodeHeight(node.type); | ||||
|         if (x > x1 - 20 && x < x2 && y > y1 - height && y < y2) { | ||||
|           $selectedNodes?.add(node.id); | ||||
| @@ -420,15 +425,15 @@ | ||||
|     const node = graph.getNode($activeNodeId); | ||||
|     if (!node) return; | ||||
|     node.tmp = node.tmp || {}; | ||||
|     node.tmp.downX = node.position.x; | ||||
|     node.tmp.downY = node.position.y; | ||||
|     node.tmp.downX = node.position[0]; | ||||
|     node.tmp.downY = node.position[1]; | ||||
|     if ($selectedNodes) { | ||||
|       for (const nodeId of $selectedNodes) { | ||||
|         const n = graph.getNode(nodeId); | ||||
|         if (!n) continue; | ||||
|         n.tmp = n.tmp || {}; | ||||
|         n.tmp.downX = n.position.x; | ||||
|         n.tmp.downY = n.position.y; | ||||
|         n.tmp.downX = n.position[0]; | ||||
|         n.tmp.downY = n.position[1]; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| @@ -438,6 +443,11 @@ | ||||
|       document.activeElement === document.body || | ||||
|       document?.activeElement?.id === "graph"; | ||||
|  | ||||
|     if (event.key === "l") { | ||||
|       const activeNode = graph.getNode($activeNodeId); | ||||
|       console.log(activeNode); | ||||
|     } | ||||
|  | ||||
|     if (event.key === "Escape") { | ||||
|       $activeNodeId = -1; | ||||
|       $selectedNodes?.clear(); | ||||
| @@ -445,14 +455,18 @@ | ||||
|       (document.activeElement as HTMLElement)?.blur(); | ||||
|     } | ||||
|  | ||||
|     if (event.key === "A" && event.shiftKey) { | ||||
|       addMenuPosition = [mousePosition[0], mousePosition[1]]; | ||||
|     } | ||||
|  | ||||
|     if (event.key === ".") { | ||||
|       const average = [0, 0]; | ||||
|       for (const node of $nodes.values()) { | ||||
|         average[0] += node.position.x; | ||||
|         average[1] += node.position.y; | ||||
|         average[0] += node.position[0]; | ||||
|         average[1] += node.position[1]; | ||||
|       } | ||||
|       average[0] /= $nodes.size; | ||||
|       average[1] /= $nodes.size; | ||||
|       average[0] = average[0] ? average[0] / $nodes.size : 0; | ||||
|       average[1] = average[1] ? average[1] / $nodes.size : 0; | ||||
|  | ||||
|       const camX = cameraPosition[0]; | ||||
|       const camY = cameraPosition[1]; | ||||
| @@ -466,6 +480,7 @@ | ||||
|           lerp(camY, average[1], ease(a)), | ||||
|           lerp(camZ, 2, ease(a)), | ||||
|         ); | ||||
|         if (mouseDown) return false; | ||||
|       }); | ||||
|     } | ||||
|  | ||||
| @@ -538,12 +553,12 @@ | ||||
|       activeNode.tmp = activeNode.tmp || {}; | ||||
|       activeNode.tmp.isMoving = false; | ||||
|       const snapLevel = getSnapLevel(); | ||||
|       activeNode.position.x = snapToGrid( | ||||
|         activeNode?.tmp?.x ?? activeNode.position.x, | ||||
|       activeNode.position[0] = snapToGrid( | ||||
|         activeNode?.tmp?.x ?? activeNode.position[0], | ||||
|         5 / snapLevel, | ||||
|       ); | ||||
|       activeNode.position.y = snapToGrid( | ||||
|         activeNode?.tmp?.y ?? activeNode.position.y, | ||||
|       activeNode.position[1] = snapToGrid( | ||||
|         activeNode?.tmp?.y ?? activeNode.position[1], | ||||
|         5 / snapLevel, | ||||
|       ); | ||||
|       const nodes = [ | ||||
| @@ -551,8 +566,8 @@ | ||||
|       ] as NodeType[]; | ||||
|  | ||||
|       const vec = [ | ||||
|         activeNode.position.x - (activeNode?.tmp.x || 0), | ||||
|         activeNode.position.y - (activeNode?.tmp.y || 0), | ||||
|         activeNode.position[0] - (activeNode?.tmp.x || 0), | ||||
|         activeNode.position[1] - (activeNode?.tmp.y || 0), | ||||
|       ]; | ||||
|  | ||||
|       for (const node of nodes) { | ||||
| @@ -560,8 +575,8 @@ | ||||
|         node.tmp = node.tmp || {}; | ||||
|         const { x, y } = node.tmp; | ||||
|         if (x !== undefined && y !== undefined) { | ||||
|           node.position.x = x + vec[0]; | ||||
|           node.position.y = y + vec[1]; | ||||
|           node.position[0] = x + vec[0]; | ||||
|           node.position[1] = y + vec[1]; | ||||
|         } | ||||
|       } | ||||
|       nodes.push(activeNode); | ||||
| @@ -572,8 +587,8 @@ | ||||
|             node.tmp["x"] !== undefined && | ||||
|             node.tmp["y"] !== undefined | ||||
|           ) { | ||||
|             node.tmp.x = lerp(node.tmp.x, node.position.x, a); | ||||
|             node.tmp.y = lerp(node.tmp.y, node.position.y, a); | ||||
|             node.tmp.x = lerp(node.tmp.x, node.position[0], a); | ||||
|             node.tmp.y = lerp(node.tmp.y, node.position[1], a); | ||||
|             updateNodePosition(node); | ||||
|             if (node?.tmp?.isMoving) { | ||||
|               return false; | ||||
| @@ -627,6 +642,7 @@ | ||||
|     $possibleSockets = []; | ||||
|     $possibleSocketIds = null; | ||||
|     $hoveredSocket = null; | ||||
|     addMenuPosition = null; | ||||
|   } | ||||
|  | ||||
|   onMount(() => { | ||||
| @@ -669,12 +685,17 @@ | ||||
| {/if} | ||||
|  | ||||
| {#if $status === "idle"} | ||||
|   {#if addMenuPosition} | ||||
|     <AddMenu bind:position={addMenuPosition} {graph} /> | ||||
|   {/if} | ||||
|  | ||||
|   {#if $activeSocket} | ||||
|     <FloatingEdge | ||||
|       from={{ x: $activeSocket.position[0], y: $activeSocket.position[1] }} | ||||
|       to={{ x: mousePosition[0], y: mousePosition[1] }} | ||||
|     /> | ||||
|   {/if} | ||||
|  | ||||
|   {#key $graphId} | ||||
|     <GraphView {nodes} {edges} {cameraPosition} /> | ||||
|   {/key} | ||||
|   | ||||
| @@ -29,14 +29,14 @@ | ||||
|   onMount(() => { | ||||
|     for (const node of $nodes.values()) { | ||||
|       if (node?.tmp?.ref) { | ||||
|         node.tmp.ref.style.setProperty("--nx", `${node.position.x * 10}px`); | ||||
|         node.tmp.ref.style.setProperty("--ny", `${node.position.y * 10}px`); | ||||
|         node.tmp.ref.style.setProperty("--nx", `${node.position[0] * 10}px`); | ||||
|         node.tmp.ref.style.setProperty("--ny", `${node.position[1] * 10}px`); | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
| </script> | ||||
|  | ||||
| {#each $edges as edge (edge[0].id + edge[2].id + edge[3])} | ||||
| {#each $edges as edge (`${edge[0].id}-${edge[1]}-${edge[2].id}-${edge[3]}`)} | ||||
|   {@const pos = getEdgePosition(edge)} | ||||
|   {@const [x1, y1, x2, y2] = pos} | ||||
|   <Edge | ||||
|   | ||||
| @@ -11,7 +11,6 @@ export const hoveredSocket: Writable<Socket | null> = writable(null); | ||||
| export const possibleSockets: Writable<Socket[]> = writable([]); | ||||
| export const possibleSocketIds: Writable<Set<string> | null> = writable(null); | ||||
|  | ||||
|  | ||||
| export const colors = writable({ | ||||
|   backgroundColorDarker: new Color().setStyle("#101010"), | ||||
|   backgroundColor: new Color().setStyle("#151515"), | ||||
| @@ -38,13 +37,7 @@ if (browser) { | ||||
|  | ||||
|   } | ||||
|  | ||||
|  | ||||
|   globalThis["updateColors"] = updateColors; | ||||
|  | ||||
|   body.addEventListener("transitionstart", () => { | ||||
|     updateColors(); | ||||
|   }) | ||||
|   window.onload = () => { | ||||
|     updateColors(); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -53,8 +53,8 @@ | ||||
| </script> | ||||
|  | ||||
| <T.Mesh | ||||
|   position.x={node.position.x + 10} | ||||
|   position.z={node.position.y + height / 2} | ||||
|   position.x={node.position[0] + 10} | ||||
|   position.z={node.position[1] + height / 2} | ||||
|   position.y={0.8} | ||||
|   rotation.x={-Math.PI / 2} | ||||
|   bind:ref={meshRef} | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| import { writable, type Writable } from "svelte/store"; | ||||
| import { type Graph, type Node, type Edge, type Socket, type NodeRegistry, type RuntimeExecutor } from "./types"; | ||||
| import { type Graph, type Node, type Edge, type Socket, type NodeRegistry, type RuntimeExecutor, } from "./types"; | ||||
| import { HistoryManager } from "./history-manager"; | ||||
| import * as templates from "./graphs"; | ||||
| import EventEmitter from "./helpers/EventEmitter"; | ||||
| @@ -35,13 +35,13 @@ export class GraphManager extends EventEmitter<{ "save": Graph }> { | ||||
|       } | ||||
|       this.inputSockets.set(s); | ||||
|     }); | ||||
|     this.execute = throttle(() => this._execute(), 100); | ||||
|     this.execute = throttle(() => this._execute(), 50); | ||||
|   } | ||||
|  | ||||
|   serialize(): Graph { | ||||
|     const nodes = Array.from(this._nodes.values()).map(node => ({ | ||||
|       id: node.id, | ||||
|       position: { x: node.position.x, y: node.position.y }, | ||||
|       position: node.position, | ||||
|       type: node.type, | ||||
|       props: node.props, | ||||
|     })); | ||||
| @@ -58,13 +58,18 @@ export class GraphManager extends EventEmitter<{ "save": Graph }> { | ||||
|     console.log(`Execution took ${end - start}ms -> ${result}`); | ||||
|   } | ||||
|  | ||||
|   getNodeTypes() { | ||||
|     return this.nodeRegistry.getAllNodes(); | ||||
|   } | ||||
|  | ||||
|  | ||||
|   private _init(graph: Graph) { | ||||
|     const nodes = new Map(graph.nodes.map(node => { | ||||
|       const nodeType = this.nodeRegistry.getNode(node.type); | ||||
|       if (nodeType) { | ||||
|         node.tmp = node.tmp || {}; | ||||
|         node.tmp.type = nodeType; | ||||
|         node.tmp = { | ||||
|           type: nodeType | ||||
|         }; | ||||
|       } | ||||
|       return [node.id, node] | ||||
|     })); | ||||
| @@ -177,6 +182,29 @@ export class GraphManager extends EventEmitter<{ "save": Graph }> { | ||||
|       nodes.delete(node.id); | ||||
|       return nodes; | ||||
|     }); | ||||
|     this.execute() | ||||
|     this.save(); | ||||
|   } | ||||
|  | ||||
|   private createNodeId() { | ||||
|     return Math.max(...this.getAllNodes().map(n => n.id), 0) + 1; | ||||
|   } | ||||
|  | ||||
|   createNode({ type, position }: { type: string, position: [number, number] }) { | ||||
|  | ||||
|     const nodeType = this.nodeRegistry.getNode(type); | ||||
|     if (!nodeType) { | ||||
|       console.error(`Node type not found: ${type}`); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     const node: Node = { id: this.createNodeId(), type, position, tmp: { type: nodeType } }; | ||||
|  | ||||
|     this.nodes.update((nodes) => { | ||||
|       nodes.set(node.id, node); | ||||
|       return nodes; | ||||
|     }); | ||||
|  | ||||
|     this.save(); | ||||
|   } | ||||
|  | ||||
| @@ -205,6 +233,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph }> { | ||||
|       return [...edges.filter(e => e[2].id !== to.id || e[3] !== toSocket), [from, fromSocket, to, toSocket]]; | ||||
|     }); | ||||
|  | ||||
|     this.execute(); | ||||
|     this.save(); | ||||
|   } | ||||
|  | ||||
| @@ -216,7 +245,13 @@ export class GraphManager extends EventEmitter<{ "save": Graph }> { | ||||
|   getParentsOfNode(node: Node) { | ||||
|     const parents = []; | ||||
|     const stack = node.tmp?.parents?.slice(0); | ||||
|  | ||||
|  | ||||
|     while (stack?.length) { | ||||
|       if (parents.length > 1000000) { | ||||
|         console.log("Infinite loop detected") | ||||
|         break; | ||||
|       } | ||||
|       const parent = stack.pop(); | ||||
|       if (!parent) continue; | ||||
|       parents.push(parent); | ||||
| @@ -287,6 +322,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph }> { | ||||
|     this.edges.update((edges) => { | ||||
|       return edges.filter((e) => e[0].id !== id0 || e[1] !== sid0 || e[2].id !== id2 || e[3] !== sid2); | ||||
|     }); | ||||
|     this.execute(); | ||||
|     this.save(); | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -19,10 +19,7 @@ export function grid(width: number, height: number) { | ||||
|       tmp: { | ||||
|         visible: false, | ||||
|       }, | ||||
|       position: { | ||||
|         x: x * 30, | ||||
|         y: y * 40, | ||||
|       }, | ||||
|       position: [x * 30, y * 40], | ||||
|       props: i == 0 ? { value: 0 } : {}, | ||||
|       type: i == 0 ? "input/float" : "math", | ||||
|     }); | ||||
| @@ -35,10 +32,7 @@ export function grid(width: number, height: number) { | ||||
|     tmp: { | ||||
|       visible: false, | ||||
|     }, | ||||
|     position: { | ||||
|       x: width * 30, | ||||
|       y: (height - 1) * 40, | ||||
|     }, | ||||
|     position: [width * 30, (height - 1) * 40], | ||||
|     type: "output", | ||||
|     props: {}, | ||||
|   }); | ||||
|   | ||||
| @@ -6,12 +6,12 @@ export function tree(depth: number): Graph { | ||||
|     { | ||||
|       id: 0, | ||||
|       type: "output", | ||||
|       position: { x: 0, y: 0 } | ||||
|       position: [0, 0] | ||||
|     }, | ||||
|     { | ||||
|       id: 1, | ||||
|       type: "math", | ||||
|       position: { x: -40, y: -10 } | ||||
|       position: [-40, -10] | ||||
|     } | ||||
|   ] | ||||
|  | ||||
| @@ -34,13 +34,13 @@ export function tree(depth: number): Graph { | ||||
|       nodes.push({ | ||||
|         id: id0, | ||||
|         type: "math", | ||||
|         position: { x, y: y }, | ||||
|         position: [x, y], | ||||
|       }); | ||||
|       edges.push([id0, 0, parent, "a"]); | ||||
|       nodes.push({ | ||||
|         id: id1, | ||||
|         type: "math", | ||||
|         position: { x, y: y + 35 }, | ||||
|         position: [x, y + 35], | ||||
|       }); | ||||
|       edges.push([id1, 0, parent, "b"]); | ||||
|     } | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| // place files you want to import through the `$lib` alias in this folder. | ||||
| @@ -41,5 +41,8 @@ export class MemoryNodeRegistry implements NodeRegistry { | ||||
|   getNode(id: string): NodeType | undefined { | ||||
|     return nodeTypes.find((nodeType) => nodeType.id === id); | ||||
|   } | ||||
|   getAllNodes(): NodeType[] { | ||||
|     return [...nodeTypes]; | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| import type { NodeInput, NodeInputType } from "./inputs"; | ||||
| import type { NodeInput } from "./inputs"; | ||||
| export type { NodeInput } from "./inputs"; | ||||
|  | ||||
| export type Node = { | ||||
| @@ -24,10 +24,7 @@ export type Node = { | ||||
|     title?: string; | ||||
|     lastModified?: string; | ||||
|   }, | ||||
|   position: { | ||||
|     x: number; | ||||
|     y: number; | ||||
|   } | ||||
|   position: [x: number, y: number] | ||||
| } | ||||
|  | ||||
| export type NodeType = { | ||||
| @@ -49,6 +46,7 @@ export type Socket = { | ||||
|  | ||||
| export interface NodeRegistry { | ||||
|   getNode: (id: string) => NodeType | undefined; | ||||
|   getAllNodes: () => NodeType[]; | ||||
| } | ||||
|  | ||||
| export interface RuntimeExecutor { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user