feat: migrate most of graph-manager to svelte-5
Some checks failed
Deploy to GitHub Pages / build_site (push) Failing after 2m44s
Some checks failed
Deploy to GitHub Pages / build_site (push) Failing after 2m44s
This commit is contained in:
parent
fa659ab74e
commit
4f03f2af5a
@ -1,16 +1,23 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { HTML } from "@threlte/extras";
|
import { HTML } from "@threlte/extras";
|
||||||
|
|
||||||
export let p1 = { x: 0, y: 0 };
|
type Props = {
|
||||||
export let p2 = { x: 0, y: 0 };
|
p1: { x: number; y: number };
|
||||||
|
p2: { x: number; y: number };
|
||||||
|
cameraPosition: [number, number, number];
|
||||||
|
};
|
||||||
|
|
||||||
export let cameraPosition = [0, 1, 0];
|
const {
|
||||||
|
p1 = { x: 0, y: 0 },
|
||||||
|
p2 = { x: 0, y: 0 },
|
||||||
|
cameraPosition = [0, 1, 0],
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
$: width = Math.abs(p1.x - p2.x) * cameraPosition[2];
|
const width = $derived(Math.abs(p1.x - p2.x) * cameraPosition[2]);
|
||||||
$: height = Math.abs(p1.y - p2.y) * cameraPosition[2];
|
const height = $derived(Math.abs(p1.y - p2.y) * cameraPosition[2]);
|
||||||
|
|
||||||
$: x = Math.max(p1.x, p2.x) - width / cameraPosition[2];
|
const x = $derived(Math.max(p1.x, p2.x) - width / cameraPosition[2]);
|
||||||
$: y = Math.max(p1.y, p2.y) - height / cameraPosition[2];
|
const y = $derived(Math.max(p1.y, p2.y) - height / cameraPosition[2]);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<HTML position.x={x} position.z={y} transform={false}>
|
<HTML position.x={x} position.z={y} transform={false}>
|
||||||
|
@ -1,18 +1,20 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { T } from '@threlte/core';
|
import { T } from "@threlte/core";
|
||||||
import { type OrthographicCamera } from 'three';
|
import { type OrthographicCamera } from "three";
|
||||||
|
type Props = {
|
||||||
|
camera: OrthographicCamera;
|
||||||
|
position: [number, number, number];
|
||||||
|
};
|
||||||
|
|
||||||
export let camera: OrthographicCamera | undefined = undefined;
|
let { camera = $bindable(), position }: Props = $props();
|
||||||
|
|
||||||
export let position: [number, number, number];
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<T.OrthographicCamera
|
<T.OrthographicCamera
|
||||||
bind:ref={camera}
|
bind:ref={camera}
|
||||||
position.x={position[0]}
|
position.x={position[0]}
|
||||||
position.y={10}
|
position.y={10}
|
||||||
position.z={position[1]}
|
position.z={position[1]}
|
||||||
rotation.x={-Math.PI / 2}
|
rotation.x={-Math.PI / 2}
|
||||||
zoom={position[2]}
|
zoom={position[2]}
|
||||||
makeDefault
|
makeDefault
|
||||||
/>
|
/>
|
||||||
|
@ -2,16 +2,17 @@
|
|||||||
import type { NodeDefinition, NodeRegistry } from "@nodes/types";
|
import type { NodeDefinition, NodeRegistry } from "@nodes/types";
|
||||||
import { onMount } from "svelte";
|
import { onMount } from "svelte";
|
||||||
|
|
||||||
let mx = 0;
|
let mx = $state(0);
|
||||||
let my = 0;
|
let my = $state(0);
|
||||||
|
|
||||||
let node: NodeDefinition | undefined = undefined;
|
let node: NodeDefinition | undefined = $state(undefined);
|
||||||
let input: string | undefined = undefined;
|
let input: string | undefined = $state(undefined);
|
||||||
|
|
||||||
let wrapper: HTMLDivElement;
|
let wrapper: HTMLDivElement;
|
||||||
|
type Props = { registry: NodeRegistry };
|
||||||
|
const { registry }: Props = $props();
|
||||||
|
|
||||||
export let registry: NodeRegistry;
|
let width = $state(0);
|
||||||
let width = 0;
|
|
||||||
|
|
||||||
function handleMouseOver(ev: MouseEvent) {
|
function handleMouseOver(ev: MouseEvent) {
|
||||||
let target = ev.target as HTMLElement | null;
|
let target = ev.target as HTMLElement | null;
|
||||||
@ -45,7 +46,7 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<svelte:window on:mousemove={handleMouseOver} />
|
<svelte:window onmousemove={handleMouseOver} />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="help-wrapper p-4"
|
class="help-wrapper p-4"
|
||||||
|
@ -3,24 +3,27 @@
|
|||||||
|
|
||||||
import BackgroundVert from "./Background.vert";
|
import BackgroundVert from "./Background.vert";
|
||||||
import BackgroundFrag from "./Background.frag";
|
import BackgroundFrag from "./Background.frag";
|
||||||
import { colors } from "../graph/stores.js";
|
import { colors } from "../graph/state.svelte";
|
||||||
import { Color } from "three";
|
import { Color } from "three";
|
||||||
|
|
||||||
export let minZoom = 4;
|
type Props = {
|
||||||
export let maxZoom = 150;
|
minZoom: number;
|
||||||
|
maxZoom: number;
|
||||||
|
cameraPosition: [number, number, number];
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
};
|
||||||
|
|
||||||
export let cameraPosition: [number, number, number] = [0, 1, 0];
|
let {
|
||||||
|
minZoom = 4,
|
||||||
|
maxZoom = 150,
|
||||||
|
cameraPosition = [0, 1, 0],
|
||||||
|
width = globalThis?.innerWidth || 100,
|
||||||
|
height = globalThis?.innerHeight || 100,
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
export let width = globalThis?.innerWidth || 100;
|
let bw = $derived(width / cameraPosition[2]);
|
||||||
export let height = globalThis?.innerHeight || 100;
|
let bh = $derived(height / cameraPosition[2]);
|
||||||
|
|
||||||
let bw = 2;
|
|
||||||
let bh = 2;
|
|
||||||
|
|
||||||
$: if (width && height) {
|
|
||||||
bw = width / cameraPosition[2];
|
|
||||||
bh = height / cameraPosition[2];
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<T.Group
|
<T.Group
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script context="module" lang="ts">
|
<script module lang="ts">
|
||||||
import { colors } from "../graph/stores";
|
import { colors } from "../graph/state.svelte";
|
||||||
|
|
||||||
const circleMaterial = new MeshBasicMaterial({
|
const circleMaterial = new MeshBasicMaterial({
|
||||||
color: get(colors).edge,
|
color: get(colors).edge,
|
||||||
@ -29,19 +29,23 @@
|
|||||||
import { createEdgeGeometry } from "./createEdgeGeometry.js";
|
import { createEdgeGeometry } from "./createEdgeGeometry.js";
|
||||||
import { get } from "svelte/store";
|
import { get } from "svelte/store";
|
||||||
|
|
||||||
export let from: { x: number; y: number };
|
type Props = {
|
||||||
export let to: { x: number; y: number };
|
from: { x: number; y: number };
|
||||||
|
to: { x: number; y: number };
|
||||||
|
};
|
||||||
|
|
||||||
|
const { from, to }: Props = $props();
|
||||||
|
|
||||||
let samples = 5;
|
let samples = 5;
|
||||||
|
|
||||||
let geometry: BufferGeometry;
|
let geometry: BufferGeometry|null = $state(null);
|
||||||
|
|
||||||
let lastId: number | null = null;
|
let lastId: number | null = null;
|
||||||
|
|
||||||
const primeA = 31;
|
const primeA = 31;
|
||||||
const primeB = 37;
|
const primeB = 37;
|
||||||
|
|
||||||
export const update = function () {
|
function update() {
|
||||||
const new_x = to.x - from.x;
|
const new_x = to.x - from.x;
|
||||||
const new_y = to.y - from.y;
|
const new_y = to.y - from.y;
|
||||||
const curveId = new_x * primeA + new_y * primeB;
|
const curveId = new_x * primeA + new_y * primeB;
|
||||||
@ -75,11 +79,13 @@
|
|||||||
lineCache.set(curveId, geometry);
|
lineCache.set(curveId, geometry);
|
||||||
};
|
};
|
||||||
|
|
||||||
$: if (from || to) {
|
$effect(() => {
|
||||||
update();
|
if (from || to) {
|
||||||
}
|
update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$: lineColor = $colors["edge"].clone().convertSRGBToLinear();
|
const lineColor = $derived($colors.edge.clone().convertSRGBToLinear());
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<T.Mesh
|
<T.Mesh
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Edge from "./Edge.svelte";
|
import Edge from "./Edge.svelte";
|
||||||
|
|
||||||
export let from: { x: number; y: number };
|
type Props = { from: { x: number; y: number }; to: { x: number; y: number } };
|
||||||
export let to: { x: number; y: number };
|
const { from, to }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Edge {from} {to} />
|
<Edge {from} {to} />
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { writable, type Writable } from "svelte/store";
|
import type { Edge, Graph, Node, NodeInput, NodeRegistry, Socket, } from "@nodes/types";
|
||||||
import type { Graph, Node, Edge, Socket, NodeRegistry, } from "@nodes/types";
|
|
||||||
import { HistoryManager } from "./history-manager.js"
|
|
||||||
import EventEmitter from "./helpers/EventEmitter.js";
|
|
||||||
import throttle from "./helpers/throttle.js";
|
|
||||||
import { createLogger } from "./helpers/index.js";
|
|
||||||
import type { NodeInput } from "@nodes/types";
|
|
||||||
import { fastHashString } from "@nodes/utils";
|
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";
|
||||||
|
|
||||||
const logger = createLogger("graph-manager");
|
const logger = createLogger("graph-manager");
|
||||||
|
|
||||||
@ -68,6 +67,7 @@ export class GraphManager extends EventEmitter<{ "save": Graph, "result": any, "
|
|||||||
const edges = this._edges.map(edge => [edge[0].id, edge[1], edge[2].id, edge[3]]) as Graph["edges"];
|
const edges = this._edges.map(edge => [edge[0].id, edge[1], edge[2].id, edge[3]]) as Graph["edges"];
|
||||||
const serialized = { id: this.graph.id, settings: this.settings, nodes, edges };
|
const serialized = { id: this.graph.id, settings: this.settings, nodes, edges };
|
||||||
logger.groupEnd();
|
logger.groupEnd();
|
||||||
|
console.log({ serialized });
|
||||||
|
|
||||||
return clone(serialized);
|
return clone(serialized);
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
lerp,
|
lerp,
|
||||||
snapToGrid as snapPointToGrid,
|
snapToGrid as snapPointToGrid,
|
||||||
} from "../helpers/index.js";
|
} from "../helpers/index.js";
|
||||||
import { Canvas } from "@threlte/core";
|
|
||||||
import type { OrthographicCamera } from "three";
|
import type { OrthographicCamera } from "three";
|
||||||
import Background from "../background/Background.svelte";
|
import Background from "../background/Background.svelte";
|
||||||
import type { GraphManager } from "../graph-manager.js";
|
import type { GraphManager } from "../graph-manager.js";
|
||||||
@ -14,20 +13,16 @@
|
|||||||
import type { Node, NodeId, Node as NodeType, Socket } from "@nodes/types";
|
import type { Node, NodeId, Node as NodeType, Socket } from "@nodes/types";
|
||||||
import { GraphSchema } from "@nodes/types";
|
import { GraphSchema } from "@nodes/types";
|
||||||
import FloatingEdge from "../edges/FloatingEdge.svelte";
|
import FloatingEdge from "../edges/FloatingEdge.svelte";
|
||||||
import {
|
import { getGraphState } from "./state.svelte";
|
||||||
activeNodeId,
|
|
||||||
activeSocket,
|
|
||||||
hoveredSocket,
|
|
||||||
possibleSockets,
|
|
||||||
possibleSocketIds,
|
|
||||||
selectedNodes,
|
|
||||||
} from "./stores.js";
|
|
||||||
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 HelpView from "../HelpView.svelte";
|
import HelpView from "../HelpView.svelte";
|
||||||
import FileSaver from "file-saver";
|
import FileSaver from "file-saver";
|
||||||
|
import { Canvas } from "@threlte/core";
|
||||||
|
|
||||||
|
const state = getGraphState();
|
||||||
|
|
||||||
export let manager: GraphManager;
|
export let manager: GraphManager;
|
||||||
|
|
||||||
@ -184,7 +179,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
setContext("setDownSocket", (socket: Socket) => {
|
setContext("setDownSocket", (socket: Socket) => {
|
||||||
$activeSocket = socket;
|
state.activeSocket = socket;
|
||||||
|
|
||||||
let { node, index, position } = socket;
|
let { node, index, position } = socket;
|
||||||
|
|
||||||
@ -203,14 +198,14 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
mouseDown = position;
|
mouseDown = position;
|
||||||
$activeSocket = {
|
state.activeSocket = {
|
||||||
node,
|
node,
|
||||||
index,
|
index,
|
||||||
position,
|
position,
|
||||||
};
|
};
|
||||||
|
|
||||||
$possibleSockets = manager
|
state.possibleSockets = manager
|
||||||
.getPossibleSockets($activeSocket)
|
.getPossibleSockets(state.activeSocket)
|
||||||
.map(([node, index]) => {
|
.map(([node, index]) => {
|
||||||
return {
|
return {
|
||||||
node,
|
node,
|
||||||
@ -218,9 +213,6 @@
|
|||||||
position: getSocketPosition(node, index),
|
position: getSocketPosition(node, index),
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
$possibleSocketIds = new Set(
|
|
||||||
$possibleSockets.map((s) => `${s.node.id}-${s.index}`),
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function getSnapLevel() {
|
function getSnapLevel() {
|
||||||
@ -271,10 +263,10 @@
|
|||||||
if (!mouseDown) return;
|
if (!mouseDown) return;
|
||||||
|
|
||||||
// we are creating a new edge here
|
// we are creating a new edge here
|
||||||
if ($activeSocket || $possibleSockets?.length) {
|
if (state.activeSocket || state.possibleSockets?.length) {
|
||||||
let smallestDist = 1000;
|
let smallestDist = 1000;
|
||||||
let _socket;
|
let _socket;
|
||||||
for (const socket of $possibleSockets) {
|
for (const socket of state.possibleSockets) {
|
||||||
const dist = Math.sqrt(
|
const dist = Math.sqrt(
|
||||||
(socket.position[0] - mousePosition[0]) ** 2 +
|
(socket.position[0] - mousePosition[0]) ** 2 +
|
||||||
(socket.position[1] - mousePosition[1]) ** 2,
|
(socket.position[1] - mousePosition[1]) ** 2,
|
||||||
@ -287,9 +279,9 @@
|
|||||||
|
|
||||||
if (_socket && smallestDist < 0.9) {
|
if (_socket && smallestDist < 0.9) {
|
||||||
mousePosition = _socket.position;
|
mousePosition = _socket.position;
|
||||||
$hoveredSocket = _socket;
|
state.hoveredSocket = _socket;
|
||||||
} else {
|
} else {
|
||||||
$hoveredSocket = null;
|
state.hoveredSocket = null;
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -309,18 +301,17 @@
|
|||||||
const y = node.position[1];
|
const y = node.position[1];
|
||||||
const height = getNodeHeight(node.type);
|
const height = getNodeHeight(node.type);
|
||||||
if (x > x1 - 20 && x < x2 && y > y1 - height && y < y2) {
|
if (x > x1 - 20 && x < x2 && y > y1 - height && y < y2) {
|
||||||
$selectedNodes?.add(node.id);
|
state.selectedNodes?.add(node.id);
|
||||||
} else {
|
} else {
|
||||||
$selectedNodes?.delete(node.id);
|
state.selectedNodes?.delete(node.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$selectedNodes = $selectedNodes;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// here we are handling dragging of nodes
|
// here we are handling dragging of nodes
|
||||||
if ($activeNodeId !== -1 && mouseDownId !== -1) {
|
if (state.activeNodeId !== -1 && mouseDownId !== -1) {
|
||||||
const node = manager.getNode($activeNodeId);
|
const node = manager.getNode(state.activeNodeId);
|
||||||
if (!node || event.buttons !== 1) return;
|
if (!node || event.buttons !== 1) return;
|
||||||
|
|
||||||
node.tmp = node.tmp || {};
|
node.tmp = node.tmp || {};
|
||||||
@ -349,8 +340,8 @@
|
|||||||
const vecX = oldX - newX;
|
const vecX = oldX - newX;
|
||||||
const vecY = oldY - newY;
|
const vecY = oldY - newY;
|
||||||
|
|
||||||
if ($selectedNodes?.size) {
|
if (state.selectedNodes?.size) {
|
||||||
for (const nodeId of $selectedNodes) {
|
for (const nodeId of state.selectedNodes) {
|
||||||
const n = manager.getNode(nodeId);
|
const n = manager.getNode(nodeId);
|
||||||
if (!n?.tmp) continue;
|
if (!n?.tmp) continue;
|
||||||
n.tmp.x = (n?.tmp?.downX || 0) - vecX;
|
n.tmp.x = (n?.tmp?.downX || 0) - vecX;
|
||||||
@ -433,44 +424,43 @@
|
|||||||
|
|
||||||
// if we clicked on a node
|
// if we clicked on a node
|
||||||
if (clickedNodeId !== -1) {
|
if (clickedNodeId !== -1) {
|
||||||
if ($activeNodeId === -1) {
|
if (state.activeNodeId === -1) {
|
||||||
$activeNodeId = clickedNodeId;
|
state.activeNodeId = clickedNodeId;
|
||||||
// if the selected node is the same as the clicked node
|
// if the selected node is the same as the clicked node
|
||||||
} else if ($activeNodeId === clickedNodeId) {
|
} else if (state.activeNodeId === clickedNodeId) {
|
||||||
//$activeNodeId = -1;
|
//$activeNodeId = -1;
|
||||||
// if the clicked node is different from the selected node and secondary
|
// if the clicked node is different from the selected node and secondary
|
||||||
} else if (event.ctrlKey) {
|
} else if (event.ctrlKey) {
|
||||||
$selectedNodes = $selectedNodes || new Set();
|
state.selectedNodes = state.selectedNodes || new Set();
|
||||||
$selectedNodes.add($activeNodeId);
|
state.selectedNodes.add(state.activeNodeId);
|
||||||
$selectedNodes.delete(clickedNodeId);
|
state.selectedNodes.delete(clickedNodeId);
|
||||||
$activeNodeId = clickedNodeId;
|
state.activeNodeId = clickedNodeId;
|
||||||
// select the node
|
// select the node
|
||||||
} else if (event.shiftKey) {
|
} else if (event.shiftKey) {
|
||||||
const activeNode = manager.getNode($activeNodeId);
|
const activeNode = manager.getNode(state.activeNodeId);
|
||||||
const newNode = manager.getNode(clickedNodeId);
|
const newNode = manager.getNode(clickedNodeId);
|
||||||
if (activeNode && newNode) {
|
if (activeNode && newNode) {
|
||||||
const edge = manager.getNodesBetween(activeNode, newNode);
|
const edge = manager.getNodesBetween(activeNode, newNode);
|
||||||
if (edge) {
|
if (edge) {
|
||||||
const selected = new Set(edge.map((n) => n.id));
|
const selected = new Set(edge.map((n) => n.id));
|
||||||
selected.add(clickedNodeId);
|
selected.add(clickedNodeId);
|
||||||
$selectedNodes = selected;
|
state.selectedNodes = selected;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!$selectedNodes?.has(clickedNodeId)) {
|
} else if (!state.selectedNodes?.has(clickedNodeId)) {
|
||||||
$activeNodeId = clickedNodeId;
|
state.activeNodeId = clickedNodeId;
|
||||||
$selectedNodes?.clear();
|
state.clearSelection();
|
||||||
$selectedNodes = $selectedNodes;
|
|
||||||
}
|
}
|
||||||
} else if (event.ctrlKey) {
|
} else if (event.ctrlKey) {
|
||||||
boxSelection = true;
|
boxSelection = true;
|
||||||
}
|
}
|
||||||
const node = manager.getNode($activeNodeId);
|
const node = manager.getNode(state.activeNodeId);
|
||||||
if (!node) return;
|
if (!node) return;
|
||||||
node.tmp = node.tmp || {};
|
node.tmp = node.tmp || {};
|
||||||
node.tmp.downX = node.position[0];
|
node.tmp.downX = node.position[0];
|
||||||
node.tmp.downY = node.position[1];
|
node.tmp.downY = node.position[1];
|
||||||
if ($selectedNodes) {
|
if (state.selectedNodes) {
|
||||||
for (const nodeId of $selectedNodes) {
|
for (const nodeId of state.selectedNodes) {
|
||||||
const n = manager.getNode(nodeId);
|
const n = manager.getNode(nodeId);
|
||||||
if (!n) continue;
|
if (!n) continue;
|
||||||
n.tmp = n.tmp || {};
|
n.tmp = n.tmp || {};
|
||||||
@ -481,8 +471,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function copyNodes() {
|
function copyNodes() {
|
||||||
if ($activeNodeId === -1 && !$selectedNodes?.size) return;
|
if (state.activeNodeId === -1 && !state.selectedNodes?.size) return;
|
||||||
let _nodes = [$activeNodeId, ...($selectedNodes?.values() || [])]
|
let _nodes = [state.activeNodeId, ...(state.selectedNodes?.values() || [])]
|
||||||
.map((id) => manager.getNode(id))
|
.map((id) => manager.getNode(id))
|
||||||
.filter(Boolean) as Node[];
|
.filter(Boolean) as Node[];
|
||||||
|
|
||||||
@ -518,7 +508,7 @@
|
|||||||
.filter(Boolean) as Node[];
|
.filter(Boolean) as Node[];
|
||||||
|
|
||||||
const newNodes = manager.createGraph(_nodes, clipboard.edges);
|
const newNodes = manager.createGraph(_nodes, clipboard.edges);
|
||||||
$selectedNodes = new Set(newNodes.map((n) => n.id));
|
state.selectedNodes = new Set(newNodes.map((n) => n.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
const isBodyFocused = () => document?.activeElement?.nodeName !== "INPUT";
|
const isBodyFocused = () => document?.activeElement?.nodeName !== "INPUT";
|
||||||
@ -527,10 +517,10 @@
|
|||||||
key: "l",
|
key: "l",
|
||||||
description: "Select linked nodes",
|
description: "Select linked nodes",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
const activeNode = manager.getNode($activeNodeId);
|
const activeNode = manager.getNode(state.activeNodeId);
|
||||||
if (activeNode) {
|
if (activeNode) {
|
||||||
const nodes = manager.getLinkedNodes(activeNode);
|
const nodes = manager.getLinkedNodes(activeNode);
|
||||||
$selectedNodes = new Set(nodes.map((n) => n.id));
|
state.selectedNodes = new Set(nodes.map((n) => n.id));
|
||||||
}
|
}
|
||||||
console.log(activeNode);
|
console.log(activeNode);
|
||||||
},
|
},
|
||||||
@ -562,9 +552,8 @@
|
|||||||
key: "Escape",
|
key: "Escape",
|
||||||
description: "Deselect nodes",
|
description: "Deselect nodes",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
$activeNodeId = -1;
|
state.activeNodeId = -1;
|
||||||
$selectedNodes?.clear();
|
state.clearSelection();
|
||||||
$selectedNodes = $selectedNodes;
|
|
||||||
(document.activeElement as HTMLElement)?.blur();
|
(document.activeElement as HTMLElement)?.blur();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@ -616,7 +605,7 @@
|
|||||||
description: "Select all nodes",
|
description: "Select all nodes",
|
||||||
callback: () => {
|
callback: () => {
|
||||||
if (!isBodyFocused()) return;
|
if (!isBodyFocused()) return;
|
||||||
$selectedNodes = new Set($nodes.keys());
|
state.selectedNodes = new Set($nodes.keys());
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -665,22 +654,21 @@
|
|||||||
callback: (event) => {
|
callback: (event) => {
|
||||||
if (!isBodyFocused()) return;
|
if (!isBodyFocused()) return;
|
||||||
manager.startUndoGroup();
|
manager.startUndoGroup();
|
||||||
if ($activeNodeId !== -1) {
|
if (state.activeNodeId !== -1) {
|
||||||
const node = manager.getNode($activeNodeId);
|
const node = manager.getNode(state.activeNodeId);
|
||||||
if (node) {
|
if (node) {
|
||||||
manager.removeNode(node, { restoreEdges: event.ctrlKey });
|
manager.removeNode(node, { restoreEdges: event.ctrlKey });
|
||||||
$activeNodeId = -1;
|
state.activeNodeId = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($selectedNodes) {
|
if (state.selectedNodes) {
|
||||||
for (const nodeId of $selectedNodes) {
|
for (const nodeId of state.selectedNodes) {
|
||||||
const node = manager.getNode(nodeId);
|
const node = manager.getNode(nodeId);
|
||||||
if (node) {
|
if (node) {
|
||||||
manager.removeNode(node, { restoreEdges: event.ctrlKey });
|
manager.removeNode(node, { restoreEdges: event.ctrlKey });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$selectedNodes.clear();
|
state.clearSelection();
|
||||||
$selectedNodes = $selectedNodes;
|
|
||||||
}
|
}
|
||||||
manager.saveUndoGroup();
|
manager.saveUndoGroup();
|
||||||
},
|
},
|
||||||
@ -689,16 +677,15 @@
|
|||||||
function handleMouseUp(event: MouseEvent) {
|
function handleMouseUp(event: MouseEvent) {
|
||||||
if (!mouseDown) return;
|
if (!mouseDown) return;
|
||||||
|
|
||||||
const activeNode = manager.getNode($activeNodeId);
|
const activeNode = manager.getNode(state.activeNodeId);
|
||||||
|
|
||||||
const clickedNodeId = getNodeIdFromEvent(event);
|
const clickedNodeId = getNodeIdFromEvent(event);
|
||||||
|
|
||||||
if (clickedNodeId !== -1) {
|
if (clickedNodeId !== -1) {
|
||||||
if (activeNode) {
|
if (activeNode) {
|
||||||
if (!activeNode?.tmp?.isMoving && !event.ctrlKey && !event.shiftKey) {
|
if (!activeNode?.tmp?.isMoving && !event.ctrlKey && !event.shiftKey) {
|
||||||
$selectedNodes?.clear();
|
state.clearSelection();
|
||||||
$selectedNodes = $selectedNodes;
|
state.activeNodeId = clickedNodeId;
|
||||||
$activeNodeId = clickedNodeId;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -721,7 +708,7 @@
|
|||||||
activeNode.position[1] = activeNode?.tmp?.y ?? activeNode.position[1];
|
activeNode.position[1] = activeNode?.tmp?.y ?? activeNode.position[1];
|
||||||
}
|
}
|
||||||
const nodes = [
|
const nodes = [
|
||||||
...[...($selectedNodes?.values() || [])].map((id) =>
|
...[...(state.selectedNodes?.values() || [])].map((id) =>
|
||||||
manager.getNode(id),
|
manager.getNode(id),
|
||||||
),
|
),
|
||||||
] as NodeType[];
|
] as NodeType[];
|
||||||
@ -760,26 +747,26 @@
|
|||||||
$edges = $edges;
|
$edges = $edges;
|
||||||
});
|
});
|
||||||
manager.save();
|
manager.save();
|
||||||
} else if ($hoveredSocket && $activeSocket) {
|
} else if (state.hoveredSocket && state.activeSocket) {
|
||||||
if (
|
if (
|
||||||
typeof $hoveredSocket.index === "number" &&
|
typeof state.hoveredSocket.index === "number" &&
|
||||||
typeof $activeSocket.index === "string"
|
typeof state.activeSocket.index === "string"
|
||||||
) {
|
) {
|
||||||
manager.createEdge(
|
manager.createEdge(
|
||||||
$hoveredSocket.node,
|
state.hoveredSocket.node,
|
||||||
$hoveredSocket.index || 0,
|
state.hoveredSocket.index || 0,
|
||||||
$activeSocket.node,
|
state.activeSocket.node,
|
||||||
$activeSocket.index,
|
state.activeSocket.index,
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
typeof $activeSocket.index == "number" &&
|
typeof state.activeSocket.index == "number" &&
|
||||||
typeof $hoveredSocket.index === "string"
|
typeof state.hoveredSocket.index === "string"
|
||||||
) {
|
) {
|
||||||
manager.createEdge(
|
manager.createEdge(
|
||||||
$activeSocket.node,
|
state.activeSocket.node,
|
||||||
$activeSocket.index || 0,
|
state.activeSocket.index || 0,
|
||||||
$hoveredSocket.node,
|
state.hoveredSocket.node,
|
||||||
$hoveredSocket.index,
|
state.hoveredSocket.index,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
manager.save();
|
manager.save();
|
||||||
@ -793,17 +780,15 @@
|
|||||||
cameraDown[1] === cameraPosition[1] &&
|
cameraDown[1] === cameraPosition[1] &&
|
||||||
isBodyFocused()
|
isBodyFocused()
|
||||||
) {
|
) {
|
||||||
$activeNodeId = -1;
|
state.activeNodeId = -1;
|
||||||
$selectedNodes?.clear();
|
state.clearSelection();
|
||||||
$selectedNodes = $selectedNodes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mouseDown = null;
|
mouseDown = null;
|
||||||
boxSelection = false;
|
boxSelection = false;
|
||||||
$activeSocket = null;
|
state.activeSocket = null;
|
||||||
$possibleSockets = [];
|
state.possibleSockets = [];
|
||||||
$possibleSocketIds = null;
|
state.hoveredSocket = null;
|
||||||
$hoveredSocket = null;
|
|
||||||
addMenuPosition = null;
|
addMenuPosition = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -958,9 +943,12 @@
|
|||||||
<AddMenu bind:position={addMenuPosition} graph={manager} />
|
<AddMenu bind:position={addMenuPosition} graph={manager} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if $activeSocket}
|
{#if state.activeSocket}
|
||||||
<FloatingEdge
|
<FloatingEdge
|
||||||
from={{ x: $activeSocket.position[0], y: $activeSocket.position[1] }}
|
from={{
|
||||||
|
x: state.activeSocket.position[0],
|
||||||
|
y: state.activeSocket.position[1],
|
||||||
|
}}
|
||||||
to={{ x: mousePosition[0], y: mousePosition[1] }}
|
to={{ x: mousePosition[0], y: mousePosition[1] }}
|
||||||
/>
|
/>
|
||||||
{/if}
|
{/if}
|
||||||
|
@ -5,13 +5,14 @@
|
|||||||
import Node from "../node/Node.svelte";
|
import Node from "../node/Node.svelte";
|
||||||
import { getContext, onMount } from "svelte";
|
import { getContext, onMount } from "svelte";
|
||||||
import type { Writable } from "svelte/store";
|
import type { Writable } from "svelte/store";
|
||||||
import { activeSocket } from "./stores.js";
|
import { getGraphState } from "./state.svelte";
|
||||||
|
|
||||||
export let nodes: Writable<Map<number, NodeType>>;
|
export let nodes: Writable<Map<number, NodeType>>;
|
||||||
export let edges: Writable<EdgeType[]>;
|
export let edges: Writable<EdgeType[]>;
|
||||||
|
|
||||||
export let cameraPosition = [0, 0, 4];
|
export let cameraPosition = [0, 0, 4];
|
||||||
|
|
||||||
|
const graphState = getGraphState();
|
||||||
|
|
||||||
const isNodeInView = getContext<(n: NodeType) => boolean>("isNodeInView");
|
const isNodeInView = getContext<(n: NodeType) => boolean>("isNodeInView");
|
||||||
|
|
||||||
const getSocketPosition =
|
const getSocketPosition =
|
||||||
@ -58,7 +59,7 @@
|
|||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="wrapper"
|
class="wrapper"
|
||||||
style:transform={`scale(${cameraPosition[2] * 0.1})`}
|
style:transform={`scale(${cameraPosition[2] * 0.1})`}
|
||||||
class:hovering-sockets={activeSocket}
|
class:hovering-sockets={graphState.activeSocket}
|
||||||
>
|
>
|
||||||
{#each $nodes.values() as node (node.id)}
|
{#each $nodes.values() as node (node.id)}
|
||||||
<Node
|
<Node
|
||||||
|
@ -2,58 +2,77 @@
|
|||||||
import type { Graph, Node, NodeRegistry } from "@nodes/types";
|
import type { Graph, Node, NodeRegistry } from "@nodes/types";
|
||||||
import GraphEl from "./Graph.svelte";
|
import GraphEl from "./Graph.svelte";
|
||||||
import { GraphManager } from "../graph-manager.js";
|
import { GraphManager } from "../graph-manager.js";
|
||||||
import { createEventDispatcher, setContext } from "svelte";
|
import { setContext } from "svelte";
|
||||||
import { type Writable } from "svelte/store";
|
import { type Writable } from "svelte/store";
|
||||||
import { debounce } from "$lib/helpers";
|
import { debounce } from "$lib/helpers";
|
||||||
import { createKeyMap } from "$lib/helpers/createKeyMap";
|
import { createKeyMap } from "$lib/helpers/createKeyMap";
|
||||||
import { activeNodeId } from "./stores";
|
import { GraphState } from "./state.svelte";
|
||||||
|
|
||||||
export let registry: NodeRegistry;
|
type Props = {
|
||||||
export let graph: Graph;
|
graph: Graph;
|
||||||
export let settings: Writable<Record<string, any>> | undefined;
|
registry: NodeRegistry;
|
||||||
export const manager = new GraphManager(registry);
|
|
||||||
export let activeNode: Node | undefined;
|
|
||||||
$: if ($activeNodeId !== -1) {
|
|
||||||
activeNode = manager.getNode($activeNodeId);
|
|
||||||
} else {
|
|
||||||
activeNode = undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const status = manager.status;
|
settings?: Writable<Record<string, any>>;
|
||||||
|
|
||||||
|
activeNode?: Node;
|
||||||
|
showGrid?: boolean;
|
||||||
|
snapToGrid?: boolean;
|
||||||
|
showHelp?: boolean;
|
||||||
|
settingTypes?: Record<string, any>;
|
||||||
|
|
||||||
|
onsave?: (save: Graph) => void;
|
||||||
|
onresult?: (result: any) => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
let {
|
||||||
|
graph,
|
||||||
|
registry,
|
||||||
|
settings = $bindable(),
|
||||||
|
activeNode = $bindable(),
|
||||||
|
showGrid,
|
||||||
|
snapToGrid,
|
||||||
|
showHelp = $bindable(false),
|
||||||
|
settingTypes = $bindable(),
|
||||||
|
onsave,
|
||||||
|
onresult,
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
export const keymap = createKeyMap([]);
|
export const keymap = createKeyMap([]);
|
||||||
|
export const manager = new GraphManager(registry);
|
||||||
|
|
||||||
|
const state = new GraphState();
|
||||||
|
setContext("graphState", state);
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
if (state.activeNodeId !== -1) {
|
||||||
|
activeNode = manager.getNode(state.activeNodeId);
|
||||||
|
} else {
|
||||||
|
activeNode = undefined;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
setContext("keymap", keymap);
|
setContext("keymap", keymap);
|
||||||
|
|
||||||
export let showGrid = false;
|
|
||||||
export let snapToGrid = false;
|
|
||||||
export let showHelp = false;
|
|
||||||
|
|
||||||
export let settingTypes = {};
|
|
||||||
|
|
||||||
const updateSettings = debounce((s) => {
|
const updateSettings = debounce((s) => {
|
||||||
manager.setSettings(s);
|
manager.setSettings(s);
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
$: if (settings && $settings) {
|
$effect(() => {
|
||||||
updateSettings($settings);
|
if (settingTypes && settings) {
|
||||||
}
|
updateSettings($settings);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
manager.on("settings", (_settings) => {
|
manager.on("settings", (_settings) => {
|
||||||
settingTypes = _settings.types;
|
settingTypes = _settings.types;
|
||||||
settings.set(_settings.values);
|
settings?.set(_settings.values);
|
||||||
});
|
});
|
||||||
|
|
||||||
manager.on("result", (result) => {
|
manager.on("result", (result) => onresult?.(result));
|
||||||
dispatch("result", result);
|
|
||||||
});
|
|
||||||
|
|
||||||
manager.on("save", (save) => {
|
manager.on("save", (save) => onsave?.(save));
|
||||||
dispatch("save", save);
|
|
||||||
});
|
|
||||||
|
|
||||||
manager.load(graph);
|
manager.load(graph);
|
||||||
|
|
||||||
const dispatch = createEventDispatcher();
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<GraphEl {manager} bind:showGrid bind:snapToGrid bind:showHelp />
|
<GraphEl {manager} bind:showGrid bind:snapToGrid bind:showHelp />
|
||||||
|
@ -18,7 +18,8 @@ let lastStyle = "";
|
|||||||
|
|
||||||
function updateColors() {
|
function updateColors() {
|
||||||
if (!("getComputedStyle" in globalThis)) return;
|
if (!("getComputedStyle" in globalThis)) return;
|
||||||
const style = getComputedStyle(document.body);
|
console.log("updateColors")
|
||||||
|
const style = getComputedStyle(document.body.parentElement!);
|
||||||
let hash = "";
|
let hash = "";
|
||||||
for (const v of variables) {
|
for (const v of variables) {
|
||||||
let color = style.getPropertyValue(`--${v}`);
|
let color = style.getPropertyValue(`--${v}`);
|
||||||
|
27
app/src/lib/graph-interface/graph/state.svelte.ts
Normal file
27
app/src/lib/graph-interface/graph/state.svelte.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import type { Socket } from "@nodes/types";
|
||||||
|
import { getContext } from "svelte";
|
||||||
|
|
||||||
|
export function getGraphState() {
|
||||||
|
return getContext<GraphState>("graphState");
|
||||||
|
}
|
||||||
|
|
||||||
|
export class GraphState {
|
||||||
|
|
||||||
|
activeNodeId = $state(-1);
|
||||||
|
|
||||||
|
selectedNodes = $state(new Set<number>());
|
||||||
|
clearSelection() {
|
||||||
|
this.selectedNodes = new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
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}`),
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export { colors } from "./colors";
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
import type { Socket } from "@nodes/types";
|
|
||||||
import { writable, type Writable } from "svelte/store";
|
|
||||||
import { Color } from "three/src/math/Color.js";
|
|
||||||
|
|
||||||
export const activeNodeId: Writable<number> = writable(-1);
|
|
||||||
export const selectedNodes: Writable<Set<number> | null> = writable(new Set());
|
|
||||||
|
|
||||||
export const activeSocket: Writable<Socket | null> = writable(null);
|
|
||||||
export const hoveredSocket: Writable<Socket | null> = writable(null);
|
|
||||||
export const possibleSockets: Writable<Socket[]> = writable([]);
|
|
||||||
export const possibleSocketIds: Writable<Set<string> | null> = writable(null);
|
|
||||||
|
|
||||||
export { colors } from "./colors";
|
|
@ -1,38 +1,44 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import type { Node } from "@nodes/types";
|
import type { Node } from "@nodes/types";
|
||||||
import { getContext, onMount } from "svelte";
|
import { getContext, onMount } from "svelte";
|
||||||
import { activeNodeId, selectedNodes } from "../graph/stores.js";
|
import { colors, getGraphState } from "../graph/state.svelte";
|
||||||
import { colors } from "../graph/stores";
|
|
||||||
import { T } from "@threlte/core";
|
import { T } from "@threlte/core";
|
||||||
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";
|
import NodeHtml from "./NodeHTML.svelte";
|
||||||
|
|
||||||
export let node: Node;
|
const graphState = getGraphState();
|
||||||
export let inView = true;
|
|
||||||
export let z = 2;
|
|
||||||
|
|
||||||
$: isActive = $activeNodeId === node.id;
|
type Props = {
|
||||||
$: isSelected = !!$selectedNodes?.has(node.id);
|
node: Node;
|
||||||
|
inView: boolean;
|
||||||
|
z: number;
|
||||||
|
};
|
||||||
|
const { node, inView, z }: Props = $props();
|
||||||
|
|
||||||
|
const isActive = $derived(graphState.activeNodeId === node.id);
|
||||||
|
const isSelected = $derived(!!graphState.selectedNodes?.has(node.id));
|
||||||
|
|
||||||
const updateNodePosition =
|
const updateNodePosition =
|
||||||
getContext<(n: Node) => void>("updateNodePosition");
|
getContext<(n: Node) => void>("updateNodePosition");
|
||||||
|
|
||||||
const getNodeHeight = getContext<(n: string) => number>("getNodeHeight");
|
const getNodeHeight = getContext<(n: string) => number>("getNodeHeight");
|
||||||
|
|
||||||
let meshRef: Mesh;
|
let meshRef: Mesh | undefined = $state();
|
||||||
|
|
||||||
const height = getNodeHeight?.(node.type);
|
const height = getNodeHeight?.(node.type);
|
||||||
|
|
||||||
$: if (node && meshRef) {
|
$effect(() => {
|
||||||
node.tmp = node.tmp || {};
|
if (node && meshRef) {
|
||||||
node.tmp.mesh = meshRef;
|
node.tmp = node.tmp || {};
|
||||||
updateNodePosition?.(node);
|
node.tmp.mesh = meshRef;
|
||||||
}
|
updateNodePosition?.(node);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$: colorBright = $colors["layer-2"];
|
const colorBright = $colors["layer-2"];
|
||||||
$: colorDark = $colors["layer-1"];
|
const colorDark = $colors["layer-1"];
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
node.tmp = node.tmp || {};
|
node.tmp = node.tmp || {};
|
||||||
|
@ -60,7 +60,7 @@
|
|||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
on:mousedown={handleMouseDown}
|
on:mousedown={handleMouseDown}
|
||||||
/>
|
></div>
|
||||||
<svg
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
viewBox="0 0 100 100"
|
viewBox="0 0 100 100"
|
||||||
|
@ -3,23 +3,33 @@
|
|||||||
import { getGraphManager } from "../graph/context.js";
|
import { getGraphManager } from "../graph/context.js";
|
||||||
import { Input } from "@nodes/ui";
|
import { Input } from "@nodes/ui";
|
||||||
|
|
||||||
export let node: Node;
|
type Props = {
|
||||||
export let input: NodeInput;
|
node: Node;
|
||||||
export let id: string;
|
input: NodeInput;
|
||||||
|
id: string;
|
||||||
|
elementId?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
node,
|
||||||
|
input,
|
||||||
|
id,
|
||||||
|
elementId = `input-${Math.random().toString(36).substring(7)}`,
|
||||||
|
}: Props = $props();
|
||||||
|
|
||||||
const graph = getGraphManager();
|
const graph = getGraphManager();
|
||||||
|
|
||||||
let value = node?.props?.[id] ?? input.value;
|
let value = $state(node?.props?.[id] ?? input.value);
|
||||||
|
|
||||||
export let elementId: string = `input-${Math.random().toString(36).substring(7)}`;
|
$effect(() => {
|
||||||
|
if (value !== undefined && node?.props?.[id] !== value) {
|
||||||
$: if (node?.props?.[id] !== value) {
|
node.props = { ...node.props, [id]: value };
|
||||||
node.props = { ...node.props, [id]: value };
|
if (graph) {
|
||||||
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 />
|
||||||
|
@ -6,20 +6,25 @@
|
|||||||
} from "@nodes/types";
|
} from "@nodes/types";
|
||||||
import { getContext } from "svelte";
|
import { getContext } from "svelte";
|
||||||
import { createNodePath } from "../helpers/index.js";
|
import { createNodePath } from "../helpers/index.js";
|
||||||
import { possibleSocketIds } from "../graph/stores.js";
|
|
||||||
import { getGraphManager } from "../graph/context.js";
|
import { getGraphManager } from "../graph/context.js";
|
||||||
import NodeInput from "./NodeInput.svelte";
|
import NodeInput from "./NodeInput.svelte";
|
||||||
|
import { getGraphState } from "../graph/state.svelte.js";
|
||||||
|
|
||||||
export let node: NodeType;
|
type Props = {
|
||||||
export let input: NodeInputType;
|
node: NodeType;
|
||||||
export let id: string;
|
input: NodeInputType;
|
||||||
export let isLast = false;
|
id: string;
|
||||||
|
isLast?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const { node = $bindable(), input, id, isLast }: Props = $props();
|
||||||
|
|
||||||
const inputType = node?.tmp?.type?.inputs?.[id]!;
|
const inputType = node?.tmp?.type?.inputs?.[id]!;
|
||||||
|
|
||||||
const socketId = `${node.id}-${id}`;
|
const socketId = `${node.id}-${id}`;
|
||||||
|
|
||||||
const graph = getGraphManager();
|
const graph = getGraphManager();
|
||||||
|
const graphState = getGraphState();
|
||||||
const graphId = graph?.id;
|
const graphId = graph?.id;
|
||||||
const inputSockets = graph?.inputSockets;
|
const inputSockets = graph?.inputSockets;
|
||||||
|
|
||||||
@ -75,7 +80,7 @@
|
|||||||
class="wrapper"
|
class="wrapper"
|
||||||
data-node-type={node.type}
|
data-node-type={node.type}
|
||||||
data-node-input={id}
|
data-node-input={id}
|
||||||
class:disabled={$possibleSocketIds && !$possibleSocketIds.has(socketId)}
|
class:disabled={!graphState.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)}>
|
||||||
@ -91,17 +96,17 @@
|
|||||||
<div
|
<div
|
||||||
data-node-socket
|
data-node-socket
|
||||||
class="large target"
|
class="large target"
|
||||||
on:mousedown={handleMouseDown}
|
onmousedown={handleMouseDown}
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
/>
|
></div>
|
||||||
<div
|
<div
|
||||||
data-node-socket
|
data-node-socket
|
||||||
class="small target"
|
class="small target"
|
||||||
on:mousedown={handleMouseDown}
|
onmousedown={handleMouseDown}
|
||||||
role="button"
|
role="button"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
/>
|
></div>
|
||||||
{/if}
|
{/if}
|
||||||
{/key}
|
{/key}
|
||||||
|
|
||||||
@ -185,8 +190,10 @@
|
|||||||
d: var(--path);
|
d: var(--path);
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.hovering-sockets) .large:hover ~ svg path {
|
:global {
|
||||||
d: var(--hover-path);
|
.hovering-sockets .large:hover ~ svg path {
|
||||||
|
d: var(--hover-path);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.content.disabled {
|
.content.disabled {
|
||||||
|
@ -50,6 +50,7 @@
|
|||||||
<div class="wrapper" class:visible={$activePanel}>
|
<div class="wrapper" class:visible={$activePanel}>
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<button
|
<button
|
||||||
|
aria-label="Close"
|
||||||
on:click={() => {
|
on:click={() => {
|
||||||
setActivePanel($activePanel ? false : keys[0]);
|
setActivePanel($activePanel ? false : keys[0]);
|
||||||
}}
|
}}
|
||||||
@ -59,11 +60,12 @@
|
|||||||
{#each keys as panel (panels[panel].id)}
|
{#each keys as panel (panels[panel].id)}
|
||||||
{#if panels[panel].visible !== false}
|
{#if panels[panel].visible !== false}
|
||||||
<button
|
<button
|
||||||
|
aria-label={panel}
|
||||||
class="tab {panels[panel].classes}"
|
class="tab {panels[panel].classes}"
|
||||||
class:active={panel === $activePanel}
|
class:active={panel === $activePanel}
|
||||||
on:click={() => setActivePanel(panel)}
|
on:click={() => setActivePanel(panel)}
|
||||||
>
|
>
|
||||||
<span class={`block w-6 h-6 ${panels[panel].icon}`} />
|
<span class={`block w-6 h-6 ${panels[panel].icon}`}></span>
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
@ -20,14 +20,16 @@ export const AppSettings = localStore("node.settings", {
|
|||||||
const themes = ["dark", "light", "catppuccin", "solarized", "high-contrast", "nord", "dracula"];
|
const themes = ["dark", "light", "catppuccin", "solarized", "high-contrast", "nord", "dracula"];
|
||||||
|
|
||||||
AppSettings.subscribe((value) => {
|
AppSettings.subscribe((value) => {
|
||||||
const classes = document.body.classList;
|
const classes = document.body.parentElement?.classList;
|
||||||
const newClassName = `theme-${themes[value.theme]}`;
|
const newClassName = `theme-${themes[value.theme]}`;
|
||||||
for (const className of classes) {
|
if (classes) {
|
||||||
if (className.startsWith("theme-") && className !== newClassName) {
|
for (const className of classes) {
|
||||||
classes.remove(className);
|
if (className.startsWith("theme-") && className !== newClassName) {
|
||||||
|
classes.remove(className);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
document.body.classList.add(newClassName);
|
document.body?.parentElement?.classList.add(newClassName);
|
||||||
});
|
});
|
||||||
|
|
||||||
export const AppSettingTypes = {
|
export const AppSettingTypes = {
|
||||||
|
@ -24,11 +24,12 @@
|
|||||||
MemoryRuntimeExecutor,
|
MemoryRuntimeExecutor,
|
||||||
} from "@nodes/runtime";
|
} from "@nodes/runtime";
|
||||||
import { IndexDBCache, RemoteNodeRegistry } from "@nodes/registry";
|
import { IndexDBCache, RemoteNodeRegistry } from "@nodes/registry";
|
||||||
import { decodeNestedArray, createPerformanceStore } from "@nodes/utils";
|
import { createPerformanceStore } from "@nodes/utils";
|
||||||
import BenchmarkPanel from "$lib/settings/panels/BenchmarkPanel.svelte";
|
import BenchmarkPanel from "$lib/settings/panels/BenchmarkPanel.svelte";
|
||||||
import { debounceAsyncFunction } from "$lib/helpers";
|
import { debounceAsyncFunction } from "$lib/helpers";
|
||||||
|
import type { Component } from "svelte";
|
||||||
|
|
||||||
let performanceStore = createPerformanceStore("page");
|
let performanceStore = createPerformanceStore();
|
||||||
|
|
||||||
const registryCache = new IndexDBCache("node-registry");
|
const registryCache = new IndexDBCache("node-registry");
|
||||||
const nodeRegistry = new RemoteNodeRegistry("");
|
const nodeRegistry = new RemoteNodeRegistry("");
|
||||||
@ -38,17 +39,6 @@
|
|||||||
const memoryRuntime = new MemoryRuntimeExecutor(nodeRegistry, runtimeCache);
|
const memoryRuntime = new MemoryRuntimeExecutor(nodeRegistry, runtimeCache);
|
||||||
memoryRuntime.perf = performanceStore;
|
memoryRuntime.perf = performanceStore;
|
||||||
|
|
||||||
globalThis.decode = decodeNestedArray;
|
|
||||||
|
|
||||||
globalThis.clearCache = () => {
|
|
||||||
registryCache.clear();
|
|
||||||
runtimeCache.clear();
|
|
||||||
localStorage.clear();
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.reload();
|
|
||||||
}, 500);
|
|
||||||
};
|
|
||||||
|
|
||||||
$: runtime = $AppSettings.useWorker ? workerRuntime : memoryRuntime;
|
$: runtime = $AppSettings.useWorker ? workerRuntime : memoryRuntime;
|
||||||
|
|
||||||
let activeNode: Node | undefined;
|
let activeNode: Node | undefined;
|
||||||
@ -59,11 +49,10 @@
|
|||||||
? JSON.parse(localStorage.getItem("graph")!)
|
? JSON.parse(localStorage.getItem("graph")!)
|
||||||
: templates.defaultPlant;
|
: templates.defaultPlant;
|
||||||
|
|
||||||
let manager: GraphManager;
|
let graphInterface: ReturnType<typeof GraphInterface>;
|
||||||
let managerStatus: Writable<"loading" | "error" | "idle">;
|
$: manager = graphInterface?.manager;
|
||||||
$: if (manager) {
|
$: managerStatus = manager?.status;
|
||||||
managerStatus = manager.status;
|
$: keymap = graphInterface?.keymap;
|
||||||
}
|
|
||||||
|
|
||||||
async function randomGenerate() {
|
async function randomGenerate() {
|
||||||
const g = manager.serialize();
|
const g = manager.serialize();
|
||||||
@ -71,7 +60,6 @@
|
|||||||
await handleUpdate(g, s);
|
await handleUpdate(g, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
let keymap: ReturnType<typeof createKeyMap>;
|
|
||||||
let applicationKeymap = createKeyMap([
|
let applicationKeymap = createKeyMap([
|
||||||
{
|
{
|
||||||
key: "r",
|
key: "r",
|
||||||
@ -136,8 +124,8 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSave(event: CustomEvent<Graph>) {
|
function handleSave(graph: Graph) {
|
||||||
localStorage.setItem("graph", JSON.stringify(event.detail));
|
localStorage.setItem("graph", JSON.stringify(graph));
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -156,18 +144,17 @@
|
|||||||
<Grid.Cell>
|
<Grid.Cell>
|
||||||
{#key graph}
|
{#key graph}
|
||||||
<GraphInterface
|
<GraphInterface
|
||||||
|
bind:this={graphInterface}
|
||||||
{graph}
|
{graph}
|
||||||
registry={nodeRegistry}
|
registry={nodeRegistry}
|
||||||
bind:manager
|
|
||||||
bind:activeNode
|
bind:activeNode
|
||||||
bind:keymap
|
|
||||||
showGrid={$AppSettings.showNodeGrid}
|
showGrid={$AppSettings.showNodeGrid}
|
||||||
snapToGrid={$AppSettings.snapToGrid}
|
snapToGrid={$AppSettings.snapToGrid}
|
||||||
bind:showHelp={$AppSettings.showHelp}
|
bind:showHelp={$AppSettings.showHelp}
|
||||||
bind:settings={graphSettings}
|
bind:settings={graphSettings}
|
||||||
bind:settingTypes={graphSettingTypes}
|
bind:settingTypes={graphSettingTypes}
|
||||||
on:result={(ev) => handleUpdate(ev.detail, $graphSettings)}
|
onresult={(result) => handleUpdate(result, $graphSettings)}
|
||||||
on:save={handleSave}
|
onsave={(graph) => handleSave(graph)}
|
||||||
/>
|
/>
|
||||||
<Settings>
|
<Settings>
|
||||||
<Panel id="general" title="General" icon="i-tabler-settings">
|
<Panel id="general" title="General" icon="i-tabler-settings">
|
||||||
|
@ -5,6 +5,7 @@ import type { Graph, RuntimeExecutor } from "@nodes/types";
|
|||||||
export class WorkerRuntimeExecutor implements RuntimeExecutor {
|
export class WorkerRuntimeExecutor implements RuntimeExecutor {
|
||||||
private worker = new ComlinkWorker<typeof import('./worker-runtime-executor-backend.ts')>(new URL("worker-runtime-executor-backend.ts", import.meta.url));
|
private worker = new ComlinkWorker<typeof import('./worker-runtime-executor-backend.ts')>(new URL("worker-runtime-executor-backend.ts", import.meta.url));
|
||||||
constructor() {
|
constructor() {
|
||||||
|
console.log(import.meta.url)
|
||||||
}
|
}
|
||||||
async execute(graph: Graph, settings: Record<string, unknown>) {
|
async execute(graph: Graph, settings: Record<string, unknown>) {
|
||||||
return this.worker.executeGraph(graph, settings);
|
return this.worker.executeGraph(graph, settings);
|
||||||
|
Loading…
Reference in New Issue
Block a user