chore: move some more components to svelte 5
Some checks failed
Deploy to GitHub Pages / build_site (push) Has been cancelled
Some checks failed
Deploy to GitHub Pages / build_site (push) Has been cancelled
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { GraphManager } from "./graph-manager.js";
|
||||
import type { GraphManager } from "./graph-manager.svelte";
|
||||
import { HTML } from "@threlte/extras";
|
||||
import { onMount } from "svelte";
|
||||
import type { NodeType } from "@nodes/types";
|
||||
@@ -10,7 +10,7 @@
|
||||
|
||||
let input: HTMLInputElement;
|
||||
let value: string = "";
|
||||
let activeNodeId: NodeType = "";
|
||||
let activeNodeId: NodeType | undefined = undefined;
|
||||
|
||||
const allNodes = graph.getNodeDefinitions();
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
|
||||
$: nodes = value === "" ? allNodes : filterNodes();
|
||||
$: if (nodes) {
|
||||
if (activeNodeId === "") {
|
||||
if (activeNodeId === undefined) {
|
||||
activeNodeId = nodes[0].id;
|
||||
} else if (nodes.length) {
|
||||
const node = nodes.find((node) => node.id === activeNodeId);
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
import BackgroundVert from "./Background.vert";
|
||||
import BackgroundFrag from "./Background.frag";
|
||||
import { colors } from "../graph/colors.svelte";
|
||||
import { Color } from "three";
|
||||
import { appSettings } from "$lib/settings/app-settings.svelte";
|
||||
|
||||
type Props = {
|
||||
@@ -42,10 +41,10 @@
|
||||
value: [0, 1, 0],
|
||||
},
|
||||
backgroundColor: {
|
||||
value: colors["layer-0"].clone(),
|
||||
value: colors["layer-0"],
|
||||
},
|
||||
lineColor: {
|
||||
value: colors["outline"].clone(),
|
||||
value: colors["outline"],
|
||||
},
|
||||
zoomLimits: {
|
||||
value: [2, 50],
|
||||
@@ -55,9 +54,8 @@
|
||||
},
|
||||
}}
|
||||
uniforms.camPos.value={cameraPosition}
|
||||
uniforms.backgroundColor.value={appSettings.theme &&
|
||||
colors["layer-0"].clone()}
|
||||
uniforms.lineColor.value={appSettings.theme && colors["outline"].clone()}
|
||||
uniforms.backgroundColor.value={appSettings.theme && colors["layer-0"]}
|
||||
uniforms.lineColor.value={appSettings.theme && colors["outline"]}
|
||||
uniforms.zoomLimits.value={[minZoom, maxZoom]}
|
||||
uniforms.dimensions.value={[width, height]}
|
||||
/>
|
||||
|
||||
@@ -4,17 +4,17 @@ import type {
|
||||
Node,
|
||||
NodeInput,
|
||||
NodeRegistry,
|
||||
NodeType,
|
||||
Socket,
|
||||
} from "@nodes/types";
|
||||
import { fastHashString } from "@nodes/utils";
|
||||
import { writable, type Writable } from "svelte/store";
|
||||
import EventEmitter from "./helpers/EventEmitter.js";
|
||||
import { createLogger } from "./helpers/index.js";
|
||||
import throttle from "./helpers/throttle.js";
|
||||
import { HistoryManager } from "./history-manager.js";
|
||||
import { SvelteMap } from "svelte/reactivity";
|
||||
import EventEmitter from "./helpers/EventEmitter";
|
||||
import { createLogger } from "./helpers/index";
|
||||
import throttle from "./helpers/throttle";
|
||||
import { HistoryManager } from "./history-manager";
|
||||
|
||||
const logger = createLogger("graph-manager");
|
||||
|
||||
logger.mute();
|
||||
|
||||
const clone =
|
||||
@@ -40,24 +40,28 @@ export class GraphManager extends EventEmitter<{
|
||||
values: Record<string, unknown>;
|
||||
};
|
||||
}> {
|
||||
status: Writable<"loading" | "idle" | "error"> = writable("loading");
|
||||
status = $state<"loading" | "idle" | "error">();
|
||||
loaded = false;
|
||||
|
||||
graph: Graph = { id: 0, nodes: [], edges: [] };
|
||||
id = writable(0);
|
||||
id = $state(0);
|
||||
|
||||
private _nodes: Map<number, Node> = new Map();
|
||||
nodes: Writable<Map<number, Node>> = writable(new Map());
|
||||
nodes = new SvelteMap<number, Node>();
|
||||
|
||||
private _edges: Edge[] = [];
|
||||
edges: Writable<Edge[]> = writable([]);
|
||||
edges = $state<Edge[]>([]);
|
||||
|
||||
settingTypes: Record<string, NodeInput> = {};
|
||||
settings: Record<string, unknown> = {};
|
||||
settings = $state<Record<string, unknown>>();
|
||||
|
||||
currentUndoGroup: number | null = null;
|
||||
|
||||
inputSockets: Writable<Set<string>> = writable(new Set());
|
||||
inputSockets = $derived.by(() => {
|
||||
const s = new Set<string>();
|
||||
for (const edge of this.edges) {
|
||||
s.add(`${edge[2].id}-${edge[3]}`);
|
||||
}
|
||||
return s;
|
||||
});
|
||||
|
||||
history: HistoryManager = new HistoryManager();
|
||||
execute = throttle(() => {
|
||||
@@ -67,28 +71,17 @@ export class GraphManager extends EventEmitter<{
|
||||
|
||||
constructor(public registry: NodeRegistry) {
|
||||
super();
|
||||
this.nodes.subscribe((nodes) => {
|
||||
this._nodes = nodes;
|
||||
});
|
||||
this.edges.subscribe((edges) => {
|
||||
this._edges = edges;
|
||||
const s = new Set<string>();
|
||||
for (const edge of edges) {
|
||||
s.add(`${edge[2].id}-${edge[3]}`);
|
||||
}
|
||||
this.inputSockets.set(s);
|
||||
});
|
||||
}
|
||||
|
||||
serialize(): Graph {
|
||||
logger.group("serializing graph");
|
||||
const nodes = Array.from(this._nodes.values()).map((node) => ({
|
||||
const nodes = Array.from(this.nodes.values()).map((node) => ({
|
||||
id: node.id,
|
||||
position: [...node.position],
|
||||
type: node.type,
|
||||
props: node.props,
|
||||
})) as Node[];
|
||||
const edges = this._edges.map((edge) => [
|
||||
const edges = this.edges.map((edge) => [
|
||||
edge[0].id,
|
||||
edge[1],
|
||||
edge[2].id,
|
||||
@@ -101,12 +94,12 @@ export class GraphManager extends EventEmitter<{
|
||||
edges,
|
||||
};
|
||||
logger.groupEnd();
|
||||
|
||||
return clone(serialized);
|
||||
return clone($state.snapshot(serialized));
|
||||
}
|
||||
|
||||
private lastSettingsHash = 0;
|
||||
setSettings(settings: Record<string, unknown>) {
|
||||
console.log("GraphManager.setSettings", settings);
|
||||
let hash = fastHashString(JSON.stringify(settings));
|
||||
if (hash === this.lastSettingsHash) return;
|
||||
this.lastSettingsHash = hash;
|
||||
@@ -141,7 +134,7 @@ export class GraphManager extends EventEmitter<{
|
||||
const children = node.tmp?.children || [];
|
||||
for (const child of children) {
|
||||
if (nodes.includes(child)) {
|
||||
const edge = this._edges.find(
|
||||
const edge = this.edges.find(
|
||||
(e) => e[0].id === node.id && e[2].id === child.id,
|
||||
);
|
||||
if (edge) {
|
||||
@@ -188,8 +181,12 @@ export class GraphManager extends EventEmitter<{
|
||||
return [from, edge[1], to, edge[3]] as Edge;
|
||||
});
|
||||
|
||||
this.edges.set(edges);
|
||||
this.nodes.set(nodes);
|
||||
this.edges = [...edges];
|
||||
|
||||
this.nodes.clear();
|
||||
for (const [id, node] of nodes) {
|
||||
this.nodes.set(id, node);
|
||||
}
|
||||
|
||||
this.execute();
|
||||
}
|
||||
@@ -199,8 +196,8 @@ export class GraphManager extends EventEmitter<{
|
||||
|
||||
this.loaded = false;
|
||||
this.graph = graph;
|
||||
this.status.set("loading");
|
||||
this.id.set(graph.id);
|
||||
this.status = "loading";
|
||||
this.id = graph.id;
|
||||
|
||||
const nodeIds = Array.from(new Set([...graph.nodes.map((n) => n.type)]));
|
||||
await this.registry.load(nodeIds);
|
||||
@@ -209,7 +206,7 @@ export class GraphManager extends EventEmitter<{
|
||||
const nodeType = this.registry.getNode(node.type);
|
||||
if (!nodeType) {
|
||||
logger.error(`Node type not found: ${node.type}`);
|
||||
this.status.set("error");
|
||||
this.status = "error";
|
||||
return;
|
||||
}
|
||||
node.tmp = node.tmp || {};
|
||||
@@ -254,7 +251,7 @@ export class GraphManager extends EventEmitter<{
|
||||
|
||||
this.save();
|
||||
|
||||
this.status.set("idle");
|
||||
this.status = "idle";
|
||||
|
||||
this.loaded = true;
|
||||
logger.log(`Graph loaded in ${performance.now() - a}ms`);
|
||||
@@ -262,18 +259,18 @@ export class GraphManager extends EventEmitter<{
|
||||
}
|
||||
|
||||
getAllNodes() {
|
||||
return Array.from(this._nodes.values());
|
||||
return Array.from(this.nodes.values());
|
||||
}
|
||||
|
||||
getNode(id: number) {
|
||||
return this._nodes.get(id);
|
||||
return this.nodes.get(id);
|
||||
}
|
||||
|
||||
getNodeType(id: string) {
|
||||
return this.registry.getNode(id);
|
||||
}
|
||||
|
||||
async loadNode(id: string) {
|
||||
async loadNode(id: NodeType) {
|
||||
await this.registry.load([id]);
|
||||
const nodeType = this.registry.getNode(id);
|
||||
|
||||
@@ -287,7 +284,8 @@ export class GraphManager extends EventEmitter<{
|
||||
if (settingId) {
|
||||
settingTypes[settingId] = nodeType.inputs[key];
|
||||
if (
|
||||
settingValues[settingId] === undefined &&
|
||||
settingValues &&
|
||||
settingValues?.[settingId] === undefined &&
|
||||
"value" in nodeType.inputs[key]
|
||||
) {
|
||||
settingValues[settingId] = nodeType.inputs[key].value;
|
||||
@@ -297,6 +295,7 @@ export class GraphManager extends EventEmitter<{
|
||||
}
|
||||
|
||||
this.settings = settingValues;
|
||||
console.log("GraphManager.setSettings", settingValues);
|
||||
this.settingTypes = settingTypes;
|
||||
this.emit("settings", { types: settingTypes, values: settingValues });
|
||||
}
|
||||
@@ -331,8 +330,8 @@ export class GraphManager extends EventEmitter<{
|
||||
}
|
||||
|
||||
removeNode(node: Node, { restoreEdges = false } = {}) {
|
||||
const edgesToNode = this._edges.filter((edge) => edge[2].id === node.id);
|
||||
const edgesFromNode = this._edges.filter((edge) => edge[0].id === node.id);
|
||||
const edgesToNode = this.edges.filter((edge) => edge[2].id === node.id);
|
||||
const edgesFromNode = this.edges.filter((edge) => edge[0].id === node.id);
|
||||
for (const edge of [...edgesToNode, ...edgesFromNode]) {
|
||||
this.removeEdge(edge, { applyDeletion: false });
|
||||
}
|
||||
@@ -355,18 +354,13 @@ export class GraphManager extends EventEmitter<{
|
||||
}
|
||||
}
|
||||
|
||||
this.edges.set(this._edges);
|
||||
|
||||
this.nodes.update((nodes) => {
|
||||
nodes.delete(node.id);
|
||||
return nodes;
|
||||
});
|
||||
this.nodes.delete(node.id);
|
||||
this.execute();
|
||||
this.save();
|
||||
}
|
||||
|
||||
createNodeId() {
|
||||
const max = Math.max(...this._nodes.keys());
|
||||
const max = Math.max(0, ...this.nodes.keys());
|
||||
return max + 1;
|
||||
}
|
||||
|
||||
@@ -406,13 +400,11 @@ export class GraphManager extends EventEmitter<{
|
||||
});
|
||||
|
||||
for (const node of nodes) {
|
||||
this._nodes.set(node.id, node);
|
||||
this.nodes.set(node.id, node);
|
||||
}
|
||||
|
||||
this._edges.push(..._edges);
|
||||
this.edges.push(..._edges);
|
||||
|
||||
this.nodes.set(this._nodes);
|
||||
this.edges.set(this._edges);
|
||||
this.save();
|
||||
return nodes;
|
||||
}
|
||||
@@ -440,10 +432,7 @@ export class GraphManager extends EventEmitter<{
|
||||
props,
|
||||
};
|
||||
|
||||
this.nodes.update((nodes) => {
|
||||
nodes.set(node.id, node);
|
||||
return nodes;
|
||||
});
|
||||
this.nodes.set(node.id, node);
|
||||
|
||||
this.save();
|
||||
}
|
||||
@@ -480,7 +469,7 @@ export class GraphManager extends EventEmitter<{
|
||||
return;
|
||||
}
|
||||
|
||||
const edgeToBeReplaced = this._edges.find(
|
||||
const edgeToBeReplaced = this.edges.find(
|
||||
(e) => e[2].id === to.id && e[3] === toSocket,
|
||||
);
|
||||
if (edgeToBeReplaced) {
|
||||
@@ -488,9 +477,9 @@ export class GraphManager extends EventEmitter<{
|
||||
}
|
||||
|
||||
if (applyUpdate) {
|
||||
this._edges.push([from, fromSocket, to, toSocket]);
|
||||
this.edges.push([from, fromSocket, to, toSocket]);
|
||||
} else {
|
||||
this._edges.push([from, fromSocket, to, toSocket]);
|
||||
this.edges.push([from, fromSocket, to, toSocket]);
|
||||
}
|
||||
|
||||
from.tmp = from.tmp || {};
|
||||
@@ -502,7 +491,6 @@ export class GraphManager extends EventEmitter<{
|
||||
to.tmp.parents.push(from);
|
||||
|
||||
if (applyUpdate) {
|
||||
this.edges.set(this._edges);
|
||||
this.save();
|
||||
}
|
||||
this.execute();
|
||||
@@ -630,7 +618,7 @@ export class GraphManager extends EventEmitter<{
|
||||
const id2 = edge[2].id;
|
||||
const sid2 = edge[3];
|
||||
|
||||
const _edge = this._edges.find(
|
||||
const _edge = this.edges.find(
|
||||
(e) =>
|
||||
e[0].id === id0 && e[1] === sid0 && e[2].id === id2 && e[3] === sid2,
|
||||
);
|
||||
@@ -651,18 +639,16 @@ export class GraphManager extends EventEmitter<{
|
||||
}
|
||||
|
||||
if (applyDeletion) {
|
||||
this.edges.update((edges) => {
|
||||
return edges.filter((e) => e !== _edge);
|
||||
});
|
||||
this.edges = this.edges.filter((e) => e !== _edge);
|
||||
this.execute();
|
||||
this.save();
|
||||
} else {
|
||||
this._edges = this._edges.filter((e) => e !== _edge);
|
||||
this.edges = this.edges.filter((e) => e !== _edge);
|
||||
}
|
||||
}
|
||||
|
||||
getEdgesToNode(node: Node) {
|
||||
return this._edges
|
||||
return this.edges
|
||||
.filter((edge) => edge[2].id === node.id)
|
||||
.map((edge) => {
|
||||
const from = this.getNode(edge[0].id);
|
||||
@@ -674,7 +660,7 @@ export class GraphManager extends EventEmitter<{
|
||||
}
|
||||
|
||||
getEdgesFromNode(node: Node) {
|
||||
return this._edges
|
||||
return this.edges
|
||||
.filter((edge) => edge[0].id === node.id)
|
||||
.map((edge) => {
|
||||
const from = this.getNode(edge[0].id);
|
||||
@@ -1,73 +1,67 @@
|
||||
<script lang="ts">
|
||||
import type { Node, NodeType, Socket } from "@nodes/types";
|
||||
import { GraphSchema } from "@nodes/types";
|
||||
import { getContext, onMount, setContext } from "svelte";
|
||||
import type { OrthographicCamera } from "three";
|
||||
import { createKeyMap } from "../../helpers/createKeyMap";
|
||||
import AddMenu from "../AddMenu.svelte";
|
||||
import Background from "../background/Background.svelte";
|
||||
import BoxSelection from "../BoxSelection.svelte";
|
||||
import Camera from "../Camera.svelte";
|
||||
import FloatingEdge from "../edges/FloatingEdge.svelte";
|
||||
import {
|
||||
animate,
|
||||
lerp,
|
||||
snapToGrid as snapPointToGrid,
|
||||
} from "../helpers/index.js";
|
||||
import type { OrthographicCamera } from "three";
|
||||
import Background from "../background/Background.svelte";
|
||||
import { getContext, onMount, setContext } from "svelte";
|
||||
import Camera from "../Camera.svelte";
|
||||
import GraphView from "./GraphView.svelte";
|
||||
import type { Node, NodeId, Node as NodeType, Socket } from "@nodes/types";
|
||||
import { GraphSchema } from "@nodes/types";
|
||||
import FloatingEdge from "../edges/FloatingEdge.svelte";
|
||||
import { getGraphState } from "./state.svelte";
|
||||
import { createKeyMap } from "../../helpers/createKeyMap";
|
||||
import BoxSelection from "../BoxSelection.svelte";
|
||||
import AddMenu from "../AddMenu.svelte";
|
||||
|
||||
import HelpView from "../HelpView.svelte";
|
||||
import FileSaver from "file-saver";
|
||||
import { Canvas } from "@threlte/core";
|
||||
import { getGraphManager } from "./context.js";
|
||||
import FileSaver from "file-saver";
|
||||
import HelpView from "../HelpView.svelte";
|
||||
import { getGraphManager } from "./context";
|
||||
|
||||
const graph = getGraphManager();
|
||||
const graphState = getGraphState();
|
||||
|
||||
export let snapToGrid = true;
|
||||
export let showGrid = true;
|
||||
export let showHelp = false;
|
||||
const {
|
||||
snapToGrid = $bindable(true),
|
||||
showGrid = $bindable(true),
|
||||
showHelp = $bindable(false),
|
||||
} = $props();
|
||||
|
||||
const keymap = getContext<ReturnType<typeof createKeyMap>>("keymap");
|
||||
|
||||
const manager = getGraphManager();
|
||||
let wrapper = $state<HTMLDivElement>(null!);
|
||||
|
||||
const status = manager.status;
|
||||
const nodes = manager.nodes;
|
||||
const edges = manager.edges;
|
||||
const rect: DOMRect = $derived(
|
||||
wrapper ? wrapper.getBoundingClientRect() : new DOMRect(0, 0, 0, 0),
|
||||
);
|
||||
let width = $derived(rect?.width ?? 100);
|
||||
let height = $derived(rect?.height ?? 100);
|
||||
|
||||
let wrapper: HTMLDivElement;
|
||||
let rect: DOMRect;
|
||||
$: rect =
|
||||
wrapper && width
|
||||
? wrapper.getBoundingClientRect()
|
||||
: ({ x: 0, y: 0, width: 0, height: 0 } as DOMRect);
|
||||
|
||||
let camera: OrthographicCamera;
|
||||
let camera = $state<OrthographicCamera>(null!);
|
||||
const minZoom = 1;
|
||||
const maxZoom = 40;
|
||||
let mousePosition = [0, 0];
|
||||
let mouseDown: null | [number, number] = null;
|
||||
let mousePosition = $state([0, 0]);
|
||||
let mouseDown = $state<[number, number] | null>(null);
|
||||
let mouseDownId = -1;
|
||||
let boxSelection = false;
|
||||
let boxSelection = $state(false);
|
||||
const cameraDown = [0, 0];
|
||||
let cameraPosition: [number, number, number] = [0, 0, 4];
|
||||
let addMenuPosition: [number, number] | null = null;
|
||||
let cameraPosition: [number, number, number] = $state([0, 0, 4]);
|
||||
let addMenuPosition = $state<[number, number] | null>(null);
|
||||
let clipboard: null | {
|
||||
nodes: Node[];
|
||||
edges: [number, number, number, string][];
|
||||
} = null;
|
||||
|
||||
let width = rect?.width ?? 100;
|
||||
let height = rect?.height ?? 100;
|
||||
|
||||
let cameraBounds = [-1000, 1000, -1000, 1000];
|
||||
$: cameraBounds = [
|
||||
const cameraBounds = $derived([
|
||||
cameraPosition[0] - width / cameraPosition[2] / 2,
|
||||
cameraPosition[0] + width / cameraPosition[2] / 2,
|
||||
cameraPosition[1] - height / cameraPosition[2] / 2,
|
||||
cameraPosition[1] + height / cameraPosition[2] / 2,
|
||||
];
|
||||
]);
|
||||
function setCameraTransform(
|
||||
x = cameraPosition[0],
|
||||
y = cameraPosition[1],
|
||||
@@ -82,7 +76,7 @@
|
||||
localStorage.setItem("cameraPosition", JSON.stringify(cameraPosition));
|
||||
}
|
||||
|
||||
function updateNodePosition(node: NodeType) {
|
||||
function updateNodePosition(node: Node) {
|
||||
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`);
|
||||
@@ -96,6 +90,7 @@
|
||||
delete node.tmp.x;
|
||||
delete node.tmp.y;
|
||||
}
|
||||
graph.edges = [...graph.edges];
|
||||
} else {
|
||||
node.tmp.ref.style.setProperty("--nx", `${node.position[0] * 10}px`);
|
||||
node.tmp.ref.style.setProperty("--ny", `${node.position[1] * 10}px`);
|
||||
@@ -112,7 +107,7 @@
|
||||
if (nodeTypeId in nodeHeightCache) {
|
||||
return nodeHeightCache[nodeTypeId];
|
||||
}
|
||||
const node = manager.getNodeType(nodeTypeId);
|
||||
const node = graph.getNodeType(nodeTypeId);
|
||||
if (!node?.inputs) {
|
||||
return 5;
|
||||
}
|
||||
@@ -131,7 +126,7 @@
|
||||
}
|
||||
setContext("getNodeHeight", getNodeHeight);
|
||||
|
||||
setContext("isNodeInView", (node: NodeType) => {
|
||||
setContext("isNodeInView", (node: Node) => {
|
||||
const height = getNodeHeight(node.type);
|
||||
const width = 20;
|
||||
return (
|
||||
@@ -162,7 +157,7 @@
|
||||
// we are going to check if we clicked on a node by coordinates
|
||||
if (clickedNodeId === -1) {
|
||||
const [downX, downY] = projectScreenToWorld(mx, my);
|
||||
for (const node of $nodes.values()) {
|
||||
for (const node of graph.nodes.values()) {
|
||||
const x = node.position[0];
|
||||
const y = node.position[1];
|
||||
const height = getNodeHeight(node.type);
|
||||
@@ -183,13 +178,13 @@
|
||||
|
||||
// remove existing edge
|
||||
if (typeof index === "string") {
|
||||
const edges = manager.getEdgesToNode(node);
|
||||
const edges = graph.getEdgesToNode(node);
|
||||
for (const edge of edges) {
|
||||
if (edge[3] === index) {
|
||||
node = edge[0];
|
||||
index = edge[1];
|
||||
position = getSocketPosition(node, index);
|
||||
manager.removeEdge(edge);
|
||||
graph.removeEdge(edge);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -202,7 +197,7 @@
|
||||
position,
|
||||
};
|
||||
|
||||
graphState.possibleSockets = manager
|
||||
graphState.possibleSockets = graph
|
||||
.getPossibleSockets(graphState.activeSocket)
|
||||
.map(([node, index]) => {
|
||||
return {
|
||||
@@ -227,7 +222,7 @@
|
||||
}
|
||||
|
||||
function getSocketPosition(
|
||||
node: NodeType,
|
||||
node: Node,
|
||||
index: string | number,
|
||||
): [number, number] {
|
||||
if (typeof index === "number") {
|
||||
@@ -294,7 +289,7 @@
|
||||
const x2 = Math.max(mouseD[0], mousePosition[0]);
|
||||
const y1 = Math.min(mouseD[1], mousePosition[1]);
|
||||
const y2 = Math.max(mouseD[1], mousePosition[1]);
|
||||
for (const node of $nodes.values()) {
|
||||
for (const node of graph.nodes.values()) {
|
||||
if (!node?.tmp) continue;
|
||||
const x = node.position[0];
|
||||
const y = node.position[1];
|
||||
@@ -310,7 +305,7 @@
|
||||
|
||||
// here we are handling dragging of nodes
|
||||
if (graphState.activeNodeId !== -1 && mouseDownId !== -1) {
|
||||
const node = manager.getNode(graphState.activeNodeId);
|
||||
const node = graph.getNode(graphState.activeNodeId);
|
||||
if (!node || event.buttons !== 1) return;
|
||||
|
||||
node.tmp = node.tmp || {};
|
||||
@@ -341,7 +336,7 @@
|
||||
|
||||
if (graphState.selectedNodes?.size) {
|
||||
for (const nodeId of graphState.selectedNodes) {
|
||||
const n = manager.getNode(nodeId);
|
||||
const n = graph.getNode(nodeId);
|
||||
if (!n?.tmp) continue;
|
||||
n.tmp.x = (n?.tmp?.downX || 0) - vecX;
|
||||
n.tmp.y = (n?.tmp?.downY || 0) - vecY;
|
||||
@@ -354,7 +349,6 @@
|
||||
|
||||
updateNodePosition(node);
|
||||
|
||||
$edges = $edges;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -436,10 +430,10 @@
|
||||
graphState.activeNodeId = clickedNodeId;
|
||||
// select the node
|
||||
} else if (event.shiftKey) {
|
||||
const activeNode = manager.getNode(graphState.activeNodeId);
|
||||
const newNode = manager.getNode(clickedNodeId);
|
||||
const activeNode = graph.getNode(graphState.activeNodeId);
|
||||
const newNode = graph.getNode(clickedNodeId);
|
||||
if (activeNode && newNode) {
|
||||
const edge = manager.getNodesBetween(activeNode, newNode);
|
||||
const edge = graph.getNodesBetween(activeNode, newNode);
|
||||
if (edge) {
|
||||
graphState.selectedNodes.clear();
|
||||
for (const node of edge) {
|
||||
@@ -456,7 +450,7 @@
|
||||
boxSelection = true;
|
||||
}
|
||||
|
||||
const node = manager.getNode(graphState.activeNodeId);
|
||||
const node = graph.getNode(graphState.activeNodeId);
|
||||
if (!node) return;
|
||||
node.tmp = node.tmp || {};
|
||||
node.tmp.downX = node.position[0];
|
||||
@@ -464,7 +458,7 @@
|
||||
|
||||
if (graphState.selectedNodes) {
|
||||
for (const nodeId of graphState.selectedNodes) {
|
||||
const n = manager.getNode(nodeId);
|
||||
const n = graph.getNode(nodeId);
|
||||
if (!n) continue;
|
||||
n.tmp = n.tmp || {};
|
||||
n.tmp.downX = n.position[0];
|
||||
@@ -480,10 +474,10 @@
|
||||
graphState.activeNodeId,
|
||||
...(graphState.selectedNodes?.values() || []),
|
||||
]
|
||||
.map((id) => manager.getNode(id))
|
||||
.map((id) => graph.getNode(id))
|
||||
.filter(Boolean) as Node[];
|
||||
|
||||
const _edges = manager.getEdgesBetweenNodes(_nodes);
|
||||
const _edges = graph.getEdgesBetweenNodes(_nodes);
|
||||
|
||||
_nodes = _nodes.map((_node) => {
|
||||
const node = globalThis.structuredClone({
|
||||
@@ -514,7 +508,7 @@
|
||||
})
|
||||
.filter(Boolean) as Node[];
|
||||
|
||||
const newNodes = manager.createGraph(_nodes, clipboard.edges);
|
||||
const newNodes = graph.createGraph(_nodes, clipboard.edges);
|
||||
graphState.selectedNodes.clear();
|
||||
for (const node of newNodes) {
|
||||
graphState.selectedNodes.add(node.id);
|
||||
@@ -527,9 +521,9 @@
|
||||
key: "l",
|
||||
description: "Select linked nodes",
|
||||
callback: () => {
|
||||
const activeNode = manager.getNode(graphState.activeNodeId);
|
||||
const activeNode = graph.getNode(graphState.activeNodeId);
|
||||
if (activeNode) {
|
||||
const nodes = manager.getLinkedNodes(activeNode);
|
||||
const nodes = graph.getLinkedNodes(activeNode);
|
||||
graphState.selectedNodes.clear();
|
||||
for (const node of nodes) {
|
||||
graphState.selectedNodes.add(node.id);
|
||||
@@ -542,7 +536,8 @@
|
||||
key: "?",
|
||||
description: "Toggle Help",
|
||||
callback: () => {
|
||||
showHelp = !showHelp;
|
||||
// TODO: fix this
|
||||
// showHelp = !showHelp;
|
||||
},
|
||||
});
|
||||
|
||||
@@ -586,12 +581,12 @@
|
||||
if (!isBodyFocused()) return;
|
||||
|
||||
const average = [0, 0];
|
||||
for (const node of $nodes.values()) {
|
||||
for (const node of graph.nodes.values()) {
|
||||
average[0] += node.position[0];
|
||||
average[1] += node.position[1];
|
||||
}
|
||||
average[0] = average[0] ? average[0] / $nodes.size : 0;
|
||||
average[1] = average[1] ? average[1] / $nodes.size : 0;
|
||||
average[0] = average[0] ? average[0] / graph.nodes.size : 0;
|
||||
average[1] = average[1] ? average[1] / graph.nodes.size : 0;
|
||||
|
||||
const camX = cameraPosition[0];
|
||||
const camY = cameraPosition[1];
|
||||
@@ -617,7 +612,7 @@
|
||||
description: "Select all nodes",
|
||||
callback: () => {
|
||||
if (!isBodyFocused()) return;
|
||||
for (const node of $nodes.keys()) {
|
||||
for (const node of graph.nodes.keys()) {
|
||||
graphState.selectedNodes.add(node);
|
||||
}
|
||||
},
|
||||
@@ -629,8 +624,8 @@
|
||||
description: "Undo",
|
||||
callback: () => {
|
||||
if (!isBodyFocused()) return;
|
||||
manager.undo();
|
||||
for (const node of $nodes.values()) {
|
||||
graph.undo();
|
||||
for (const node of graph.nodes.values()) {
|
||||
updateNodePosition(node);
|
||||
}
|
||||
},
|
||||
@@ -641,8 +636,8 @@
|
||||
ctrl: true,
|
||||
description: "Redo",
|
||||
callback: () => {
|
||||
manager.redo();
|
||||
for (const node of $nodes.values()) {
|
||||
graph.redo();
|
||||
for (const node of graph.nodes.values()) {
|
||||
updateNodePosition(node);
|
||||
}
|
||||
},
|
||||
@@ -654,7 +649,7 @@
|
||||
description: "Save",
|
||||
preventDefault: true,
|
||||
callback: () => {
|
||||
const state = manager.serialize();
|
||||
const state = graph.serialize();
|
||||
const blob = new Blob([JSON.stringify(state)], {
|
||||
type: "application/json;charset=utf-8",
|
||||
});
|
||||
@@ -667,24 +662,24 @@
|
||||
description: "Delete selected nodes",
|
||||
callback: (event) => {
|
||||
if (!isBodyFocused()) return;
|
||||
manager.startUndoGroup();
|
||||
graph.startUndoGroup();
|
||||
if (graphState.activeNodeId !== -1) {
|
||||
const node = manager.getNode(graphState.activeNodeId);
|
||||
const node = graph.getNode(graphState.activeNodeId);
|
||||
if (node) {
|
||||
manager.removeNode(node, { restoreEdges: event.ctrlKey });
|
||||
graph.removeNode(node, { restoreEdges: event.ctrlKey });
|
||||
graphState.activeNodeId = -1;
|
||||
}
|
||||
}
|
||||
if (graphState.selectedNodes) {
|
||||
for (const nodeId of graphState.selectedNodes) {
|
||||
const node = manager.getNode(nodeId);
|
||||
const node = graph.getNode(nodeId);
|
||||
if (node) {
|
||||
manager.removeNode(node, { restoreEdges: event.ctrlKey });
|
||||
graph.removeNode(node, { restoreEdges: event.ctrlKey });
|
||||
}
|
||||
}
|
||||
graphState.clearSelection();
|
||||
}
|
||||
manager.saveUndoGroup();
|
||||
graph.saveUndoGroup();
|
||||
},
|
||||
});
|
||||
|
||||
@@ -692,7 +687,7 @@
|
||||
isPanning = false;
|
||||
if (!mouseDown) return;
|
||||
|
||||
const activeNode = manager.getNode(graphState.activeNodeId);
|
||||
const activeNode = graph.getNode(graphState.activeNodeId);
|
||||
|
||||
const clickedNodeId = getNodeIdFromEvent(event);
|
||||
|
||||
@@ -724,9 +719,9 @@
|
||||
}
|
||||
const nodes = [
|
||||
...[...(graphState.selectedNodes?.values() || [])].map((id) =>
|
||||
manager.getNode(id),
|
||||
graph.getNode(id),
|
||||
),
|
||||
] as NodeType[];
|
||||
] as Node[];
|
||||
|
||||
const vec = [
|
||||
activeNode.position[0] - (activeNode?.tmp.x || 0),
|
||||
@@ -758,16 +753,14 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$edges = $edges;
|
||||
});
|
||||
manager.save();
|
||||
graph.save();
|
||||
} else if (graphState.hoveredSocket && graphState.activeSocket) {
|
||||
if (
|
||||
typeof graphState.hoveredSocket.index === "number" &&
|
||||
typeof graphState.activeSocket.index === "string"
|
||||
) {
|
||||
manager.createEdge(
|
||||
graph.createEdge(
|
||||
graphState.hoveredSocket.node,
|
||||
graphState.hoveredSocket.index || 0,
|
||||
graphState.activeSocket.node,
|
||||
@@ -777,14 +770,14 @@
|
||||
typeof graphState.activeSocket.index == "number" &&
|
||||
typeof graphState.hoveredSocket.index === "string"
|
||||
) {
|
||||
manager.createEdge(
|
||||
graph.createEdge(
|
||||
graphState.activeSocket.node,
|
||||
graphState.activeSocket.index || 0,
|
||||
graphState.hoveredSocket.node,
|
||||
graphState.hoveredSocket.index,
|
||||
);
|
||||
}
|
||||
manager.save();
|
||||
graph.save();
|
||||
}
|
||||
|
||||
// check if camera moved
|
||||
@@ -807,9 +800,9 @@
|
||||
addMenuPosition = null;
|
||||
}
|
||||
|
||||
let isPanning = false;
|
||||
let isDragging = false;
|
||||
let hoveredNodeId = -1;
|
||||
let isPanning = $state(false);
|
||||
let isDragging = $state(false);
|
||||
let hoveredNodeId = $state(-1);
|
||||
|
||||
function handleMouseLeave() {
|
||||
isDragging = false;
|
||||
@@ -820,7 +813,7 @@
|
||||
event.preventDefault();
|
||||
isDragging = false;
|
||||
if (!event.dataTransfer) return;
|
||||
const nodeId = event.dataTransfer.getData("data/node-id") as NodeId;
|
||||
const nodeId = event.dataTransfer.getData("data/node-id") as NodeType;
|
||||
let mx = event.clientX - rect.x;
|
||||
let my = event.clientY - rect.y;
|
||||
|
||||
@@ -841,8 +834,8 @@
|
||||
}
|
||||
|
||||
const pos = projectScreenToWorld(mx, my);
|
||||
manager.registry.load([nodeId]).then(() => {
|
||||
manager.createNode({
|
||||
graph.registry.load([nodeId]).then(() => {
|
||||
graph.createNode({
|
||||
type: nodeId,
|
||||
props,
|
||||
position: pos,
|
||||
@@ -856,9 +849,9 @@
|
||||
reader.onload = async (e) => {
|
||||
const buffer = e.target?.result;
|
||||
if (buffer?.constructor === ArrayBuffer) {
|
||||
const nodeType = await manager.registry.register(buffer);
|
||||
const nodeType = await graph.registry.register(buffer);
|
||||
|
||||
manager.createNode({
|
||||
graph.createNode({
|
||||
type: nodeType.id,
|
||||
props: {},
|
||||
position: projectScreenToWorld(mx, my),
|
||||
@@ -869,10 +862,10 @@
|
||||
} else if (file.type === "application/json") {
|
||||
const reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
const buffer = e.target?.result as Buffer;
|
||||
const buffer = e.target?.result as ArrayBuffer;
|
||||
if (buffer) {
|
||||
const state = GraphSchema.parse(JSON.parse(buffer.toString()));
|
||||
manager.load(state);
|
||||
graph.load(state);
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
@@ -908,10 +901,10 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:window on:mousemove={handleMouseMove} on:mouseup={handleMouseUp} />
|
||||
<svelte:window onmousemove={handleMouseMove} onmouseup={handleMouseUp} />
|
||||
|
||||
<div
|
||||
on:wheel={handleMouseScroll}
|
||||
onwheel={handleMouseScroll}
|
||||
bind:this={wrapper}
|
||||
class="graph-wrapper"
|
||||
class:is-panning={isPanning}
|
||||
@@ -921,21 +914,21 @@
|
||||
tabindex="0"
|
||||
bind:clientWidth={width}
|
||||
bind:clientHeight={height}
|
||||
on:dragenter={handleDragEnter}
|
||||
on:dragover={handlerDragOver}
|
||||
on:dragexit={handleDragEnd}
|
||||
on:drop={handleDrop}
|
||||
on:mouseleave={handleMouseLeave}
|
||||
on:keydown={keymap.handleKeyboardEvent}
|
||||
on:mousedown={handleMouseDown}
|
||||
ondragenter={handleDragEnter}
|
||||
ondragover={handlerDragOver}
|
||||
ondragexit={handleDragEnd}
|
||||
ondrop={handleDrop}
|
||||
onmouseleave={handleMouseLeave}
|
||||
onkeydown={keymap.handleKeyboardEvent}
|
||||
onmousedown={handleMouseDown}
|
||||
>
|
||||
<input
|
||||
type="file"
|
||||
accept="application/wasm,application/json"
|
||||
id="drop-zone"
|
||||
disabled={!isDragging}
|
||||
on:dragend={handleDragEnd}
|
||||
on:dragleave={handleDragEnd}
|
||||
ondragend={handleDragEnd}
|
||||
ondragleave={handleDragEnd}
|
||||
/>
|
||||
<label for="drop-zone"></label>
|
||||
|
||||
@@ -958,9 +951,9 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
{#if $status === "idle"}
|
||||
{#if graph.status === "idle"}
|
||||
{#if addMenuPosition}
|
||||
<AddMenu bind:position={addMenuPosition} graph={manager} />
|
||||
<AddMenu bind:position={addMenuPosition} {graph} />
|
||||
{/if}
|
||||
|
||||
{#if graphState.activeSocket}
|
||||
@@ -974,17 +967,17 @@
|
||||
/>
|
||||
{/if}
|
||||
|
||||
<GraphView {nodes} {edges} {cameraPosition} />
|
||||
{:else if $status === "loading"}
|
||||
<GraphView nodes={graph.nodes} edges={graph.edges} {cameraPosition} />
|
||||
{:else if graph.status === "loading"}
|
||||
<span>Loading</span>
|
||||
{:else if $status === "error"}
|
||||
{:else if graph.status === "error"}
|
||||
<span>Error</span>
|
||||
{/if}
|
||||
</Canvas>
|
||||
</div>
|
||||
|
||||
{#if showHelp}
|
||||
<HelpView registry={manager.registry} />
|
||||
<HelpView registry={graph.registry} />
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
|
||||
@@ -4,14 +4,13 @@
|
||||
import Edge from "../edges/Edge.svelte";
|
||||
import Node from "../node/Node.svelte";
|
||||
import { getContext, onMount } from "svelte";
|
||||
import type { Writable } from "svelte/store";
|
||||
import { getGraphState } from "./state.svelte";
|
||||
import { useThrelte } from "@threlte/core";
|
||||
import { appSettings } from "$lib/settings/app-settings.svelte";
|
||||
|
||||
type Props = {
|
||||
nodes: Writable<Map<number, NodeType>>;
|
||||
edges: Writable<EdgeType[]>;
|
||||
nodes: Map<number, NodeType>;
|
||||
edges: EdgeType[];
|
||||
cameraPosition: [number, number, number];
|
||||
};
|
||||
|
||||
@@ -24,6 +23,8 @@
|
||||
invalidate();
|
||||
});
|
||||
|
||||
$effect(() => console.log({ nodes }));
|
||||
|
||||
const graphState = getGraphState();
|
||||
|
||||
const isNodeInView = getContext<(n: NodeType) => boolean>("isNodeInView");
|
||||
@@ -33,14 +34,26 @@
|
||||
"getSocketPosition",
|
||||
);
|
||||
|
||||
function getEdgePosition(edge: EdgeType) {
|
||||
const pos1 = getSocketPosition(edge[0], edge[1]);
|
||||
const pos2 = getSocketPosition(edge[2], edge[3]);
|
||||
return [pos1[0], pos1[1], pos2[0], pos2[1]];
|
||||
const edgePositions = $derived(
|
||||
edges.map((edge) => {
|
||||
const fromNode = nodes.get(edge[0].id);
|
||||
const toNode = nodes.get(edge[2].id);
|
||||
|
||||
// This check is important because nodes might not be there during some transitions.
|
||||
if (!fromNode || !toNode) {
|
||||
return [0, 0, 0, 0];
|
||||
}
|
||||
|
||||
const pos1 = getSocketPosition(fromNode, edge[1]);
|
||||
const pos2 = getSocketPosition(toNode, edge[3]);
|
||||
return [pos1[0], pos1[1], pos2[0], pos2[1]];
|
||||
}),
|
||||
);
|
||||
|
||||
const nodeArray = $derived(Array.from(nodes.values()));
|
||||
|
||||
onMount(() => {
|
||||
for (const node of $nodes.values()) {
|
||||
for (const node of nodes.values()) {
|
||||
if (node?.tmp?.ref) {
|
||||
node.tmp.ref.style.setProperty("--nx", `${node.position[0] * 10}px`);
|
||||
node.tmp.ref.style.setProperty("--ny", `${node.position[1] * 10}px`);
|
||||
@@ -49,9 +62,8 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
{#each $edges as edge (`${edge[0].id}-${edge[1]}-${edge[2].id}-${edge[3]}`)}
|
||||
{@const pos = getEdgePosition(edge)}
|
||||
{@const [x1, y1, x2, y2] = pos}
|
||||
{#each edgePositions as edge (`${edge.join("-")}`)}
|
||||
{@const [x1, y1, x2, y2] = edge}
|
||||
<Edge
|
||||
z={cameraPosition[2]}
|
||||
from={{
|
||||
@@ -74,9 +86,9 @@
|
||||
style:transform={`scale(${cameraPosition[2] * 0.1})`}
|
||||
class:hovering-sockets={graphState.activeSocket}
|
||||
>
|
||||
{#each $nodes.values() as node (node.id)}
|
||||
{#each nodeArray as node, i (node.id)}
|
||||
<Node
|
||||
{node}
|
||||
bind:node={nodeArray[i]}
|
||||
inView={cameraPosition && isNodeInView(node)}
|
||||
z={cameraPosition[2]}
|
||||
/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { Graph, Node, NodeRegistry } from "@nodes/types";
|
||||
import GraphEl from "./Graph.svelte";
|
||||
import { GraphManager } from "../graph-manager.js";
|
||||
import { GraphManager } from "../graph-manager.svelte";
|
||||
import { setContext } from "svelte";
|
||||
import { debounce } from "$lib/helpers";
|
||||
import { createKeyMap } from "$lib/helpers/createKeyMap";
|
||||
@@ -53,13 +53,13 @@
|
||||
}
|
||||
});
|
||||
|
||||
const updateSettings = debounce((s) => {
|
||||
const updateSettings = debounce((s: Record<string, any>) => {
|
||||
manager.setSettings(s);
|
||||
}, 200);
|
||||
|
||||
$effect(() => {
|
||||
if (settingTypes && settings) {
|
||||
updateSettings($state.snapshot(settings));
|
||||
updateSettings(settings);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { GraphManager } from "../graph-manager.js";
|
||||
import type { GraphManager } from "../graph-manager.svelte";
|
||||
import { getContext } from "svelte";
|
||||
|
||||
export function getGraphManager(): GraphManager {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Socket } from "@nodes/types";
|
||||
import { getContext } from "svelte";
|
||||
import { SvelteSet } from 'svelte/reactivity';
|
||||
import { SvelteSet } from "svelte/reactivity";
|
||||
|
||||
export function getGraphState() {
|
||||
return getContext<GraphState>("graphState");
|
||||
@@ -12,11 +12,10 @@ export class GraphState {
|
||||
activeSocket = $state<Socket | null>(null);
|
||||
hoveredSocket = $state<Socket | null>(null);
|
||||
possibleSockets = $state<Socket[]>([]);
|
||||
possibleSocketIds = $derived(new Set(
|
||||
this.possibleSockets.map((s) => `${s.node.id}-${s.index}`),
|
||||
));
|
||||
possibleSocketIds = $derived(
|
||||
new Set(this.possibleSockets.map((s) => `${s.node.id}-${s.index}`)),
|
||||
);
|
||||
clearSelection() {
|
||||
this.selectedNodes.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
inView: boolean;
|
||||
z: number;
|
||||
};
|
||||
const { node, inView, z }: Props = $props();
|
||||
let { node = $bindable(), inView, z }: Props = $props();
|
||||
|
||||
const isActive = $derived(graphState.activeNodeId === node.id);
|
||||
const isSelected = $derived(graphState.selectedNodes.has(node.id));
|
||||
@@ -41,13 +41,11 @@
|
||||
const height = getNodeHeight?.(node.type);
|
||||
|
||||
$effect(() => {
|
||||
node.tmp = node.tmp || {};
|
||||
node.tmp.mesh = meshRef;
|
||||
updateNodePosition?.(node);
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
node.tmp = node.tmp || {};
|
||||
if (!node.tmp) node.tmp = {};
|
||||
node.tmp.mesh = meshRef;
|
||||
updateNodePosition?.(node);
|
||||
});
|
||||
@@ -79,4 +77,4 @@
|
||||
/>
|
||||
</T.Mesh>
|
||||
|
||||
<NodeHtml {node} {inView} {isActive} {isSelected} {z} />
|
||||
<NodeHtml bind:node {inView} {isActive} {isSelected} {z} />
|
||||
|
||||
@@ -59,7 +59,7 @@
|
||||
|
||||
{#each parameters as [key, value], i}
|
||||
<NodeParameter
|
||||
bind:node
|
||||
{node}
|
||||
id={key}
|
||||
input={value}
|
||||
isLast={i == parameters.length - 1}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import type { Node, Socket } from "@nodes/types";
|
||||
import { getContext } from "svelte";
|
||||
|
||||
export let node: Node;
|
||||
const { node = $bindable<Node>() } = $props();
|
||||
|
||||
const setDownSocket = getContext<(socket: Socket) => void>("setDownSocket");
|
||||
const getSocketPosition =
|
||||
@@ -33,14 +33,14 @@
|
||||
rightBump,
|
||||
aspectRatio,
|
||||
});
|
||||
const pathDisabled = createNodePath({
|
||||
depth: 0,
|
||||
height: 15,
|
||||
y: 50,
|
||||
cornerTop,
|
||||
rightBump,
|
||||
aspectRatio,
|
||||
});
|
||||
// const pathDisabled = createNodePath({
|
||||
// depth: 0,
|
||||
// height: 15,
|
||||
// y: 50,
|
||||
// cornerTop,
|
||||
// rightBump,
|
||||
// aspectRatio,
|
||||
// });
|
||||
const pathHover = createNodePath({
|
||||
depth: 8.5,
|
||||
height: 50,
|
||||
@@ -59,7 +59,7 @@
|
||||
class="click-target"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:mousedown={handleMouseDown}
|
||||
onmousedown={handleMouseDown}
|
||||
></div>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
isLast?: boolean;
|
||||
};
|
||||
|
||||
const { node = $bindable(), input, id, isLast }: Props = $props();
|
||||
const { node, input, id, isLast }: Props = $props();
|
||||
|
||||
const inputType = node?.tmp?.type?.inputs?.[id]!;
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
const graph = getGraphManager();
|
||||
const graphState = getGraphState();
|
||||
const graphId = graph?.id;
|
||||
const inputSockets = graph?.inputSockets;
|
||||
|
||||
const elementId = `input-${Math.random().toString(36).substring(7)}`;
|
||||
|
||||
@@ -83,7 +82,7 @@
|
||||
class:disabled={!graphState?.possibleSocketIds.has(socketId)}
|
||||
>
|
||||
{#key id && graphId}
|
||||
<div class="content" class:disabled={$inputSockets?.has(socketId)}>
|
||||
<div class="content" class:disabled={graph.inputSockets?.has(socketId)}>
|
||||
{#if inputType.label !== ""}
|
||||
<label for={elementId}>{input.label || id}</label>
|
||||
{/if}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from "svelte";
|
||||
import type { Writable } from "svelte/store";
|
||||
|
||||
let index = -1;
|
||||
let wrapper: HTMLDivElement;
|
||||
@@ -9,7 +8,7 @@
|
||||
index = getContext<() => number>("registerCell")();
|
||||
}
|
||||
|
||||
const sizes = getContext<Writable<string[]>>("sizes");
|
||||
const sizes = getContext<string[]>("sizes");
|
||||
|
||||
let downSizes: string[] = [];
|
||||
let downWidth = 0;
|
||||
@@ -17,7 +16,7 @@
|
||||
let startX = 0;
|
||||
|
||||
function handleMouseDown(event: MouseEvent) {
|
||||
downSizes = [...$sizes];
|
||||
downSizes = [...sizes];
|
||||
mouseDown = true;
|
||||
startX = event.clientX;
|
||||
downWidth = wrapper.getBoundingClientRect().width;
|
||||
@@ -26,8 +25,7 @@
|
||||
function handleMouseMove(event: MouseEvent) {
|
||||
if (mouseDown) {
|
||||
const width = downWidth + startX - event.clientX;
|
||||
$sizes[index] = `${width}px`;
|
||||
$sizes = $sizes;
|
||||
sizes[index] = `${width}px`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,27 +1,33 @@
|
||||
<script lang="ts">
|
||||
import { setContext, getContext } from "svelte";
|
||||
import localStore from "$lib/helpers/localStore";
|
||||
import { localState } from "$lib/helpers/localState.svelte";
|
||||
|
||||
const gridId = getContext<string>("grid-id") || "grid-0";
|
||||
let sizes = localStore<string[]>(gridId, []);
|
||||
let sizes = localState<string[]>(gridId, []);
|
||||
|
||||
const { children } = $props();
|
||||
|
||||
console.log("RowChildren", children);
|
||||
|
||||
let registerIndex = 0;
|
||||
setContext("registerCell", function () {
|
||||
let index = registerIndex;
|
||||
registerIndex++;
|
||||
if (registerIndex > $sizes.length) {
|
||||
$sizes = [...$sizes, "1fr"];
|
||||
if (registerIndex > sizes.length) {
|
||||
sizes = [...sizes, "1fr"];
|
||||
}
|
||||
return index;
|
||||
});
|
||||
|
||||
setContext("sizes", sizes);
|
||||
|
||||
$: cols = $sizes.map((size, i) => `${i > 0 ? "1px " : ""}` + size).join(" ");
|
||||
const cols = $derived(
|
||||
sizes.map((size, i) => `${i > 0 ? "1px " : ""}` + size).join(" "),
|
||||
);
|
||||
</script>
|
||||
|
||||
<div class="wrapper" style={`grid-template-columns: ${cols};`}>
|
||||
<slot />
|
||||
{@render children()}
|
||||
</div>
|
||||
|
||||
<style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
export function localState<T>(key: string, defaultValue: T): T {
|
||||
const stored = localStorage.getItem(key)
|
||||
const state = $state(stored ? JSON.parse(stored) : defaultValue)
|
||||
const stored = localStorage.getItem(key);
|
||||
const state = $state(stored ? JSON.parse(stored) : defaultValue);
|
||||
$effect.root(() => {
|
||||
$effect(() => {
|
||||
const value = $state.snapshot(state);
|
||||
|
||||
@@ -7,11 +7,15 @@
|
||||
import type { PerspectiveCamera, Vector3Tuple } from "three";
|
||||
import type { OrbitControls as OrbitControlsType } from "three/examples/jsm/controls/OrbitControls.js";
|
||||
|
||||
let camera: PerspectiveCamera;
|
||||
let controls: OrbitControlsType;
|
||||
let camera = $state<PerspectiveCamera>();
|
||||
let controls = $state<OrbitControlsType>();
|
||||
|
||||
export let center: Vector3;
|
||||
export let centerCamera: boolean = true;
|
||||
type Props = {
|
||||
center: Vector3;
|
||||
centerCamera: boolean;
|
||||
};
|
||||
|
||||
const { center, centerCamera }: Props = $props();
|
||||
|
||||
const cameraTransform = localStore<{
|
||||
camera: Vector3Tuple;
|
||||
@@ -22,7 +26,7 @@
|
||||
});
|
||||
|
||||
function saveCameraState() {
|
||||
if (!camera) return;
|
||||
if (!camera || !controls) return;
|
||||
let cPos = camera.position.toArray();
|
||||
let tPos = controls.target.toArray();
|
||||
// check if tPos is NaN or tPos is NaN
|
||||
@@ -35,6 +39,7 @@
|
||||
|
||||
let isRunning = false;
|
||||
const task = useTask(() => {
|
||||
if (!controls) return;
|
||||
let length = center.clone().sub(controls.target).length();
|
||||
if (length < 0.01 || !centerCamera) {
|
||||
isRunning = false;
|
||||
@@ -47,7 +52,8 @@
|
||||
});
|
||||
task.stop();
|
||||
|
||||
$: if (
|
||||
$effect(() => {
|
||||
if (
|
||||
center &&
|
||||
controls &&
|
||||
centerCamera &&
|
||||
@@ -59,10 +65,11 @@
|
||||
isRunning = true;
|
||||
task.start();
|
||||
}
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
controls.target.fromArray($cameraTransform.target);
|
||||
controls.update();
|
||||
controls?.target.fromArray($cameraTransform.target);
|
||||
controls?.update();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,7 +1,15 @@
|
||||
import { localState } from "$lib/helpers/localState.svelte";
|
||||
import type { NodeInput } from "@nodes/types";
|
||||
|
||||
const themes = ["dark", "light", "catppuccin", "solarized", "high-contrast", "nord", "dracula"];
|
||||
const themes = [
|
||||
"dark",
|
||||
"light",
|
||||
"catppuccin",
|
||||
"solarized",
|
||||
"high-contrast",
|
||||
"nord",
|
||||
"dracula",
|
||||
];
|
||||
|
||||
export const AppSettingTypes = {
|
||||
theme: {
|
||||
@@ -18,28 +26,33 @@ export const AppSettingTypes = {
|
||||
centerCamera: {
|
||||
type: "boolean",
|
||||
label: "Center Camera",
|
||||
value: true
|
||||
value: true,
|
||||
},
|
||||
nodeInterface: {
|
||||
title: "Node Interface",
|
||||
showNodeGrid: {
|
||||
type: "boolean",
|
||||
label: "Show Grid",
|
||||
value: true
|
||||
value: true,
|
||||
},
|
||||
snapToGrid: {
|
||||
type: "boolean",
|
||||
label: "Snap to Grid",
|
||||
value: true
|
||||
value: true,
|
||||
},
|
||||
showHelp: {
|
||||
type: "boolean",
|
||||
label: "Show Help",
|
||||
value: false
|
||||
}
|
||||
value: false,
|
||||
},
|
||||
},
|
||||
debug: {
|
||||
title: "Debug",
|
||||
amount: {
|
||||
type: "number",
|
||||
label: "Amount",
|
||||
value: 4,
|
||||
},
|
||||
wireframe: {
|
||||
type: "boolean",
|
||||
label: "Wireframe",
|
||||
@@ -81,30 +94,30 @@ export const AppSettingTypes = {
|
||||
type: "integer",
|
||||
min: 2,
|
||||
max: 15,
|
||||
value: 4
|
||||
value: 4,
|
||||
},
|
||||
loadGrid: {
|
||||
type: "button",
|
||||
label: "Load Grid"
|
||||
label: "Load Grid",
|
||||
},
|
||||
loadTree: {
|
||||
type: "button",
|
||||
label: "Load Tree"
|
||||
label: "Load Tree",
|
||||
},
|
||||
lottaFaces: {
|
||||
type: "button",
|
||||
label: "Load 'lots of faces'"
|
||||
label: "Load 'lots of faces'",
|
||||
},
|
||||
lottaNodes: {
|
||||
type: "button",
|
||||
label: "Load 'lots of nodes'"
|
||||
label: "Load 'lots of nodes'",
|
||||
},
|
||||
lottaNodesAndFaces: {
|
||||
type: "button",
|
||||
label: "Load 'lots of nodes and faces'"
|
||||
}
|
||||
label: "Load 'lots of nodes and faces'",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
} as const;
|
||||
|
||||
type IsInputDefinition<T> = T extends NodeInput ? T : never;
|
||||
@@ -118,10 +131,9 @@ type Widen<T> = T extends boolean
|
||||
? string
|
||||
: T;
|
||||
|
||||
|
||||
type ExtractSettingsValues<T> = {
|
||||
-readonly [K in keyof T]: T[K] extends HasTitle
|
||||
? ExtractSettingsValues<Omit<T[K], 'title'>>
|
||||
? ExtractSettingsValues<Omit<T[K], "title">>
|
||||
: T[K] extends IsInputDefinition<T[K]>
|
||||
? T[K] extends { value: infer V }
|
||||
? Widen<V>
|
||||
@@ -135,8 +147,8 @@ function settingsToStore<T>(settings: T): ExtractSettingsValues<T> {
|
||||
const result = {} as any;
|
||||
for (const key in settings) {
|
||||
const value = settings[key];
|
||||
if (value && typeof value === 'object') {
|
||||
if ('value' in value) {
|
||||
if (value && typeof value === "object") {
|
||||
if ("value" in value) {
|
||||
result[key] = value.value;
|
||||
} else {
|
||||
result[key] = settingsToStore(value);
|
||||
@@ -146,7 +158,10 @@ function settingsToStore<T>(settings: T): ExtractSettingsValues<T> {
|
||||
return result;
|
||||
}
|
||||
|
||||
export const appSettings = localState("app-settings", settingsToStore(AppSettingTypes));
|
||||
export let appSettings = localState(
|
||||
"app-settings",
|
||||
settingsToStore(AppSettingTypes),
|
||||
);
|
||||
|
||||
$effect.root(() => {
|
||||
$effect(() => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script lang="ts">
|
||||
import type { Node, NodeInput } from "@nodes/types";
|
||||
import NestedSettings from "$lib/settings/NestedSettings.svelte";
|
||||
import type { GraphManager } from "$lib/graph-interface/graph-manager";
|
||||
import type { GraphManager } from "$lib/graph-interface/graph-manager.svelte";
|
||||
|
||||
type Props = {
|
||||
manager: GraphManager;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import type { Node } from "@nodes/types";
|
||||
import type { GraphManager } from "$lib/graph-interface/graph-manager";
|
||||
import type { GraphManager } from "$lib/graph-interface/graph-manager.svelte";
|
||||
import ActiveNodeSelected from "./ActiveNodeSelected.svelte";
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -26,8 +26,6 @@
|
||||
import { IndexDBCache, RemoteNodeRegistry } from "@nodes/registry";
|
||||
import { createPerformanceStore } from "@nodes/utils";
|
||||
import BenchmarkPanel from "$lib/sidebar/panels/BenchmarkPanel.svelte";
|
||||
import { debounceAsyncFunction } from "$lib/helpers";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
let performanceStore = createPerformanceStore();
|
||||
|
||||
@@ -45,14 +43,15 @@
|
||||
let activeNode = $state<Node | undefined>(undefined);
|
||||
let scene = $state<Group>(null!);
|
||||
|
||||
let graph = localStorage.getItem("graph")
|
||||
let graph = $state(
|
||||
localStorage.getItem("graph")
|
||||
? JSON.parse(localStorage.getItem("graph")!)
|
||||
: templates.defaultPlant;
|
||||
: templates.defaultPlant,
|
||||
);
|
||||
|
||||
let graphInterface = $state<ReturnType<typeof GraphInterface>>(null!);
|
||||
let viewerComponent = $state<ReturnType<typeof Viewer>>();
|
||||
const manager = $derived(graphInterface?.manager);
|
||||
const managerStatus = $derived(manager?.status);
|
||||
|
||||
async function randomGenerate() {
|
||||
if (!manager) return;
|
||||
@@ -69,16 +68,29 @@
|
||||
},
|
||||
]);
|
||||
let graphSettings = $state<Record<string, any>>({});
|
||||
let graphSettingTypes = $state({
|
||||
type BooleanSchema = {
|
||||
[key: string]: {
|
||||
type: "boolean";
|
||||
value: false;
|
||||
};
|
||||
};
|
||||
let graphSettingTypes = $state<BooleanSchema>({
|
||||
randomSeed: { type: "boolean", value: false },
|
||||
});
|
||||
|
||||
const handleUpdate = debounceAsyncFunction(
|
||||
async (g: Graph, s: Record<string, any> = graphSettings) => {
|
||||
let runIndex = 0;
|
||||
const handleUpdate = async (
|
||||
g: Graph,
|
||||
s: Record<string, any> = graphSettings,
|
||||
) => {
|
||||
runIndex++;
|
||||
performanceStore.startRun();
|
||||
try {
|
||||
let a = performance.now();
|
||||
const graphResult = await runtime.execute(g, $state.snapshot(s));
|
||||
const graphResult = await runtime.execute(
|
||||
$state.snapshot(g),
|
||||
$state.snapshot(s),
|
||||
);
|
||||
let b = performance.now();
|
||||
|
||||
if (appSettings.debug.useWorker) {
|
||||
@@ -94,49 +106,48 @@
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
viewerComponent?.update(graphResult);
|
||||
} catch (error) {
|
||||
console.log("errors", error);
|
||||
} finally {
|
||||
performanceStore.stopRun();
|
||||
}
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// $ if (AppSettings) {
|
||||
// //@ts-ignore
|
||||
// AppSettingTypes.debug.stressTest.loadGrid.callback = () => {
|
||||
// graph = templates.grid($AppSettings.amount, $AppSettings.amount);
|
||||
// };
|
||||
// //@ts-ignore
|
||||
// AppSettingTypes.debug.stressTest.loadTree.callback = () => {
|
||||
// graph = templates.tree($AppSettings.amount);
|
||||
// };
|
||||
// //@ts-ignore
|
||||
// AppSettingTypes.debug.stressTest.lottaFaces.callback = () => {
|
||||
// graph = templates.lottaFaces;
|
||||
// };
|
||||
// //@ts-ignore
|
||||
// AppSettingTypes.debug.stressTest.lottaNodes.callback = () => {
|
||||
// graph = templates.lottaNodes;
|
||||
// };
|
||||
// //@ts-ignore
|
||||
// AppSettingTypes.debug.stressTest.lottaNodesAndFaces.callback = () => {
|
||||
// graph = templates.lottaNodesAndFaces;
|
||||
// };
|
||||
// }
|
||||
$effect(() => {
|
||||
//@ts-ignore
|
||||
AppSettingTypes.debug.stressTest.loadGrid.callback = () => {
|
||||
graph = templates.grid(
|
||||
appSettings.debug.amount.value,
|
||||
appSettings.debug.amount.value,
|
||||
);
|
||||
};
|
||||
//@ts-ignore
|
||||
AppSettingTypes.debug.stressTest.loadTree.callback = () => {
|
||||
graph = templates.tree(appSettings.debug.amount.value);
|
||||
};
|
||||
//@ts-ignore
|
||||
AppSettingTypes.debug.stressTest.lottaFaces.callback = () => {
|
||||
graph = templates.lottaFaces;
|
||||
};
|
||||
//@ts-ignore
|
||||
AppSettingTypes.debug.stressTest.lottaNodes.callback = () => {
|
||||
graph = templates.lottaNodes;
|
||||
};
|
||||
//@ts-ignore
|
||||
AppSettingTypes.debug.stressTest.lottaNodesAndFaces.callback = () => {
|
||||
graph = templates.lottaNodesAndFaces;
|
||||
};
|
||||
});
|
||||
|
||||
function handleSave(graph: Graph) {
|
||||
localStorage.setItem("graph", JSON.stringify(graph));
|
||||
}
|
||||
onMount(() => {
|
||||
handleUpdate(graph);
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:document on:keydown={applicationKeymap.handleKeyboardEvent} />
|
||||
<div class="wrapper manager-{$managerStatus}">
|
||||
<svelte:document onkeydown={applicationKeymap.handleKeyboardEvent} />
|
||||
|
||||
<div class="wrapper manager-{manager?.status}">
|
||||
<header></header>
|
||||
<Grid.Row>
|
||||
<Grid.Cell>
|
||||
@@ -148,10 +159,9 @@
|
||||
/>
|
||||
</Grid.Cell>
|
||||
<Grid.Cell>
|
||||
{#key graph}
|
||||
<GraphInterface
|
||||
bind:this={graphInterface}
|
||||
{graph}
|
||||
bind:this={graphInterface}
|
||||
registry={nodeRegistry}
|
||||
showGrid={appSettings.nodeInterface.showNodeGrid}
|
||||
snapToGrid={appSettings.nodeInterface.snapToGrid}
|
||||
@@ -234,7 +244,6 @@
|
||||
<ActiveNodeSettings {manager} node={activeNode} />
|
||||
</Panel>
|
||||
</Sidebar>
|
||||
{/key}
|
||||
</Grid.Cell>
|
||||
</Grid.Row>
|
||||
</div>
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
decodeFloat,
|
||||
encodeFloat,
|
||||
decodeNestedArray,
|
||||
encodeNestedArray,
|
||||
concatEncodedArrays,
|
||||
} from "@nodes/utils";
|
||||
|
||||
console.clear();
|
||||
|
||||
{
|
||||
const encodedPositions = new Int32Array([
|
||||
encodeFloat(1.1),
|
||||
encodeFloat(2.0),
|
||||
encodeFloat(3.0),
|
||||
encodeFloat(4.0),
|
||||
encodeFloat(5.0),
|
||||
encodeFloat(6.0),
|
||||
encodeFloat(7.0),
|
||||
encodeFloat(8.0),
|
||||
encodeFloat(9.0),
|
||||
]);
|
||||
|
||||
// Create a Float32Array using the same buffer that backs the Int32Array
|
||||
const floatView = new Float32Array(encodedPositions.buffer);
|
||||
console.log({ encodedPositions, floatView });
|
||||
}
|
||||
|
||||
if (false) {
|
||||
const input_a = encodeNestedArray([1, 2, 3]);
|
||||
const input_b = 2;
|
||||
const input_c = 89;
|
||||
const input_d = encodeNestedArray([4, 5, 6]);
|
||||
|
||||
const output = concatNestedArrays([input_a, input_b, input_c, input_d]);
|
||||
|
||||
const decoded = decodeNestedArray(output);
|
||||
console.log("CONCAT", [input_a, input_b, input_c, input_d]);
|
||||
console.log(output);
|
||||
console.log(decoded);
|
||||
}
|
||||
|
||||
if (false) {
|
||||
let maxError = 0;
|
||||
new Array(10_000).fill(null).forEach((v, i) => {
|
||||
const input = i < 5_000 ? i : Math.random() * 100;
|
||||
const encoded = encodeFloat(input);
|
||||
const output = decodeFloat(encoded[0], encoded[1]);
|
||||
|
||||
const error = Math.abs(input - output);
|
||||
if (error > maxError) {
|
||||
maxError = error;
|
||||
}
|
||||
});
|
||||
|
||||
console.log("DECODE FLOAT");
|
||||
console.log(maxError);
|
||||
console.log(encodeFloat(2.0));
|
||||
console.log("----");
|
||||
}
|
||||
|
||||
if (false) {
|
||||
console.log("Turning Int32Array into Array");
|
||||
const test_size = 2_000_000;
|
||||
const a = new Int32Array(test_size);
|
||||
let t0 = performance.now();
|
||||
for (let i = 0; i < test_size; i++) {
|
||||
a[i] = Math.floor(Math.random() * 100);
|
||||
}
|
||||
console.log("TIME", performance.now() - t0);
|
||||
t0 = performance.now();
|
||||
const b = [...a.slice(0, test_size)];
|
||||
console.log("TIME", performance.now() - t0);
|
||||
console.log(typeof b, Array.isArray(b), b instanceof Int32Array);
|
||||
}
|
||||
|
||||
if (false) {
|
||||
// const input = [5, [6, 1], [7, 2, [5, 1]]];
|
||||
// const input = [5, [], [6, []], []];
|
||||
// const input = [52];
|
||||
const input = [0, 0, [0, 2, 0, 128, 0, 128], 0, 128];
|
||||
|
||||
console.log("INPUT");
|
||||
console.log(input);
|
||||
|
||||
let encoded = encodeNestedArray(input);
|
||||
// encoded = [];
|
||||
console.log("ENCODED");
|
||||
console.log(encoded);
|
||||
|
||||
encoded = [0, 2, 1, 0, 4, 4, 2, 4, 1, 2, 2, 0, 3, 2, 3, 1, 1, 1, 1];
|
||||
|
||||
const decoded = decodeNestedArray(encoded);
|
||||
console.log("DECODED");
|
||||
console.log(decoded);
|
||||
|
||||
console.log("EQUALS", JSON.stringify(input) === JSON.stringify(decoded));
|
||||
}
|
||||
</script>
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Graph, NodeDefinition, NodeId } from "./types";
|
||||
import { Graph, NodeDefinition, NodeType } from "./types";
|
||||
|
||||
export interface NodeRegistry {
|
||||
/**
|
||||
@@ -13,13 +13,13 @@ export interface NodeRegistry {
|
||||
* @throws An error if the nodes could not be loaded
|
||||
* @remarks This method should be called before calling getNode or getAllNodes
|
||||
*/
|
||||
load: (nodeIds: NodeId[]) => Promise<NodeDefinition[]>;
|
||||
load: (nodeIds: NodeType[]) => Promise<NodeDefinition[]>;
|
||||
/**
|
||||
* Get a node by id
|
||||
* @param id - The id of the node to get
|
||||
* @returns The node with the given id, or undefined if no such node exists
|
||||
*/
|
||||
getNode: (id: NodeId | string) => NodeDefinition | undefined;
|
||||
getNode: (id: NodeType | string) => NodeDefinition | undefined;
|
||||
/**
|
||||
* Get all nodes
|
||||
* @returns An array of all nodes
|
||||
|
||||
@@ -9,9 +9,9 @@ export type {
|
||||
Node,
|
||||
NodeDefinition,
|
||||
Socket,
|
||||
NodeType as NodeId,
|
||||
NodeType,
|
||||
Edge,
|
||||
Graph,
|
||||
} from "./types";
|
||||
export { NodeSchema, GraphSchema, NodeType } from "./types";
|
||||
export { NodeSchema, GraphSchema } from "./types";
|
||||
export { NodeDefinitionSchema } from "./types";
|
||||
|
||||
@@ -44,7 +44,7 @@ export type Node = {
|
||||
} & z.infer<typeof NodeSchema>;
|
||||
|
||||
export const NodeDefinitionSchema = z.object({
|
||||
id: z.string(),
|
||||
id: NodeTypeSchema,
|
||||
inputs: z.record(z.string(), NodeInputSchema).optional(),
|
||||
outputs: z.array(z.string()).optional(),
|
||||
meta: z
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
ctrl?: boolean;
|
||||
shift?: boolean;
|
||||
alt?: boolean;
|
||||
key: string;
|
||||
key: string | string[];
|
||||
}
|
||||
|
||||
let {
|
||||
|
||||
Reference in New Issue
Block a user