{
+ this.graph.createNode({
+ type: nodeId,
+ props,
+ position: pos
+ });
+ });
+ } else if (event.dataTransfer.files.length) {
+ const file = event.dataTransfer.files[0];
+
+ if (file.type === 'application/wasm') {
+ const reader = new FileReader();
+ reader.onload = async (e) => {
+ const buffer = e.target?.result;
+ if (buffer?.constructor === ArrayBuffer) {
+ const nodeType = await this.graph.registry.register(buffer);
+
+ this.graph.createNode({
+ type: nodeType.id,
+ props: {},
+ position: this.state.projectScreenToWorld(mx, my)
+ });
+ }
+ };
+ reader.readAsArrayBuffer(file);
+ } else if (file.type === 'application/json') {
+ const reader = new FileReader();
+ reader.onload = (e) => {
+ const buffer = e.target?.result as ArrayBuffer;
+ if (buffer) {
+ const state = GraphSchema.parse(JSON.parse(buffer.toString()));
+ this.graph.load(state);
+ }
+ };
+ reader.readAsText(file);
+ }
+ }
+ }
+
+ handleMouseLeave() {
+ this.state.isDragging = false;
+ this.state.isPanning = false;
+ }
+
+ handleDragEnter(e: DragEvent) {
+ e.preventDefault();
+ this.state.isDragging = true;
+ this.state.isPanning = false;
+ }
+
+ handleDragOver(e: DragEvent) {
+ e.preventDefault();
+ this.state.isDragging = true;
+ this.state.isPanning = false;
+ }
+
+ handleDragEnd(e: DragEvent) {
+ e.preventDefault();
+ this.state.isDragging = true;
+ this.state.isPanning = false;
+ }
+
+ getEventListenerProps() {
+ return {
+ ondragenter: (ev: DragEvent) => this.handleDragEnter(ev),
+ ondragover: (ev: DragEvent) => this.handleDragOver(ev),
+ ondragexit: (ev: DragEvent) => this.handleDragEnd(ev),
+ ondrop: (ev: DragEvent) => this.handleFileDrop(ev),
+ onmouseleave: () => this.handleMouseLeave()
+ };
+ }
+}
diff --git a/app/src/lib/graph-interface/graph/edge.events.ts b/app/src/lib/graph-interface/graph/edge.events.ts
new file mode 100644
index 0000000..7fb9e21
--- /dev/null
+++ b/app/src/lib/graph-interface/graph/edge.events.ts
@@ -0,0 +1,110 @@
+import type { Box } from '@nodarium/types';
+import type { GraphManager } from '../graph-manager.svelte';
+import type { GraphState } from '../graph-state.svelte';
+import { distanceFromPointToSegment } from '../helpers';
+
+export class EdgeInteractionManager {
+ constructor(
+ private graph: GraphManager,
+ private state: GraphState
+ ) { }
+
+ private MIN_DISTANCE = 3;
+
+ private _boundingBoxes = new Map
();
+
+ handleMouseDown() {
+ this._boundingBoxes.clear();
+
+ const possibleEdges = this.graph
+ .getPossibleDropOnEdges(this.state.activeNodeId)
+ .map(e => this.graph.getEdgeId(e));
+
+ const edges = this.state.getEdges();
+ for (const edge of edges) {
+ const edgeId = edge[0];
+ if (!possibleEdges.includes(edgeId)) {
+ edges.delete(edgeId);
+ }
+ }
+
+ for (const [edgeId, data] of edges) {
+ const points = data.points;
+ let minX = points[0].x + data.x1;
+ let maxX = points[0].x + data.x1;
+ let minY = points[0].z + data.y1;
+ let maxY = points[0].z + data.y1;
+
+ // Iterate through all points to find the true bounds
+ for (let i = 0; i < points.length; i++) {
+ const x = data.x1 + points[i].x;
+ const y = data.y1 + points[i].z;
+ if (x < minX) minX = x;
+ if (x > maxX) maxX = x;
+ if (y < minY) minY = y;
+ if (y > maxY) maxY = y;
+ }
+
+ const boundingBox = {
+ minX: minX - this.MIN_DISTANCE,
+ maxX: maxX + this.MIN_DISTANCE,
+ minY: minY - this.MIN_DISTANCE,
+ maxY: maxY + this.MIN_DISTANCE
+ };
+
+ this._boundingBoxes.set(edgeId, boundingBox);
+ }
+ }
+
+ handleMouseMove() {
+ const [mouseX, mouseY] = this.state.mousePosition;
+ const hoveredEdgeIds: string[] = [];
+
+ const edges = this.state.getEdges();
+
+ // Check if mouse is inside any bounding box
+ for (const [edgeId, box] of this._boundingBoxes) {
+ const isInside = mouseX >= box.minX
+ && mouseX <= box.maxX
+ && mouseY >= box.minY
+ && mouseY <= box.maxY;
+
+ if (isInside) {
+ hoveredEdgeIds.push(edgeId);
+ }
+ }
+
+ let hoveredEdgeId: string | null = null;
+ let hoveredEdgeDistance = Infinity;
+
+ const DENSITY = 10; // higher DENSITY = less points checked (yes density might not be the best name :-)
+ for (const edgeId of hoveredEdgeIds) {
+ const edge = edges.get(edgeId)!;
+ for (let i = 0; i < edge.points.length - DENSITY; i += DENSITY) {
+ const pointAx = edge.points[i].x + edge.x1;
+ const pointAy = edge.points[i].z + edge.y1;
+ const pointBx = edge.points[i + DENSITY].x + edge.x1;
+ const pointBy = edge.points[i + DENSITY].z + edge.y1;
+ const distance = distanceFromPointToSegment(pointAx, pointAy, pointBx, pointBy, mouseX, mouseY);
+ if (distance < this.MIN_DISTANCE) {
+ if (distance < hoveredEdgeDistance) {
+ hoveredEdgeDistance = distance;
+ hoveredEdgeId = edgeId;
+ }
+ }
+ }
+ }
+
+ this.state.hoveredEdgeId = hoveredEdgeId;
+ }
+
+ handleMouseUp() {
+ if (this.state.hoveredEdgeId) {
+ const edge = this.graph.getEdgeById(this.state.hoveredEdgeId);
+ if (edge) {
+ this.graph.dropNodeOnEdge(this.state.activeNodeId, edge);
+ }
+ this.state.hoveredEdgeId = null;
+ }
+ }
+}
diff --git a/app/src/lib/graph-interface/graph/events.ts b/app/src/lib/graph-interface/graph/mouse.events.ts
similarity index 64%
rename from app/src/lib/graph-interface/graph/events.ts
rename to app/src/lib/graph-interface/graph/mouse.events.ts
index fda0513..c856a77 100644
--- a/app/src/lib/graph-interface/graph/events.ts
+++ b/app/src/lib/graph-interface/graph/mouse.events.ts
@@ -1,127 +1,23 @@
-import { GraphSchema, type NodeId, type NodeInstance } from "@nodarium/types";
-import type { GraphManager } from "../graph-manager.svelte";
-import type { GraphState } from "../graph-state.svelte";
-import { animate, lerp } from "$lib/helpers";
-import { snapToGrid as snapPointToGrid } from "../helpers";
-import { maxZoom, minZoom, zoomSpeed } from "./constants";
-
-
-export class FileDropEventManager {
-
- constructor(
- private graph: GraphManager,
- private state: GraphState
- ) { }
-
- handleFileDrop(event: DragEvent) {
- event.preventDefault();
- this.state.isDragging = false;
- if (!event.dataTransfer) return;
- const nodeId = event.dataTransfer.getData("data/node-id") as NodeId;
- let mx = event.clientX - this.state.rect.x;
- let my = event.clientY - this.state.rect.y;
-
- if (nodeId) {
- let nodeOffsetX = event.dataTransfer.getData("data/node-offset-x");
- let nodeOffsetY = event.dataTransfer.getData("data/node-offset-y");
- if (nodeOffsetX && nodeOffsetY) {
- mx += parseInt(nodeOffsetX);
- my += parseInt(nodeOffsetY);
- }
-
- let props = {};
- let rawNodeProps = event.dataTransfer.getData("data/node-props");
- if (rawNodeProps) {
- try {
- props = JSON.parse(rawNodeProps);
- } catch (e) { }
- }
-
- const pos = this.state.projectScreenToWorld(mx, my);
- this.graph.registry.load([nodeId]).then(() => {
- this.graph.createNode({
- type: nodeId,
- props,
- position: pos,
- });
- });
- } else if (event.dataTransfer.files.length) {
- const file = event.dataTransfer.files[0];
-
- if (file.type === "application/wasm") {
- const reader = new FileReader();
- reader.onload = async (e) => {
- const buffer = e.target?.result;
- if (buffer?.constructor === ArrayBuffer) {
- const nodeType = await this.graph.registry.register(buffer);
-
- this.graph.createNode({
- type: nodeType.id,
- props: {},
- position: this.state.projectScreenToWorld(mx, my),
- });
- }
- };
- reader.readAsArrayBuffer(file);
- } else if (file.type === "application/json") {
- const reader = new FileReader();
- reader.onload = (e) => {
- const buffer = e.target?.result as ArrayBuffer;
- if (buffer) {
- const state = GraphSchema.parse(JSON.parse(buffer.toString()));
- this.graph.load(state);
- }
- };
- reader.readAsText(file);
- }
- }
- }
-
- handleMouseLeave() {
- this.state.isDragging = false;
- this.state.isPanning = false;
- }
-
- handleDragEnter(e: DragEvent) {
- e.preventDefault();
- this.state.isDragging = true;
- this.state.isPanning = false;
- }
-
- handleDragOver(e: DragEvent) {
- e.preventDefault();
- this.state.isDragging = true;
- this.state.isPanning = false;
- }
-
- handleDragEnd(e: DragEvent) {
- e.preventDefault();
- this.state.isDragging = true;
- this.state.isPanning = false;
- }
-
- getEventListenerProps() {
- return {
- ondragenter: (ev: DragEvent) => this.handleDragEnter(ev),
- ondragover: (ev: DragEvent) => this.handleDragOver(ev),
- ondragexit: (ev: DragEvent) => this.handleDragEnd(ev),
- ondrop: (ev: DragEvent) => this.handleFileDrop(ev),
- onmouseleave: () => this.handleMouseLeave(),
- }
- }
-}
-
+import { animate, lerp } from '$lib/helpers';
+import { type NodeInstance } from '@nodarium/types';
+import type { GraphManager } from '../graph-manager.svelte';
+import type { GraphState } from '../graph-state.svelte';
+import { snapToGrid as snapPointToGrid } from '../helpers';
+import { maxZoom, minZoom, zoomSpeed } from './constants';
+import { EdgeInteractionManager } from './edge.events';
export class MouseEventManager {
-
+ edgeInteractionManager: EdgeInteractionManager;
constructor(
private graph: GraphManager,
private state: GraphState
- ) { }
-
+ ) {
+ this.edgeInteractionManager = new EdgeInteractionManager(graph, state);
+ }
handleMouseUp(event: MouseEvent) {
+ this.edgeInteractionManager.handleMouseUp();
this.state.isPanning = false;
if (!this.state.mouseDown) return;
@@ -145,25 +41,23 @@ export class MouseEventManager {
const snapLevel = this.state.getSnapLevel();
activeNode.position[0] = snapPointToGrid(
activeNode?.state?.x ?? activeNode.position[0],
- 5 / snapLevel,
+ 5 / snapLevel
);
activeNode.position[1] = snapPointToGrid(
activeNode?.state?.y ?? activeNode.position[1],
- 5 / snapLevel,
+ 5 / snapLevel
);
} else {
activeNode.position[0] = activeNode?.state?.x ?? activeNode.position[0];
activeNode.position[1] = activeNode?.state?.y ?? activeNode.position[1];
}
const nodes = [
- ...[...(this.state.selectedNodes?.values() || [])].map((id) =>
- this.graph.getNode(id),
- ),
+ ...[...(this.state.selectedNodes?.values() || [])].map((id) => this.graph.getNode(id))
] as NodeInstance[];
const vec = [
activeNode.position[0] - (activeNode?.state.x || 0),
- activeNode.position[1] - (activeNode?.state.y || 0),
+ activeNode.position[1] - (activeNode?.state.y || 0)
];
for (const node of nodes) {
@@ -179,9 +73,9 @@ export class MouseEventManager {
animate(500, (a: number) => {
for (const node of nodes) {
if (
- node?.state &&
- node.state["x"] !== undefined &&
- node.state["y"] !== undefined
+ node?.state
+ && node.state['x'] !== undefined
+ && node.state['y'] !== undefined
) {
node.state.x = lerp(node.state.x, node.position[0], a);
node.state.y = lerp(node.state.y, node.position[1], a);
@@ -195,24 +89,24 @@ export class MouseEventManager {
this.graph.save();
} else if (this.state.hoveredSocket && this.state.activeSocket) {
if (
- typeof this.state.hoveredSocket.index === "number" &&
- typeof this.state.activeSocket.index === "string"
+ typeof this.state.hoveredSocket.index === 'number'
+ && typeof this.state.activeSocket.index === 'string'
) {
this.graph.createEdge(
this.state.hoveredSocket.node,
this.state.hoveredSocket.index || 0,
this.state.activeSocket.node,
- this.state.activeSocket.index,
+ this.state.activeSocket.index
);
} else if (
- typeof this.state.activeSocket.index == "number" &&
- typeof this.state.hoveredSocket.index === "string"
+ typeof this.state.activeSocket.index == 'number'
+ && typeof this.state.hoveredSocket.index === 'string'
) {
this.graph.createEdge(
this.state.activeSocket.node,
this.state.activeSocket.index || 0,
this.state.hoveredSocket.node,
- this.state.hoveredSocket.index,
+ this.state.hoveredSocket.index
);
}
this.graph.save();
@@ -220,18 +114,18 @@ export class MouseEventManager {
// Handle automatic adding of nodes on ctrl+mouseUp
this.state.edgeEndPosition = [
this.state.mousePosition[0],
- this.state.mousePosition[1],
+ this.state.mousePosition[1]
];
- if (typeof this.state.activeSocket.index === "number") {
+ if (typeof this.state.activeSocket.index === 'number') {
this.state.addMenuPosition = [
this.state.mousePosition[0],
- this.state.mousePosition[1] - 25 / this.state.cameraPosition[2],
+ this.state.mousePosition[1] - 25 / this.state.cameraPosition[2]
];
} else {
this.state.addMenuPosition = [
this.state.mousePosition[0] - 155 / this.state.cameraPosition[2],
- this.state.mousePosition[1] - 25 / this.state.cameraPosition[2],
+ this.state.mousePosition[1] - 25 / this.state.cameraPosition[2]
];
}
return;
@@ -239,11 +133,11 @@ export class MouseEventManager {
// check if camera moved
if (
- clickedNodeId === -1 &&
- !this.state.boxSelection &&
- this.state.cameraDown[0] === this.state.cameraPosition[0] &&
- this.state.cameraDown[1] === this.state.cameraPosition[1] &&
- this.state.isBodyFocused()
+ clickedNodeId === -1
+ && !this.state.boxSelection
+ && this.state.cameraDown[0] === this.state.cameraPosition[0]
+ && this.state.cameraDown[1] === this.state.cameraPosition[1]
+ && this.state.isBodyFocused()
) {
this.state.activeNodeId = -1;
this.state.clearSelection();
@@ -257,16 +151,15 @@ export class MouseEventManager {
this.state.addMenuPosition = null;
}
-
handleMouseDown(event: MouseEvent) {
if (this.state.mouseDown) return;
this.state.edgeEndPosition = null;
if (event.target instanceof HTMLElement) {
if (
- event.target.nodeName !== "CANVAS" &&
- !event.target.classList.contains("node") &&
- !event.target.classList.contains("content")
+ event.target.nodeName !== 'CANVAS'
+ && !event.target.classList.contains('node')
+ && !event.target.classList.contains('content')
) {
return;
}
@@ -288,7 +181,7 @@ export class MouseEventManager {
this.state.activeNodeId = clickedNodeId;
// if the selected node is the same as the clicked node
} else if (this.state.activeNodeId === clickedNodeId) {
- //$activeNodeId = -1;
+ // $activeNodeId = -1;
// if the clicked node is different from the selected node and secondary
} else if (event.ctrlKey) {
this.state.selectedNodes.add(this.state.activeNodeId);
@@ -312,6 +205,7 @@ export class MouseEventManager {
this.state.activeNodeId = clickedNodeId;
this.state.clearSelection();
}
+ this.edgeInteractionManager.handleMouseDown();
} else if (event.ctrlKey) {
this.state.boxSelection = true;
}
@@ -335,7 +229,6 @@ export class MouseEventManager {
this.state.edgeEndPosition = null;
}
-
handleMouseMove(event: MouseEvent) {
let mx = event.clientX - this.state.rect.x;
let my = event.clientY - this.state.rect.y;
@@ -351,8 +244,8 @@ export class MouseEventManager {
let _socket;
for (const socket of this.state.possibleSockets) {
const dist = Math.sqrt(
- (socket.position[0] - this.state.mousePosition[0]) ** 2 +
- (socket.position[1] - this.state.mousePosition[1]) ** 2,
+ (socket.position[0] - this.state.mousePosition[0]) ** 2
+ + (socket.position[1] - this.state.mousePosition[1]) ** 2
);
if (dist < smallestDist) {
smallestDist = dist;
@@ -375,7 +268,7 @@ export class MouseEventManager {
event.stopPropagation();
const mouseD = this.state.projectScreenToWorld(
this.state.mouseDown[0],
- this.state.mouseDown[1],
+ this.state.mouseDown[1]
);
const x1 = Math.min(mouseD[0], this.state.mousePosition[0]);
const x2 = Math.max(mouseD[0], this.state.mousePosition[0]);
@@ -397,6 +290,7 @@ export class MouseEventManager {
// here we are handling dragging of nodes
if (this.state.activeNodeId !== -1 && this.state.mouseDownNodeId !== -1) {
+ this.edgeInteractionManager.handleMouseMove();
const node = this.graph.getNode(this.state.activeNodeId);
if (!node || event.buttons !== 1) return;
@@ -405,10 +299,8 @@ export class MouseEventManager {
const oldX = node.state.downX || 0;
const oldY = node.state.downY || 0;
- let newX =
- oldX + (mx - this.state.mouseDown[0]) / this.state.cameraPosition[2];
- let newY =
- oldY + (my - this.state.mouseDown[1]) / this.state.cameraPosition[2];
+ let newX = oldX + (mx - this.state.mouseDown[0]) / this.state.cameraPosition[2];
+ let newY = oldY + (my - this.state.mouseDown[1]) / this.state.cameraPosition[2];
if (event.ctrlKey) {
const snapLevel = this.state.getSnapLevel();
@@ -448,23 +340,19 @@ export class MouseEventManager {
// here we are handling panning of camera
this.state.isPanning = true;
- let newX =
- this.state.cameraDown[0] -
- (mx - this.state.mouseDown[0]) / this.state.cameraPosition[2];
- let newY =
- this.state.cameraDown[1] -
- (my - this.state.mouseDown[1]) / this.state.cameraPosition[2];
+ let newX = this.state.cameraDown[0]
+ - (mx - this.state.mouseDown[0]) / this.state.cameraPosition[2];
+ let newY = this.state.cameraDown[1]
+ - (my - this.state.mouseDown[1]) / this.state.cameraPosition[2];
this.state.cameraPosition[0] = newX;
this.state.cameraPosition[1] = newY;
}
-
handleMouseScroll(event: WheelEvent) {
- const bodyIsFocused =
- document.activeElement === document.body ||
- document.activeElement === this.state.wrapper ||
- document?.activeElement?.id === "graph";
+ const bodyIsFocused = document.activeElement === document.body
+ || document.activeElement === this.state.wrapper
+ || document?.activeElement?.id === 'graph';
if (!bodyIsFocused) return;
// Define zoom speed and clamp it between -1 and 1
@@ -479,21 +367,19 @@ export class MouseEventManager {
maxZoom,
isNegative
? this.state.cameraPosition[2] / delta
- : this.state.cameraPosition[2] * delta,
- ),
+ : this.state.cameraPosition[2] * delta
+ )
);
// Calculate the ratio of the new zoom to the original zoom
const zoomRatio = newZoom / this.state.cameraPosition[2];
// Update camera position and zoom level
- this.state.cameraPosition[0] = this.state.mousePosition[0] -
- (this.state.mousePosition[0] - this.state.cameraPosition[0]) /
- zoomRatio;
- this.state.cameraPosition[1] = this.state.mousePosition[1] -
- (this.state.mousePosition[1] - this.state.cameraPosition[1]) /
- zoomRatio,
- this.state.cameraPosition[2] = newZoom;
+ this.state.cameraPosition[0] = this.state.mousePosition[0]
+ - (this.state.mousePosition[0] - this.state.cameraPosition[0])
+ / zoomRatio;
+ this.state.cameraPosition[1] = this.state.mousePosition[1]
+ - (this.state.mousePosition[1] - this.state.cameraPosition[1])
+ / zoomRatio, this.state.cameraPosition[2] = newZoom;
}
-
}
diff --git a/app/src/lib/graph-interface/helpers/index.ts b/app/src/lib/graph-interface/helpers/index.ts
index f3f9d0f..8a26774 100644
--- a/app/src/lib/graph-interface/helpers/index.ts
+++ b/app/src/lib/graph-interface/helpers/index.ts
@@ -8,7 +8,7 @@ export function lerp(a: number, b: number, t: number) {
export function animate(
duration: number,
- callback: (progress: number) => void | false,
+ callback: (progress: number) => void | false
) {
const start = performance.now();
const loop = (time: number) => {
@@ -33,41 +33,37 @@ export function createNodePath({
cornerBottom = 0,
leftBump = false,
rightBump = false,
- aspectRatio = 1,
+ aspectRatio = 1
} = {}) {
return `M0,${cornerTop}
- ${
- cornerTop
- ? ` V${cornerTop}
+ ${cornerTop
+ ? ` V${cornerTop}
Q0,0 ${cornerTop * aspectRatio},0
H${100 - cornerTop * aspectRatio}
Q100,0 100,${cornerTop}
`
- : ` V0
+ : ` V0
H100
`
- }
+ }
V${y - height / 2}
- ${
- rightBump
- ? ` C${100 - depth},${y - height / 2} ${100 - depth},${y + height / 2} 100,${y + height / 2}`
- : ` H100`
- }
- ${
- cornerBottom
- ? ` V${100 - cornerBottom}
+ ${rightBump
+ ? ` C${100 - depth},${y - height / 2} ${100 - depth},${y + height / 2} 100,${y + height / 2}`
+ : ` H100`
+ }
+ ${cornerBottom
+ ? ` V${100 - cornerBottom}
Q100,100 ${100 - cornerBottom * aspectRatio},100
H${cornerBottom * aspectRatio}
Q0,100 0,${100 - cornerBottom}
`
- : `${leftBump ? `V100 H0` : `V100`}`
- }
- ${
- leftBump
- ? ` V${y + height / 2} C${depth},${y + height / 2} ${depth},${y - height / 2} 0,${y - height / 2}`
- : ` H0`
- }
- Z`.replace(/\s+/g, " ");
+ : `${leftBump ? `V100 H0` : `V100`}`
+ }
+ ${leftBump
+ ? ` V${y + height / 2} C${depth},${y + height / 2} ${depth},${y - height / 2} 0,${y - height / 2}`
+ : ` H0`
+ }
+ Z`.replace(/\s+/g, ' ');
}
export const debounce = (fn: Function, ms = 300) => {
@@ -78,14 +74,13 @@ export const debounce = (fn: Function, ms = 300) => {
};
};
-export const clone: (v: T) => T =
- "structedClone" in globalThis
- ? globalThis.structuredClone
- : (obj) => JSON.parse(JSON.stringify(obj));
+export const clone: (v: T) => T = 'structedClone' in globalThis
+ ? globalThis.structuredClone
+ : (obj) => JSON.parse(JSON.stringify(obj));
export function withSubComponents>(
component: A,
- subcomponents: B,
+ subcomponents: B
): A & B {
Object.keys(subcomponents).forEach((key) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -93,3 +88,27 @@ export function withSubComponents>(
});
return component as A & B;
}
+
+export function distanceFromPointToSegment(
+ x1: number,
+ y1: number,
+ x2: number,
+ y2: number,
+ x0: number,
+ y0: number
+): number {
+ const dx = x2 - x1;
+ const dy = y2 - y1;
+
+ if (dx === 0 && dy === 0) {
+ return Math.hypot(x0 - x1, y0 - y1);
+ }
+
+ const t = ((x0 - x1) * dx + (y0 - y1) * dy) / (dx * dx + dy * dy);
+ const clampedT = Math.max(0, Math.min(1, t));
+
+ const px = x1 + clampedT * dx;
+ const py = y1 + clampedT * dy;
+
+ return Math.hypot(x0 - px, y0 - py);
+}
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
index 962f6bf..476c9e0 100644
--- a/packages/types/src/index.ts
+++ b/packages/types/src/index.ts
@@ -1,18 +1,5 @@
-export type { NodeInput } from "./inputs";
-export type {
- NodeRegistry,
- RuntimeExecutor,
- SyncCache,
- AsyncCache,
-} from "./components";
-export type {
- SerializedNode,
- NodeInstance,
- NodeDefinition,
- Socket,
- NodeId,
- Edge,
- Graph,
-} from "./types";
-export { NodeSchema, GraphSchema } from "./types";
-export { NodeDefinitionSchema } from "./types";
+export type { AsyncCache, NodeRegistry, RuntimeExecutor, SyncCache } from './components';
+export type { NodeInput } from './inputs';
+export type { Box, Edge, Graph, NodeDefinition, NodeId, NodeInstance, SerializedNode, Socket } from './types';
+export { GraphSchema, NodeSchema } from './types';
+export { NodeDefinitionSchema } from './types';
diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts
index 278b7f6..2a9167b 100644
--- a/packages/types/src/types.ts
+++ b/packages/types/src/types.ts
@@ -1,9 +1,16 @@
-import { z } from "zod";
-import { NodeInputSchema } from "./inputs";
+import { z } from 'zod';
+import { NodeInputSchema } from './inputs';
+
+export type Box = {
+ minX: number;
+ maxX: number;
+ minY: number;
+ maxY: number;
+};
export const NodeIdSchema = z
.string()
- .regex(/^[^/]+\/[^/]+\/[^/]+$/, "Invalid NodeId format")
+ .regex(/^[^/]+\/[^/]+\/[^/]+$/, 'Invalid NodeId format')
.transform((value) => value as `${string}/${string}/${string}`);
export type NodeId = z.infer;
@@ -35,9 +42,9 @@ export const NodeDefinitionSchema = z.object({
meta: z
.object({
description: z.string().optional(),
- title: z.string().optional(),
+ title: z.string().optional()
})
- .optional(),
+ .optional()
});
export const NodeSchema = z.object({
@@ -49,13 +56,12 @@ export const NodeSchema = z.object({
meta: z
.object({
title: z.string().optional(),
- lastModified: z.string().optional(),
+ lastModified: z.string().optional()
})
.optional(),
- position: z.tuple([z.number(), z.number()]),
+ position: z.tuple([z.number(), z.number()])
});
-
export type SerializedNode = z.infer;
export type NodeDefinition = z.infer & {
@@ -75,12 +81,12 @@ export const GraphSchema = z.object({
meta: z
.object({
title: z.string().optional(),
- lastModified: z.string().optional(),
+ lastModified: z.string().optional()
})
.optional(),
settings: z.record(z.string(), z.any()).optional(),
nodes: z.array(NodeSchema),
- edges: z.array(z.tuple([z.number(), z.number(), z.number(), z.string()])),
+ edges: z.array(z.tuple([z.number(), z.number(), z.number(), z.string()]))
});
export type Graph = z.infer;