feat: node store interface
This commit is contained in:
parent
1d203c687c
commit
78c88e4d66
@ -36,7 +36,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
|
|||||||
|
|
||||||
history: HistoryManager = new HistoryManager();
|
history: HistoryManager = new HistoryManager();
|
||||||
|
|
||||||
constructor(private nodeRegistry: NodeRegistry) {
|
constructor(public registry: NodeRegistry) {
|
||||||
super();
|
super();
|
||||||
this.nodes.subscribe((nodes) => {
|
this.nodes.subscribe((nodes) => {
|
||||||
this._nodes = nodes;
|
this._nodes = nodes;
|
||||||
@ -82,7 +82,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
|
|||||||
}
|
}
|
||||||
|
|
||||||
getNodeTypes() {
|
getNodeTypes() {
|
||||||
return this.nodeRegistry.getAllNodes();
|
return this.registry.getAllNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
getLinkedNodes(node: Node) {
|
getLinkedNodes(node: Node) {
|
||||||
@ -122,7 +122,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
|
|||||||
|
|
||||||
private _init(graph: Graph) {
|
private _init(graph: Graph) {
|
||||||
const nodes = new Map(graph.nodes.map(node => {
|
const nodes = new Map(graph.nodes.map(node => {
|
||||||
const nodeType = this.nodeRegistry.getNode(node.type);
|
const nodeType = this.registry.getNode(node.type);
|
||||||
if (nodeType) {
|
if (nodeType) {
|
||||||
node.tmp = {
|
node.tmp = {
|
||||||
random: (Math.random() - 0.5) * 2,
|
random: (Math.random() - 0.5) * 2,
|
||||||
@ -164,10 +164,10 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
|
|||||||
this.id.set(graph.id);
|
this.id.set(graph.id);
|
||||||
|
|
||||||
const nodeIds = Array.from(new Set([...graph.nodes.map(n => n.type)]));
|
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) {
|
for (const node of this.graph.nodes) {
|
||||||
const nodeType = this.nodeRegistry.getNode(node.type);
|
const nodeType = this.registry.getNode(node.type);
|
||||||
if (!nodeType) {
|
if (!nodeType) {
|
||||||
logger.error(`Node type not found: ${node.type}`);
|
logger.error(`Node type not found: ${node.type}`);
|
||||||
this.status.set("error");
|
this.status.set("error");
|
||||||
@ -222,7 +222,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
|
|||||||
}
|
}
|
||||||
|
|
||||||
getNodeType(id: string) {
|
getNodeType(id: string) {
|
||||||
return this.nodeRegistry.getNode(id);
|
return this.registry.getNode(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildrenOfNode(node: Node) {
|
getChildrenOfNode(node: Node) {
|
||||||
@ -303,7 +303,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
|
|||||||
nodes = nodes.map((node, i) => {
|
nodes = nodes.map((node, i) => {
|
||||||
const id = startId + i;
|
const id = startId + i;
|
||||||
idMap.set(node.id, id);
|
idMap.set(node.id, id);
|
||||||
const type = this.nodeRegistry.getNode(node.type);
|
const type = this.registry.getNode(node.type);
|
||||||
if (!type) {
|
if (!type) {
|
||||||
throw new Error(`Node type not found: ${node.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"] }) {
|
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) {
|
if (!nodeType) {
|
||||||
logger.error(`Node type not found: ${type}`);
|
logger.error(`Node type not found: ${type}`);
|
||||||
return;
|
return;
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
import { createKeyMap } from "../../helpers/createKeyMap";
|
import { createKeyMap } from "../../helpers/createKeyMap";
|
||||||
import BoxSelection from "../BoxSelection.svelte";
|
import BoxSelection from "../BoxSelection.svelte";
|
||||||
import AddMenu from "../AddMenu.svelte";
|
import AddMenu from "../AddMenu.svelte";
|
||||||
|
import { get } from "svelte/store";
|
||||||
|
|
||||||
export let graph: GraphManager;
|
export let graph: GraphManager;
|
||||||
|
|
||||||
@ -78,7 +79,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateNodePosition(node: NodeType) {
|
function updateNodePosition(node: NodeType) {
|
||||||
if (node?.tmp?.ref) {
|
if (node?.tmp?.ref && node?.tmp?.mesh) {
|
||||||
if (node.tmp["x"] !== undefined && node.tmp["y"] !== undefined) {
|
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("--nx", `${node.tmp.x * 10}px`);
|
||||||
node.tmp.ref.style.setProperty("--ny", `${node.tmp.y * 10}px`);
|
node.tmp.ref.style.setProperty("--ny", `${node.tmp.y * 10}px`);
|
||||||
@ -758,6 +759,34 @@
|
|||||||
addMenuPosition = null;
|
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(() => {
|
onMount(() => {
|
||||||
if (localStorage.getItem("cameraPosition")) {
|
if (localStorage.getItem("cameraPosition")) {
|
||||||
const cPosition = JSON.parse(localStorage.getItem("cameraPosition")!);
|
const cPosition = JSON.parse(localStorage.getItem("cameraPosition")!);
|
||||||
@ -779,6 +808,8 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
bind:clientWidth={width}
|
bind:clientWidth={width}
|
||||||
bind:clientHeight={height}
|
bind:clientHeight={height}
|
||||||
|
on:dragover={handlerDragOver}
|
||||||
|
on:drop={handleDrop}
|
||||||
on:keydown={keymap.handleKeyboardEvent}
|
on:keydown={keymap.handleKeyboardEvent}
|
||||||
on:mousedown={handleMouseDown}
|
on:mousedown={handleMouseDown}
|
||||||
>
|
>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
export let graph: Graph;
|
export let graph: Graph;
|
||||||
export let settings: Writable<Record<string, any>> | undefined;
|
export let settings: Writable<Record<string, any>> | undefined;
|
||||||
|
|
||||||
const manager = new GraphManager(registry);
|
export const manager = new GraphManager(registry);
|
||||||
|
|
||||||
export const status = manager.status;
|
export const status = manager.status;
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
import { Color, type Mesh } from "three";
|
import { Color, type Mesh } from "three";
|
||||||
import NodeFrag from "./Node.frag";
|
import NodeFrag from "./Node.frag";
|
||||||
import NodeVert from "./Node.vert";
|
import NodeVert from "./Node.vert";
|
||||||
|
import NodeHtml from "./NodeHTML.svelte";
|
||||||
|
|
||||||
export let node: Node;
|
export let node: Node;
|
||||||
export let inView = true;
|
export let inView = true;
|
||||||
@ -22,32 +23,20 @@
|
|||||||
|
|
||||||
const getNodeHeight = getContext<(n: string) => number>("getNodeHeight");
|
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;
|
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 = node.tmp || {};
|
||||||
node.tmp.ref = ref;
|
|
||||||
node.tmp.mesh = meshRef;
|
node.tmp.mesh = meshRef;
|
||||||
updateNodePosition(node);
|
updateNodePosition?.(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
node.tmp = node.tmp || {};
|
node.tmp = node.tmp || {};
|
||||||
node.tmp.ref = ref;
|
|
||||||
node.tmp.mesh = meshRef;
|
node.tmp.mesh = meshRef;
|
||||||
updateNodePosition(node);
|
updateNodePosition?.(node);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -83,51 +72,4 @@
|
|||||||
/>
|
/>
|
||||||
</T.Mesh>
|
</T.Mesh>
|
||||||
|
|
||||||
<div
|
<NodeHtml {node} {inView} {isActive} {isSelected} {z} />
|
||||||
class="node"
|
|
||||||
class:active={isActive}
|
|
||||||
style:--cz={z + zOffset}
|
|
||||||
style:display={inView && z > zLimit ? "block" : "none"}
|
|
||||||
class:selected={isSelected}
|
|
||||||
class:out-of-view={!inView}
|
|
||||||
data-node-id={node.id}
|
|
||||||
bind:this={ref}
|
|
||||||
>
|
|
||||||
<NodeHeader {node} />
|
|
||||||
|
|
||||||
{#each parameters as [key, value], i}
|
|
||||||
<NodeParameter
|
|
||||||
bind:node
|
|
||||||
id={key}
|
|
||||||
input={value}
|
|
||||||
isLast={i == parameters.length - 1}
|
|
||||||
/>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.node {
|
|
||||||
position: absolute;
|
|
||||||
box-sizing: border-box;
|
|
||||||
user-select: none !important;
|
|
||||||
cursor: pointer;
|
|
||||||
width: 200px;
|
|
||||||
color: var(--text-color);
|
|
||||||
transform: translate3d(var(--nx), var(--ny), 0);
|
|
||||||
z-index: 1;
|
|
||||||
opacity: calc((var(--cz) - 2.5) / 3.5);
|
|
||||||
font-weight: 300;
|
|
||||||
--stroke: var(--outline);
|
|
||||||
--stroke-width: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node.active {
|
|
||||||
--stroke: var(--active);
|
|
||||||
--stroke-width: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.node.selected {
|
|
||||||
--stroke: var(--selected);
|
|
||||||
--stroke-width: 2px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
86
app/src/lib/graph-interface/node/NodeHTML.svelte
Normal file
86
app/src/lib/graph-interface/node/NodeHTML.svelte
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { Node } from "@nodes/types";
|
||||||
|
import NodeHeader from "./NodeHeader.svelte";
|
||||||
|
import NodeParameter from "./NodeParameter.svelte";
|
||||||
|
import { getContext, onMount } from "svelte";
|
||||||
|
export let isActive = false;
|
||||||
|
export let isSelected = false;
|
||||||
|
export let inView = true;
|
||||||
|
export let z = 2;
|
||||||
|
|
||||||
|
let ref: HTMLDivElement;
|
||||||
|
export let node: Node;
|
||||||
|
export let position = "absolute";
|
||||||
|
|
||||||
|
const zOffset = (node.tmp?.random || 0) * 0.5;
|
||||||
|
const zLimit = 2 - zOffset;
|
||||||
|
|
||||||
|
const type = node?.tmp?.type;
|
||||||
|
|
||||||
|
const parameters = Object.entries(type?.inputs || {})
|
||||||
|
.filter((p) => p[1].type !== "seed")
|
||||||
|
.filter((p) => !("setting" in p[1]));
|
||||||
|
|
||||||
|
const updateNodePosition =
|
||||||
|
getContext<(n: Node) => void>("updateNodePosition");
|
||||||
|
|
||||||
|
$: if (node && ref) {
|
||||||
|
node.tmp = node.tmp || {};
|
||||||
|
node.tmp.ref = ref;
|
||||||
|
updateNodePosition?.(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
onMount(() => {
|
||||||
|
node.tmp = node.tmp || {};
|
||||||
|
node.tmp.ref = ref;
|
||||||
|
updateNodePosition?.(node);
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="node {position}"
|
||||||
|
class:active={isActive}
|
||||||
|
style:--cz={z + zOffset}
|
||||||
|
style:display={inView && z > zLimit ? "block" : "none"}
|
||||||
|
class:selected={isSelected}
|
||||||
|
class:out-of-view={!inView}
|
||||||
|
data-node-id={node.id}
|
||||||
|
bind:this={ref}
|
||||||
|
>
|
||||||
|
<NodeHeader {node} />
|
||||||
|
|
||||||
|
{#each parameters as [key, value], i}
|
||||||
|
<NodeParameter
|
||||||
|
bind:node
|
||||||
|
id={key}
|
||||||
|
input={value}
|
||||||
|
isLast={i == parameters.length - 1}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.node {
|
||||||
|
box-sizing: border-box;
|
||||||
|
user-select: none !important;
|
||||||
|
cursor: pointer;
|
||||||
|
width: 200px;
|
||||||
|
color: var(--text-color);
|
||||||
|
transform: translate3d(var(--nx), var(--ny), 0);
|
||||||
|
z-index: 1;
|
||||||
|
opacity: calc((var(--cz) - 2.5) / 3.5);
|
||||||
|
font-weight: 300;
|
||||||
|
--stroke: var(--outline);
|
||||||
|
--stroke-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node.active {
|
||||||
|
--stroke: var(--active);
|
||||||
|
--stroke-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node.selected {
|
||||||
|
--stroke: var(--selected);
|
||||||
|
--stroke-width: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
@ -14,10 +14,10 @@
|
|||||||
function handleMouseDown(event: MouseEvent) {
|
function handleMouseDown(event: MouseEvent) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
setDownSocket({
|
setDownSocket?.({
|
||||||
node,
|
node,
|
||||||
index: 0,
|
index: 0,
|
||||||
position: getSocketPosition(node, 0),
|
position: getSocketPosition?.(node, 0),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,9 +15,11 @@
|
|||||||
|
|
||||||
$: if (node?.props?.[id] !== value) {
|
$: if (node?.props?.[id] !== value) {
|
||||||
node.props = { ...node.props, [id]: value };
|
node.props = { ...node.props, [id]: value };
|
||||||
|
if (graph) {
|
||||||
graph.save();
|
graph.save();
|
||||||
graph.execute();
|
graph.execute();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Input id="input-{elementId}" {input} bind:value />
|
<Input id="input-{elementId}" {input} bind:value />
|
||||||
|
@ -20,8 +20,8 @@
|
|||||||
const socketId = `${node.id}-${id}`;
|
const socketId = `${node.id}-${id}`;
|
||||||
|
|
||||||
const graph = getGraphManager();
|
const graph = getGraphManager();
|
||||||
const graphId = graph.id;
|
const graphId = graph?.id;
|
||||||
const inputSockets = graph.inputSockets;
|
const inputSockets = graph?.inputSockets;
|
||||||
|
|
||||||
const elementId = `input-${Math.random().toString(36).substring(7)}`;
|
const elementId = `input-${Math.random().toString(36).substring(7)}`;
|
||||||
|
|
||||||
@ -34,10 +34,10 @@
|
|||||||
function handleMouseDown(ev: MouseEvent) {
|
function handleMouseDown(ev: MouseEvent) {
|
||||||
ev.preventDefault();
|
ev.preventDefault();
|
||||||
ev.stopPropagation();
|
ev.stopPropagation();
|
||||||
setDownSocket({
|
setDownSocket?.({
|
||||||
node,
|
node,
|
||||||
index: id,
|
index: id,
|
||||||
position: getSocketPosition(node, id),
|
position: getSocketPosition?.(node, id),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +76,7 @@
|
|||||||
class:disabled={$possibleSocketIds && !$possibleSocketIds.has(socketId)}
|
class:disabled={$possibleSocketIds && !$possibleSocketIds.has(socketId)}
|
||||||
>
|
>
|
||||||
{#key id && graphId}
|
{#key id && graphId}
|
||||||
<div class="content" class:disabled={$inputSockets.has(socketId)}>
|
<div class="content" class:disabled={$inputSockets?.has(socketId)}>
|
||||||
{#if inputType.label !== false}
|
{#if inputType.label !== false}
|
||||||
<label for={elementId}>{input.label || id}</label>
|
<label for={elementId}>{input.label || id}</label>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { get, writable } from "svelte/store";
|
import { derived, get, writable } from "svelte/store";
|
||||||
|
|
||||||
type Shortcut = {
|
type Shortcut = {
|
||||||
key: string | string[],
|
key: string | string[],
|
||||||
@ -9,35 +9,45 @@ type Shortcut = {
|
|||||||
callback: (event: KeyboardEvent) => void
|
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[]) {
|
export function createKeyMap(keys: Shortcut[]) {
|
||||||
|
|
||||||
const store = writable(keys);
|
const store = writable(new Map(keys.map(k => [getShortcutId(k), k])));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
handleKeyboardEvent: (event: KeyboardEvent) => {
|
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 (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 ("shift" in k && k.shift !== event.shiftKey) return false;
|
||||||
if ("ctrl" in k && k.ctrl !== event.ctrlKey) return false;
|
if ("ctrl" in k && k.ctrl !== event.ctrlKey) return false;
|
||||||
if ("alt" in k && k.alt !== event.altKey) return false;
|
if ("alt" in k && k.alt !== event.altKey) return false;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
console.log({ keys: get(store), out: key, key: event.key });
|
|
||||||
key?.callback(event);
|
key?.callback(event);
|
||||||
},
|
},
|
||||||
addShortcut: (shortcut: Shortcut) => {
|
addShortcut: (shortcut: Shortcut) => {
|
||||||
if (Array.isArray(shortcut.key)) {
|
if (Array.isArray(shortcut.key)) {
|
||||||
for (const k of shortcut.key) {
|
for (const k of shortcut.key) {
|
||||||
store.update(keys => {
|
store.update(shortcuts => {
|
||||||
if (keys.find(kk => kk.key === k)) return keys;
|
const id = getShortcutId({ ...shortcut, key: k });
|
||||||
return [...keys, { ...shortcut, key: k }];
|
shortcuts.delete(id);
|
||||||
|
shortcuts.set(id, { ...shortcut, key: k });
|
||||||
|
return shortcuts;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} 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()))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
93
app/src/lib/node-registry-client.ts
Normal file
93
app/src/lib/node-registry-client.ts
Normal file
@ -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<string, NodeType> = 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()];
|
||||||
|
}
|
||||||
|
}
|
@ -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[] = [
|
try {
|
||||||
{
|
await fs.access(filePath);
|
||||||
id: "max/plantarium/float",
|
} catch (e) {
|
||||||
inputs: {
|
return null
|
||||||
"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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
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<string, NodeType> = 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 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 wrapper = createWasmWrapper();
|
||||||
const module = new WebAssembly.Module(await wasmResponse.arrayBuffer());
|
const module = new WebAssembly.Module(wasmBytes);
|
||||||
const instance = new WebAssembly.Instance(module, { ["./index_bg.js"]: wrapper });
|
const instance = new WebAssembly.Instance(module, { ["./index_bg.js"]: wrapper });
|
||||||
wrapper.setInstance(instance);
|
wrapper.setInstance(instance)
|
||||||
|
|
||||||
const node = await response.json();
|
return wrapper;
|
||||||
node.execute = wrapper.execute;
|
|
||||||
return node;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async load(nodeIds: string[]) {
|
|
||||||
const a = performance.now();
|
|
||||||
|
|
||||||
nodeIds.push("max/plantarium/random");
|
export async function getNode(id: `${string}/${string}/${string}`) {
|
||||||
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)));
|
const wrapper = await getNodeWasm(id);
|
||||||
|
|
||||||
for (const node of nodes) {
|
const definition = wrapper?.get_definition?.();
|
||||||
this.nodes.set(node.id, node);
|
|
||||||
|
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 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const duration = performance.now() - a;
|
|
||||||
|
|
||||||
log.log("loaded nodes in", duration, "ms");
|
|
||||||
this.status = "ready";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getNode(id: string) {
|
export async function getCollectionNodes(userId: `${string}/${string}`) {
|
||||||
return this.nodes.get(id);
|
const nodes = await fs.readdir(path.resolve(`../nodes/${userId}`));
|
||||||
|
return nodes
|
||||||
|
.filter(n => n !== "pkg" && n !== ".template")
|
||||||
|
.map(n => {
|
||||||
|
return {
|
||||||
|
id: `${userId}/${n}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllNodes() {
|
export async function getCollection(userId: `${string}/${string}`) {
|
||||||
return [...this.nodes.values()];
|
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}`));
|
||||||
status: "loading" | "ready" | "error" = "ready";
|
return Promise.all(collections.map(async n => {
|
||||||
|
const nodes = await getCollectionNodes(`${userId}/${n}`);
|
||||||
async load(nodeIds: string[]) {
|
return {
|
||||||
// Do nothing
|
id: `${userId}/${n}`,
|
||||||
|
nodes,
|
||||||
|
}
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
getNode(id: string) {
|
export async function getUser(userId: string) {
|
||||||
return nodeTypes.find((nodeType) => nodeType.id === id);
|
const collections = await getUserCollections(userId);
|
||||||
}
|
return {
|
||||||
getAllNodes() {
|
id: userId,
|
||||||
return [...nodeTypes];
|
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;
|
||||||
|
}
|
||||||
|
81
app/src/lib/node-store/BreadCrumbs.svelte
Normal file
81
app/src/lib/node-store/BreadCrumbs.svelte
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { Writable } from "svelte/store";
|
||||||
|
|
||||||
|
export let activeId: Writable<string>;
|
||||||
|
$: [activeUser, activeCollection, activeNode] = $activeId.split(`/`);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="breadcrumbs">
|
||||||
|
{#if activeUser}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
$activeId = "";
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
root
|
||||||
|
</button>
|
||||||
|
{#if activeCollection}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
$activeId = activeUser;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{activeUser}
|
||||||
|
</button>
|
||||||
|
{#if activeNode}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
$activeId = `${activeUser}/${activeCollection}`;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{activeCollection}
|
||||||
|
</button>
|
||||||
|
<span>{activeNode}</span>
|
||||||
|
{:else}
|
||||||
|
<span>{activeCollection}</span>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<span>{activeUser}</span>
|
||||||
|
{/if}
|
||||||
|
{:else}
|
||||||
|
<span>root</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.breadcrumbs {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.4em;
|
||||||
|
gap: 0.8em;
|
||||||
|
height: 1em;
|
||||||
|
border-bottom: solid thin var(--outline);
|
||||||
|
}
|
||||||
|
.breadcrumbs > button {
|
||||||
|
position: relative;
|
||||||
|
background: none;
|
||||||
|
font-family: var(--font-family);
|
||||||
|
border: none;
|
||||||
|
font-size: 1em;
|
||||||
|
padding: 0px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumbs > button::after {
|
||||||
|
content: "/";
|
||||||
|
position: absolute;
|
||||||
|
right: -11px;
|
||||||
|
opacity: 0.5;
|
||||||
|
white-space: pre;
|
||||||
|
text-decoration: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumbs > button:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumbs > span {
|
||||||
|
font-size: 1em;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
</style>
|
74
app/src/lib/node-store/DraggableNode.svelte
Normal file
74
app/src/lib/node-store/DraggableNode.svelte
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import NodeHtml from "$lib/graph-interface/node/NodeHTML.svelte";
|
||||||
|
import type { NodeType } from "@nodes/types";
|
||||||
|
|
||||||
|
export let node: NodeType;
|
||||||
|
|
||||||
|
let dragging = false;
|
||||||
|
|
||||||
|
function handleDragStart(e: DragEvent) {
|
||||||
|
dragging = true;
|
||||||
|
const box = (e?.target as HTMLElement)?.getBoundingClientRect();
|
||||||
|
if (e.dataTransfer === null) return;
|
||||||
|
e.dataTransfer.effectAllowed = "move";
|
||||||
|
e.dataTransfer.setData("data/node-id", node.id);
|
||||||
|
e.dataTransfer.setData(
|
||||||
|
"data/node-offset-x",
|
||||||
|
Math.round(box.left - e.clientX).toString(),
|
||||||
|
);
|
||||||
|
e.dataTransfer.setData(
|
||||||
|
"data/node-offset-y",
|
||||||
|
Math.round(box.top - e.clientY).toString(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="node-wrapper" class:dragging>
|
||||||
|
<div
|
||||||
|
on:dragend={() => {
|
||||||
|
dragging = false;
|
||||||
|
}}
|
||||||
|
draggable={true}
|
||||||
|
role="button"
|
||||||
|
tabindex="0"
|
||||||
|
on:dragstart={handleDragStart}
|
||||||
|
>
|
||||||
|
<NodeHtml
|
||||||
|
inView={true}
|
||||||
|
position={"relative"}
|
||||||
|
z={5}
|
||||||
|
node={{
|
||||||
|
id: 0,
|
||||||
|
type: node.id,
|
||||||
|
position: [0, 0],
|
||||||
|
tmp: {
|
||||||
|
type: node,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.node-wrapper {
|
||||||
|
width: fit-content;
|
||||||
|
border-radius: 5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
border: solid 2px transparent;
|
||||||
|
padding: 5px;
|
||||||
|
margin-left: -5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dragging {
|
||||||
|
border: dashed 2px var(--outline);
|
||||||
|
}
|
||||||
|
.node-wrapper > div {
|
||||||
|
opacity: 1;
|
||||||
|
display: block;
|
||||||
|
pointer-events: all;
|
||||||
|
transition: opacity 0.2s;
|
||||||
|
}
|
||||||
|
.dragging > div {
|
||||||
|
opacity: 0.2;
|
||||||
|
}
|
||||||
|
</style>
|
92
app/src/lib/node-store/NodeStore.svelte
Normal file
92
app/src/lib/node-store/NodeStore.svelte
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import type { GraphManager } from "$lib/graph-interface/graph-manager";
|
||||||
|
import Node from "$lib/graph-interface/node/Node.svelte";
|
||||||
|
import localStore from "$lib/helpers/localStore";
|
||||||
|
import type { RemoteNodeRegistry } from "$lib/node-registry-client";
|
||||||
|
import { Canvas } from "@threlte/core";
|
||||||
|
import BreadCrumbs from "./BreadCrumbs.svelte";
|
||||||
|
import NodeHtml from "$lib/graph-interface/node/NodeHTML.svelte";
|
||||||
|
import DraggableNode from "./DraggableNode.svelte";
|
||||||
|
|
||||||
|
export let nodeRegistry: RemoteNodeRegistry;
|
||||||
|
export let manager: GraphManager;
|
||||||
|
|
||||||
|
function handleImport() {
|
||||||
|
nodeRegistry.load([$activeId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeId = localStore<
|
||||||
|
`${string}` | `${string}/${string}` | `${string}/${string}/${string}`
|
||||||
|
>("nodes.store.activeId", "");
|
||||||
|
|
||||||
|
$: [activeUser, activeCollection, activeNode] = $activeId.split(`/`);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<BreadCrumbs {activeId} />
|
||||||
|
|
||||||
|
<div class="wrapper">
|
||||||
|
{#if !activeUser}
|
||||||
|
<h3>Users</h3>
|
||||||
|
{#await nodeRegistry.fetchUsers()}
|
||||||
|
<div>Loading...</div>
|
||||||
|
{:then users}
|
||||||
|
{#each users as user}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
$activeId = user.id;
|
||||||
|
}}>{user.id}</button
|
||||||
|
>
|
||||||
|
{/each}
|
||||||
|
{:catch error}
|
||||||
|
<div>{error.message}</div>
|
||||||
|
{/await}
|
||||||
|
{:else if !activeCollection}
|
||||||
|
{#await nodeRegistry.fetchUser(activeUser)}
|
||||||
|
<div>Loading...</div>
|
||||||
|
{:then user}
|
||||||
|
<h3>Collections</h3>
|
||||||
|
{#each user.collections as collection}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
$activeId = collection.id;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{collection.id.split(`/`)[1]}
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
{:catch error}
|
||||||
|
<div>{error.message}</div>
|
||||||
|
{/await}
|
||||||
|
{:else if !activeNode}
|
||||||
|
<h3>Nodes</h3>
|
||||||
|
{#await nodeRegistry.fetchCollection(`${activeUser}/${activeCollection}`)}
|
||||||
|
<div>Loading...</div>
|
||||||
|
{:then collection}
|
||||||
|
{#each collection.nodes as node}
|
||||||
|
<button
|
||||||
|
on:click={() => {
|
||||||
|
$activeId = node.id;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{node.id.split(`/`)[2]}
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
{:catch error}
|
||||||
|
<div>{error.message}</div>
|
||||||
|
{/await}
|
||||||
|
{:else}
|
||||||
|
{#await nodeRegistry.fetchNodeDefinition(`${activeUser}/${activeCollection}/${activeNode}`)}
|
||||||
|
<div>Loading...</div>
|
||||||
|
{:then node}
|
||||||
|
<DraggableNode {node} />
|
||||||
|
{:catch error}
|
||||||
|
<div>{error.message}</div>
|
||||||
|
{/await}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.wrapper {
|
||||||
|
padding: 1em;
|
||||||
|
}
|
||||||
|
</style>
|
31
app/src/lib/node-store/Spinner.svelte
Normal file
31
app/src/lib/node-store/Spinner.svelte
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<span class="spinner"></span>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@keyframes spin {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.spinner::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
bottom: 10px;
|
||||||
|
right: 10px;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
pointer-events: none;
|
||||||
|
animation: spin 1s linear infinite;
|
||||||
|
background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round' class='icon icon-tabler icons-tabler-outline icon-tabler-loader-2'%3E%3Cpath stroke='none' d='M0 0h24v24H0z' fill='none'/%3E%3Cpath d='M12 3a9 9 0 1 0 9 9' /%3E%3C/svg%3E");
|
||||||
|
background-size: cover;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
</style>
|
@ -14,15 +14,15 @@
|
|||||||
<div class="command-wrapper">
|
<div class="command-wrapper">
|
||||||
<div class="command">
|
<div class="command">
|
||||||
{#if key.ctrl}
|
{#if key.ctrl}
|
||||||
<b>Ctrl</b>
|
<span>Ctrl + </span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if key.shift}
|
{#if key.shift}
|
||||||
<b>Shift</b>
|
<span>Shift</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if key.alt}
|
{#if key.alt}
|
||||||
<b>Alt</b>
|
<span>Alt</span>
|
||||||
{/if}
|
{/if}
|
||||||
<b>{key.key}</b>
|
<span>{key.key}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p>{key.description}</p>
|
<p>{key.description}</p>
|
||||||
@ -51,21 +51,18 @@
|
|||||||
|
|
||||||
.command-wrapper {
|
.command-wrapper {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: left;
|
justify-content: right;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.command {
|
.command {
|
||||||
background: var(--layer-3);
|
background: var(--outline);
|
||||||
padding: 0.4em;
|
padding: 0.4em;
|
||||||
|
font-size: 0.8em;
|
||||||
border-radius: 0.3em;
|
border-radius: 0.3em;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.command > * {
|
|
||||||
color: var(--layer-0);
|
|
||||||
}
|
|
||||||
|
|
||||||
p {
|
p {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -113,7 +113,7 @@
|
|||||||
transform 0.2s,
|
transform 0.2s,
|
||||||
background 0.2s ease;
|
background 0.2s ease;
|
||||||
width: 30%;
|
width: 30%;
|
||||||
min-width: 300px;
|
min-width: 350px;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1 {
|
h1 {
|
||||||
|
1
app/src/routes/+layout.server.ts
Normal file
1
app/src/routes/+layout.server.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export const prerender = true;
|
@ -5,7 +5,3 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<slot />
|
<slot />
|
||||||
|
|
||||||
{#if false}
|
|
||||||
<span class="absolute i-tabler-settings w-6 h-6 block"></span>
|
|
||||||
{/if}
|
|
||||||
|
@ -2,17 +2,20 @@
|
|||||||
import Grid from "$lib/grid";
|
import Grid from "$lib/grid";
|
||||||
import GraphInterface from "$lib/graph-interface";
|
import GraphInterface from "$lib/graph-interface";
|
||||||
import { MemoryRuntimeExecutor } from "$lib/runtime-executor";
|
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 * as templates from "$lib/graph-templates";
|
||||||
import type { Graph } from "@nodes/types";
|
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 Settings from "$lib/settings/Settings.svelte";
|
||||||
import { AppSettings, AppSettingTypes } from "$lib/settings/app-settings";
|
import { AppSettings, AppSettingTypes } from "$lib/settings/app-settings";
|
||||||
import { get, writable, type Writable } from "svelte/store";
|
import { get, writable, type Writable } from "svelte/store";
|
||||||
import Keymap from "$lib/settings/Keymap.svelte";
|
import Keymap from "$lib/settings/Keymap.svelte";
|
||||||
import type { createKeyMap } from "$lib/helpers/createKeyMap";
|
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);
|
const runtimeExecutor = new MemoryRuntimeExecutor(nodeRegistry);
|
||||||
|
|
||||||
let res: Int32Array;
|
let res: Int32Array;
|
||||||
@ -21,7 +24,11 @@
|
|||||||
? JSON.parse(localStorage.getItem("graph")!)
|
? JSON.parse(localStorage.getItem("graph")!)
|
||||||
: templates.grid(3, 3);
|
: templates.grid(3, 3);
|
||||||
|
|
||||||
|
let manager: GraphManager;
|
||||||
let managerStatus: Writable<"loading" | "error" | "idle">;
|
let managerStatus: Writable<"loading" | "error" | "idle">;
|
||||||
|
$: if (manager) {
|
||||||
|
setContext("graphManager", manager);
|
||||||
|
}
|
||||||
|
|
||||||
let keymap: ReturnType<typeof createKeyMap>;
|
let keymap: ReturnType<typeof createKeyMap>;
|
||||||
|
|
||||||
@ -41,6 +48,7 @@
|
|||||||
definition: AppSettingTypes,
|
definition: AppSettingTypes,
|
||||||
},
|
},
|
||||||
shortcuts: {},
|
shortcuts: {},
|
||||||
|
nodeStore: {},
|
||||||
graph: {},
|
graph: {},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -53,7 +61,16 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
settings = settings;
|
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(
|
function handleSettings(
|
||||||
@ -91,10 +108,10 @@
|
|||||||
<Grid.Cell>
|
<Grid.Cell>
|
||||||
{#key graph}
|
{#key graph}
|
||||||
<GraphInterface
|
<GraphInterface
|
||||||
|
bind:manager
|
||||||
registry={nodeRegistry}
|
registry={nodeRegistry}
|
||||||
{graph}
|
{graph}
|
||||||
bind:keymap
|
bind:keymap
|
||||||
bind:status={managerStatus}
|
|
||||||
settings={settings?.graph?.settings}
|
settings={settings?.graph?.settings}
|
||||||
on:settings={handleSettings}
|
on:settings={handleSettings}
|
||||||
on:result={handleResult}
|
on:result={handleResult}
|
||||||
|
22
app/src/routes/nodes/[user].json/+server.ts
Normal file
22
app/src/routes/nodes/[user].json/+server.ts
Normal file
@ -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: user.id }
|
||||||
|
}).flat(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async function GET({ params }) {
|
||||||
|
|
||||||
|
const namespaces = await registry.getUser(params.user)
|
||||||
|
|
||||||
|
return json(namespaces);
|
||||||
|
|
||||||
|
}
|
22
app/src/routes/nodes/[user]/[collection].json/+server.ts
Normal file
22
app/src/routes/nodes/[user]/[collection].json/+server.ts
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -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 });
|
||||||
|
}
|
||||||
|
}
|
@ -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" } });
|
||||||
|
}
|
14
app/src/routes/nodes/users.json/+server.ts
Normal file
14
app/src/routes/nodes/users.json/+server.ts
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
@ -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
|
|
@ -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'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
10
packages/node-registry/.gitignore
vendored
10
packages/node-registry/.gitignore
vendored
@ -1,10 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
node_modules
|
|
||||||
/build
|
|
||||||
/.svelte-kit
|
|
||||||
/package
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
vite.config.js.timestamp-*
|
|
||||||
vite.config.ts.timestamp-*
|
|
@ -1 +0,0 @@
|
|||||||
engine-strict=true
|
|
@ -1,4 +0,0 @@
|
|||||||
# Ignore files for PNPM, NPM and YARN
|
|
||||||
pnpm-lock.yaml
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"useTabs": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"printWidth": 100,
|
|
||||||
"plugins": ["prettier-plugin-svelte"],
|
|
||||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
|
||||||
}
|
|
@ -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.
|
|
@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
13
packages/node-registry/src/app.d.ts
vendored
13
packages/node-registry/src/app.d.ts
vendored
@ -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 {};
|
|
@ -1,12 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
%sveltekit.head%
|
|
||||||
</head>
|
|
||||||
<body data-sveltekit-preload-data="hover">
|
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
@ -1 +0,0 @@
|
|||||||
// place files you want to import through the `$lib` alias in this folder.
|
|
@ -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 });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,2 +0,0 @@
|
|||||||
<h1>Welcome to SvelteKit</h1>
|
|
||||||
<p>Visit <a href="https://kit.svelte.dev">kit.svelte.dev</a> to read the documentation</p>
|
|
@ -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 });
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { PageData } from './$types';
|
|
||||||
|
|
||||||
import { getNode, getNodeWasm } from '$lib/registry';
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
|
|
||||||
export let data: PageData;
|
|
||||||
const nodeId = `${data.params.user}/${data.params.collection}/${data.params.node}` as const;
|
|
||||||
|
|
||||||
let node;
|
|
||||||
let wasm;
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
wasm = await getNodeWasm(nodeId);
|
|
||||||
window['wasm'] = wasm;
|
|
||||||
node = await getNode(nodeId);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<h1>{data.params.user}/{data.params.collection}/{data.params.node}</h1>
|
|
||||||
|
|
||||||
<h3>Node Definition</h3>
|
|
||||||
{#if !node}
|
|
||||||
<p>Loading Node</p>
|
|
||||||
{:else}
|
|
||||||
<pre>{JSON.stringify(node, null, 2)}</pre>
|
|
||||||
{/if}
|
|
@ -1,7 +0,0 @@
|
|||||||
import type { PageLoad } from "./$types";
|
|
||||||
|
|
||||||
export const load: PageLoad = ({ params }) => {
|
|
||||||
return {
|
|
||||||
params
|
|
||||||
}
|
|
||||||
};
|
|
@ -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" } });
|
|
||||||
}
|
|
Binary file not shown.
Before Width: | Height: | Size: 5.3 KiB |
Binary file not shown.
Before Width: | Height: | Size: 1.5 KiB |
@ -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;
|
|
@ -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
|
|
||||||
}
|
|
@ -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}']
|
|
||||||
}
|
|
||||||
});
|
|
@ -35,7 +35,7 @@ export type NodeType = {
|
|||||||
meta?: {
|
meta?: {
|
||||||
title?: string;
|
title?: string;
|
||||||
},
|
},
|
||||||
execute?: (args: number[]) => unknown;
|
execute?: (args: Int32Array) => Int32Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Socket = {
|
export type Socket = {
|
||||||
@ -44,7 +44,6 @@ export type Socket = {
|
|||||||
position: [number, number];
|
position: [number, number];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export interface NodeRegistry {
|
export interface NodeRegistry {
|
||||||
/**
|
/**
|
||||||
* The status of the node registry
|
* The status of the node registry
|
||||||
|
Loading…
Reference in New Issue
Block a user