feat/remove-wasm-bindgen #19
59
.dprint.jsonc
Normal file
59
.dprint.jsonc
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://dprint.dev/schemas/v0.json",
|
||||||
|
"indentWidth": 2,
|
||||||
|
"typescript": {
|
||||||
|
// https://dprint.dev/plugins/typescript/config/
|
||||||
|
"quoteStyle": "preferSingle",
|
||||||
|
"trailingCommas": "never"
|
||||||
|
},
|
||||||
|
"json": {
|
||||||
|
},
|
||||||
|
"markdown": {
|
||||||
|
},
|
||||||
|
"toml": {
|
||||||
|
},
|
||||||
|
"dockerfile": {
|
||||||
|
},
|
||||||
|
"ruff": {
|
||||||
|
},
|
||||||
|
"jupyter": {
|
||||||
|
},
|
||||||
|
"malva": {
|
||||||
|
},
|
||||||
|
"markup": {
|
||||||
|
},
|
||||||
|
"yaml": {
|
||||||
|
},
|
||||||
|
"graphql": {
|
||||||
|
},
|
||||||
|
"exec": {
|
||||||
|
"cwd": "${configDir}",
|
||||||
|
"commands": [{
|
||||||
|
"command": "rustfmt --edition 2024",
|
||||||
|
"exts": ["rs"],
|
||||||
|
// add the config files for automatic cache invalidation when the rust version or rustfmt config changes
|
||||||
|
"cacheKeyFiles": [
|
||||||
|
"rustfmt.toml",
|
||||||
|
"rust-toolchain.toml"
|
||||||
|
]
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
"excludes": [
|
||||||
|
"**/node_modules",
|
||||||
|
"**/*-lock.yaml"
|
||||||
|
],
|
||||||
|
"plugins": [
|
||||||
|
"https://plugins.dprint.dev/typescript-0.95.13.wasm",
|
||||||
|
"https://plugins.dprint.dev/json-0.21.1.wasm",
|
||||||
|
"https://plugins.dprint.dev/markdown-0.20.0.wasm",
|
||||||
|
"https://plugins.dprint.dev/toml-0.7.0.wasm",
|
||||||
|
"https://plugins.dprint.dev/dockerfile-0.3.3.wasm",
|
||||||
|
"https://plugins.dprint.dev/ruff-0.6.11.wasm",
|
||||||
|
"https://plugins.dprint.dev/jupyter-0.2.1.wasm",
|
||||||
|
"https://plugins.dprint.dev/g-plane/malva-v0.15.1.wasm",
|
||||||
|
"https://plugins.dprint.dev/g-plane/markup_fmt-v0.25.3.wasm",
|
||||||
|
"https://plugins.dprint.dev/g-plane/pretty_yaml-v0.5.1.wasm",
|
||||||
|
"https://plugins.dprint.dev/g-plane/pretty_graphql-v0.2.3.wasm",
|
||||||
|
"https://plugins.dprint.dev/exec-0.6.0.json@a054130d458f124f9b5c91484833828950723a5af3f8ff2bd1523bd47b83b364"
|
||||||
|
]
|
||||||
|
}
|
||||||
1576
app/pnpm-lock.yaml
generated
1576
app/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,19 +1,44 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { MeshLineGeometry, MeshLineMaterial } from '@threlte/extras';
|
import { MeshLineGeometry, MeshLineMaterial } from "@threlte/extras";
|
||||||
import { points, lines } from './store.js';
|
import { points, lines, rects } from "./store.js";
|
||||||
import { T } from '@threlte/core';
|
import { T } from "@threlte/core";
|
||||||
|
import { Color } from "three";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#each $points as point}
|
{#each $points as point}
|
||||||
<T.Mesh position.x={point.x} position.y={point.y} position.z={point.z} rotation.x={-Math.PI / 2}>
|
<T.Mesh
|
||||||
<T.CircleGeometry args={[0.2, 32]} />
|
position.x={point.x}
|
||||||
<T.MeshBasicMaterial color="red" />
|
position.y={point.y}
|
||||||
</T.Mesh>
|
position.z={point.z}
|
||||||
|
rotation.x={-Math.PI / 2}
|
||||||
|
>
|
||||||
|
<T.CircleGeometry args={[0.2, 32]} />
|
||||||
|
<T.MeshBasicMaterial color="red" />
|
||||||
|
</T.Mesh>
|
||||||
|
{/each}
|
||||||
|
|
||||||
|
{#each $rects as rect, i}
|
||||||
|
<T.Mesh
|
||||||
|
position.x={(rect.minX + rect.maxX) / 2}
|
||||||
|
position.y={0}
|
||||||
|
position.z={(rect.minY + rect.maxY) / 2}
|
||||||
|
rotation.x={-Math.PI / 2}
|
||||||
|
>
|
||||||
|
<T.PlaneGeometry args={[rect.maxX - rect.minX, rect.maxY - rect.minY]} />
|
||||||
|
<T.MeshBasicMaterial
|
||||||
|
color={new Color().setHSL((i * 1.77) % 1, 1, 0.5)}
|
||||||
|
opacity={0.9}
|
||||||
|
/>
|
||||||
|
</T.Mesh>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
{#each $lines as line}
|
{#each $lines as line}
|
||||||
<T.Mesh>
|
<T.Mesh position.y={1}>
|
||||||
<MeshLineGeometry points={line} />
|
<MeshLineGeometry points={line.points} />
|
||||||
<MeshLineMaterial color="red" linewidth={1} attenuate={false} />
|
<MeshLineMaterial
|
||||||
</T.Mesh>
|
color={line.color || "red"}
|
||||||
|
linewidth={1}
|
||||||
|
attenuate={false}
|
||||||
|
/>
|
||||||
|
</T.Mesh>
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import { Vector3 } from "three/src/math/Vector3.js";
|
import type { Box } from '@nodarium/types';
|
||||||
import { lines, points } from "./store";
|
import { Vector3 } from 'three/src/math/Vector3.js';
|
||||||
|
import Component from './Debug.svelte';
|
||||||
|
import { lines, points, rects } from './store';
|
||||||
|
|
||||||
export function debugPosition(x: number, y: number) {
|
export function debugPosition(x: number, y: number) {
|
||||||
points.update((p) => {
|
points.update((p) => {
|
||||||
@@ -8,18 +10,28 @@ export function debugPosition(x: number, y: number) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function debugRect(rect: Box) {
|
||||||
|
console.log(rect);
|
||||||
|
rects.update((r) => {
|
||||||
|
r.push(rect);
|
||||||
|
return r;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function clear() {
|
export function clear() {
|
||||||
points.set([]);
|
points.set([]);
|
||||||
lines.set([]);
|
lines.set([]);
|
||||||
|
rects.set([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function debugLine(line: Vector3[]) {
|
export function debugLine(points: Vector3[], color?: Color) {
|
||||||
lines.update((l) => {
|
lines.update((l) => {
|
||||||
l.push(line);
|
l.push({ points, color });
|
||||||
return l;
|
return l;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
import Component from "./Debug.svelte";
|
export default Component;
|
||||||
|
export function clearLines() {
|
||||||
export default Component
|
lines.set([]);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
import { writable } from "svelte/store";
|
import type { Box } from '@nodarium/types';
|
||||||
import { Vector3 } from "three/src/math/Vector3.js";
|
import { writable } from 'svelte/store';
|
||||||
|
import type { Color } from 'three';
|
||||||
|
import { Vector3 } from 'three/src/math/Vector3.js';
|
||||||
|
|
||||||
export const points = writable<Vector3[]>([]);
|
export const points = writable<Vector3[]>([]);
|
||||||
|
export const rects = writable<Box[]>([]);
|
||||||
export const lines = writable<Vector3[][]>([]);
|
export const lines = writable<{ points: Vector3[]; color?: Color }[]>([]);
|
||||||
|
|||||||
@@ -31,6 +31,10 @@
|
|||||||
import { CubicBezierCurve } from "three/src/extras/curves/CubicBezierCurve.js";
|
import { CubicBezierCurve } from "three/src/extras/curves/CubicBezierCurve.js";
|
||||||
import { Vector2 } from "three/src/math/Vector2.js";
|
import { Vector2 } from "three/src/math/Vector2.js";
|
||||||
import { appSettings } from "$lib/settings/app-settings.svelte";
|
import { appSettings } from "$lib/settings/app-settings.svelte";
|
||||||
|
import { getGraphState } from "../graph-state.svelte";
|
||||||
|
import { onDestroy } from "svelte";
|
||||||
|
|
||||||
|
const graphState = getGraphState();
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
x1: number;
|
x1: number;
|
||||||
@@ -38,20 +42,21 @@
|
|||||||
x2: number;
|
x2: number;
|
||||||
y2: number;
|
y2: number;
|
||||||
z: number;
|
z: number;
|
||||||
|
id?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const { x1, y1, x2, y2, z }: Props = $props();
|
const { x1, y1, x2, y2, z, id }: Props = $props();
|
||||||
|
|
||||||
const thickness = $derived(Math.max(0.001, 0.00082 * Math.exp(0.055 * z)));
|
const thickness = $derived(Math.max(0.001, 0.00082 * Math.exp(0.055 * z)));
|
||||||
|
|
||||||
let points = $state<Vector3[]>([]);
|
let points = $state<Vector3[]>([]);
|
||||||
|
|
||||||
let lastId: string | null = null;
|
let lastId: string | null = null;
|
||||||
|
const curveId = $derived(`${x1}-${y1}-${x2}-${y2}`);
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
const new_x = x2 - x1;
|
const new_x = x2 - x1;
|
||||||
const new_y = y2 - y1;
|
const new_y = y2 - y1;
|
||||||
const curveId = `${x1}-${y1}-${x2}-${y2}`;
|
|
||||||
if (lastId === curveId) {
|
if (lastId === curveId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -72,6 +77,15 @@
|
|||||||
.getPoints(samples)
|
.getPoints(samples)
|
||||||
.map((p) => new Vector3(p.x, 0, p.y))
|
.map((p) => new Vector3(p.x, 0, p.y))
|
||||||
.flat();
|
.flat();
|
||||||
|
|
||||||
|
if (id) {
|
||||||
|
graphState.setEdgeGeometry(
|
||||||
|
id,
|
||||||
|
x1,
|
||||||
|
y1,
|
||||||
|
$state.snapshot(points) as unknown as Vector3[],
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
@@ -79,6 +93,10 @@
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (id) graphState.removeEdgeGeometry(id);
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<T.Mesh
|
<T.Mesh
|
||||||
@@ -101,6 +119,18 @@
|
|||||||
<T.CircleGeometry args={[0.5, 16]} />
|
<T.CircleGeometry args={[0.5, 16]} />
|
||||||
</T.Mesh>
|
</T.Mesh>
|
||||||
|
|
||||||
|
{#if graphState.hoveredEdgeId === id}
|
||||||
|
<T.Mesh position.x={x1} position.z={y1} position.y={0.1}>
|
||||||
|
<MeshLineGeometry {points} />
|
||||||
|
<MeshLineMaterial
|
||||||
|
width={thickness * 5}
|
||||||
|
color={lineColor}
|
||||||
|
opacity={0.5}
|
||||||
|
transparent
|
||||||
|
/>
|
||||||
|
</T.Mesh>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<T.Mesh position.x={x1} position.z={y1} position.y={0.1}>
|
<T.Mesh position.x={x1} position.z={y1} position.y={0.1}>
|
||||||
<MeshLineGeometry {points} />
|
<MeshLineGeometry {points} />
|
||||||
<MeshLineMaterial width={thickness} color={lineColor} />
|
<MeshLineMaterial width={thickness} color={lineColor} />
|
||||||
|
|||||||
@@ -1,31 +1,30 @@
|
|||||||
|
import throttle from '$lib/helpers/throttle';
|
||||||
import type {
|
import type {
|
||||||
Edge,
|
Edge,
|
||||||
Graph,
|
Graph,
|
||||||
NodeInstance,
|
|
||||||
NodeDefinition,
|
NodeDefinition,
|
||||||
NodeInput,
|
|
||||||
NodeRegistry,
|
|
||||||
NodeId,
|
NodeId,
|
||||||
Socket,
|
NodeInput,
|
||||||
} from "@nodarium/types";
|
NodeInstance,
|
||||||
import { fastHashString } from "@nodarium/utils";
|
NodeRegistry,
|
||||||
import { SvelteMap } from "svelte/reactivity";
|
Socket
|
||||||
import EventEmitter from "./helpers/EventEmitter";
|
} from '@nodarium/types';
|
||||||
import { createLogger } from "@nodarium/utils";
|
import { fastHashString } from '@nodarium/utils';
|
||||||
import throttle from "$lib/helpers/throttle";
|
import { createLogger } from '@nodarium/utils';
|
||||||
import { HistoryManager } from "./history-manager";
|
import { SvelteMap } from 'svelte/reactivity';
|
||||||
|
import EventEmitter from './helpers/EventEmitter';
|
||||||
|
import { HistoryManager } from './history-manager';
|
||||||
|
|
||||||
const logger = createLogger("graph-manager");
|
const logger = createLogger('graph-manager');
|
||||||
logger.mute();
|
logger.mute();
|
||||||
|
|
||||||
const clone =
|
const clone = 'structuredClone' in self
|
||||||
"structuredClone" in self
|
? self.structuredClone
|
||||||
? self.structuredClone
|
: (args: any) => JSON.parse(JSON.stringify(args));
|
||||||
: (args: any) => JSON.parse(JSON.stringify(args));
|
|
||||||
|
|
||||||
function areSocketsCompatible(
|
function areSocketsCompatible(
|
||||||
output: string | undefined,
|
output: string | undefined,
|
||||||
inputs: string | (string | undefined)[] | undefined,
|
inputs: string | (string | undefined)[] | undefined
|
||||||
) {
|
) {
|
||||||
if (Array.isArray(inputs) && output) {
|
if (Array.isArray(inputs) && output) {
|
||||||
return inputs.includes(output);
|
return inputs.includes(output);
|
||||||
@@ -34,24 +33,23 @@ function areSocketsCompatible(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function areEdgesEqual(firstEdge: Edge, secondEdge: Edge) {
|
function areEdgesEqual(firstEdge: Edge, secondEdge: Edge) {
|
||||||
|
|
||||||
if (firstEdge[0].id !== secondEdge[0].id) {
|
if (firstEdge[0].id !== secondEdge[0].id) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstEdge[1] !== secondEdge[1]) {
|
if (firstEdge[1] !== secondEdge[1]) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstEdge[2].id !== secondEdge[2].id) {
|
if (firstEdge[2].id !== secondEdge[2].id) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (firstEdge[3] !== secondEdge[3]) {
|
if (firstEdge[3] !== secondEdge[3]) {
|
||||||
return false
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GraphManager extends EventEmitter<{
|
export class GraphManager extends EventEmitter<{
|
||||||
@@ -62,7 +60,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
values: Record<string, unknown>;
|
values: Record<string, unknown>;
|
||||||
};
|
};
|
||||||
}> {
|
}> {
|
||||||
status = $state<"loading" | "idle" | "error">();
|
status = $state<'loading' | 'idle' | 'error'>();
|
||||||
loaded = false;
|
loaded = false;
|
||||||
|
|
||||||
graph: Graph = { id: 0, nodes: [], edges: [] };
|
graph: Graph = { id: 0, nodes: [], edges: [] };
|
||||||
@@ -88,7 +86,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
history: HistoryManager = new HistoryManager();
|
history: HistoryManager = new HistoryManager();
|
||||||
execute = throttle(() => {
|
execute = throttle(() => {
|
||||||
if (this.loaded === false) return;
|
if (this.loaded === false) return;
|
||||||
this.emit("result", this.serialize());
|
this.emit('result', this.serialize());
|
||||||
}, 10);
|
}, 10);
|
||||||
|
|
||||||
constructor(public registry: NodeRegistry) {
|
constructor(public registry: NodeRegistry) {
|
||||||
@@ -100,21 +98,21 @@ export class GraphManager extends EventEmitter<{
|
|||||||
id: node.id,
|
id: node.id,
|
||||||
position: [...node.position],
|
position: [...node.position],
|
||||||
type: node.type,
|
type: node.type,
|
||||||
props: node.props,
|
props: node.props
|
||||||
})) as NodeInstance[];
|
})) as NodeInstance[];
|
||||||
const edges = this.edges.map((edge) => [
|
const edges = this.edges.map((edge) => [
|
||||||
edge[0].id,
|
edge[0].id,
|
||||||
edge[1],
|
edge[1],
|
||||||
edge[2].id,
|
edge[2].id,
|
||||||
edge[3],
|
edge[3]
|
||||||
]) as Graph["edges"];
|
]) as Graph['edges'];
|
||||||
const serialized = {
|
const serialized = {
|
||||||
id: this.graph.id,
|
id: this.graph.id,
|
||||||
settings: $state.snapshot(this.settings),
|
settings: $state.snapshot(this.settings),
|
||||||
nodes,
|
nodes,
|
||||||
edges,
|
edges
|
||||||
};
|
};
|
||||||
logger.log("serializing graph", serialized);
|
logger.log('serializing graph', serialized);
|
||||||
return clone($state.snapshot(serialized));
|
return clone($state.snapshot(serialized));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,6 +146,95 @@ export class GraphManager extends EventEmitter<{
|
|||||||
return [...nodes.values()];
|
return [...nodes.values()];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEdgeId(e: Edge) {
|
||||||
|
return `${e[0].id}-${e[1]}-${e[2].id}-${e[3]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
getEdgeById(id: string): Edge | undefined {
|
||||||
|
return this.edges.find((e) => this.getEdgeId(e) === id);
|
||||||
|
}
|
||||||
|
|
||||||
|
dropNodeOnEdge(nodeId: number, edge: Edge) {
|
||||||
|
const draggedNode = this.getNode(nodeId);
|
||||||
|
if (!draggedNode || !draggedNode.state?.type) return;
|
||||||
|
|
||||||
|
const [fromNode, fromSocketIdx, toNode, toSocketKey] = edge;
|
||||||
|
|
||||||
|
const draggedInputs = Object.entries(draggedNode.state.type.inputs ?? {});
|
||||||
|
const draggedOutputs = draggedNode.state.type.outputs ?? [];
|
||||||
|
|
||||||
|
const edgeOutputSocketType = fromNode.state?.type?.outputs?.[fromSocketIdx];
|
||||||
|
const targetInput = toNode.state?.type?.inputs?.[toSocketKey];
|
||||||
|
const targetAcceptedTypes = [targetInput?.type, ...(targetInput?.accepts || [])];
|
||||||
|
|
||||||
|
const bestInputEntry = draggedInputs.find(([_, input]) => {
|
||||||
|
const accepted = [input.type, ...(input.accepts || [])];
|
||||||
|
return areSocketsCompatible(edgeOutputSocketType, accepted);
|
||||||
|
});
|
||||||
|
|
||||||
|
const bestOutputIdx = draggedOutputs.findIndex(outputType => areSocketsCompatible(outputType, targetAcceptedTypes));
|
||||||
|
|
||||||
|
if (!bestInputEntry || bestOutputIdx === -1) {
|
||||||
|
logger.error('Could not find compatible sockets for drop');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.startUndoGroup();
|
||||||
|
|
||||||
|
this.removeEdge(edge, { applyDeletion: false });
|
||||||
|
|
||||||
|
this.createEdge(fromNode, fromSocketIdx, draggedNode, bestInputEntry[0], {
|
||||||
|
applyUpdate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.createEdge(draggedNode, bestOutputIdx, toNode, toSocketKey, {
|
||||||
|
applyUpdate: false
|
||||||
|
});
|
||||||
|
|
||||||
|
this.saveUndoGroup();
|
||||||
|
this.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
getPossibleDropOnEdges(nodeId: number): Edge[] {
|
||||||
|
const draggedNode = this.getNode(nodeId);
|
||||||
|
if (!draggedNode || !draggedNode.state?.type) return [];
|
||||||
|
|
||||||
|
const draggedInputs = Object.values(draggedNode.state.type.inputs ?? {});
|
||||||
|
const draggedOutputs = draggedNode.state.type.outputs ?? [];
|
||||||
|
|
||||||
|
// Optimization: Pre-calculate parents to avoid cycles
|
||||||
|
const parentIds = new Set(this.getParentsOfNode(draggedNode).map(n => n.id));
|
||||||
|
|
||||||
|
return this.edges.filter((edge) => {
|
||||||
|
const [fromNode, fromSocketIdx, toNode, toSocketKey] = edge;
|
||||||
|
|
||||||
|
// 1. Prevent cycles: If the target node is already a parent, we can't drop here
|
||||||
|
if (parentIds.has(toNode.id)) return false;
|
||||||
|
|
||||||
|
// 2. Prevent self-dropping: Don't drop on edges already connected to this node
|
||||||
|
if (fromNode.id === nodeId || toNode.id === nodeId) return false;
|
||||||
|
|
||||||
|
// 3. Check if edge.source can plug into ANY draggedNode.input
|
||||||
|
const edgeOutputSocketType = fromNode.state?.type?.outputs?.[fromSocketIdx];
|
||||||
|
const canPlugIntoDragged = draggedInputs.some(input => {
|
||||||
|
const acceptedTypes = [input.type, ...(input.accepts || [])];
|
||||||
|
return areSocketsCompatible(edgeOutputSocketType, acceptedTypes);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!canPlugIntoDragged) return false;
|
||||||
|
|
||||||
|
// 4. Check if ANY draggedNode.output can plug into edge.target
|
||||||
|
const targetInput = toNode.state?.type?.inputs?.[toSocketKey];
|
||||||
|
const targetAcceptedTypes = [targetInput?.type, ...(targetInput?.accepts || [])];
|
||||||
|
|
||||||
|
const draggedCanPlugIntoTarget = draggedOutputs.some(outputType =>
|
||||||
|
areSocketsCompatible(outputType, targetAcceptedTypes)
|
||||||
|
);
|
||||||
|
|
||||||
|
return draggedCanPlugIntoTarget;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
getEdgesBetweenNodes(nodes: NodeInstance[]): [number, number, number, string][] {
|
getEdgesBetweenNodes(nodes: NodeInstance[]): [number, number, number, string][] {
|
||||||
const edges = [];
|
const edges = [];
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
@@ -155,14 +242,14 @@ export class GraphManager extends EventEmitter<{
|
|||||||
for (const child of children) {
|
for (const child of children) {
|
||||||
if (nodes.includes(child)) {
|
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,
|
(e) => e[0].id === node.id && e[2].id === child.id
|
||||||
);
|
);
|
||||||
if (edge) {
|
if (edge) {
|
||||||
edges.push([edge[0].id, edge[1], edge[2].id, edge[3]] as [
|
edges.push([edge[0].id, edge[1], edge[2].id, edge[3]] as [
|
||||||
number,
|
number,
|
||||||
number,
|
number,
|
||||||
number,
|
number,
|
||||||
string,
|
string
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -179,18 +266,18 @@ export class GraphManager extends EventEmitter<{
|
|||||||
const n = node as NodeInstance;
|
const n = node as NodeInstance;
|
||||||
if (nodeType) {
|
if (nodeType) {
|
||||||
n.state = {
|
n.state = {
|
||||||
type: nodeType,
|
type: nodeType
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return [node.id, n];
|
return [node.id, n];
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
const edges = graph.edges.map((edge) => {
|
const edges = graph.edges.map((edge) => {
|
||||||
const from = nodes.get(edge[0]);
|
const from = nodes.get(edge[0]);
|
||||||
const to = nodes.get(edge[2]);
|
const to = nodes.get(edge[2]);
|
||||||
if (!from || !to) {
|
if (!from || !to) {
|
||||||
throw new Error("Edge references non-existing node");
|
throw new Error('Edge references non-existing node');
|
||||||
}
|
}
|
||||||
from.state.children = from.state.children || [];
|
from.state.children = from.state.children || [];
|
||||||
from.state.children.push(to);
|
from.state.children.push(to);
|
||||||
@@ -214,21 +301,21 @@ export class GraphManager extends EventEmitter<{
|
|||||||
|
|
||||||
this.loaded = false;
|
this.loaded = false;
|
||||||
this.graph = graph;
|
this.graph = graph;
|
||||||
this.status = "loading";
|
this.status = 'loading';
|
||||||
this.id = graph.id;
|
this.id = graph.id;
|
||||||
|
|
||||||
logger.info("loading graph", $state.snapshot(graph));
|
logger.info('loading graph', $state.snapshot(graph));
|
||||||
|
|
||||||
const nodeIds = Array.from(new Set([...graph.nodes.map((n) => n.type)]));
|
const nodeIds = Array.from(new Set([...graph.nodes.map((n) => n.type)]));
|
||||||
await this.registry.load(nodeIds);
|
await this.registry.load(nodeIds);
|
||||||
|
|
||||||
logger.info("loaded node types", this.registry.getAllNodes());
|
logger.info('loaded node types', this.registry.getAllNodes());
|
||||||
|
|
||||||
for (const node of this.graph.nodes) {
|
for (const node of this.graph.nodes) {
|
||||||
const nodeType = this.registry.getNode(node.type);
|
const nodeType = this.registry.getNode(node.type);
|
||||||
if (!nodeType) {
|
if (!nodeType) {
|
||||||
logger.error(`Node type not found: ${node.type}`);
|
logger.error(`Node type not found: ${node.type}`);
|
||||||
this.status = "error";
|
this.status = 'error';
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Turn into runtime node
|
// Turn into runtime node
|
||||||
@@ -253,11 +340,11 @@ export class GraphManager extends EventEmitter<{
|
|||||||
settingTypes[settingId] = {
|
settingTypes[settingId] = {
|
||||||
__node_type: type.id,
|
__node_type: type.id,
|
||||||
__node_input: key,
|
__node_input: key,
|
||||||
...type.inputs[key],
|
...type.inputs[key]
|
||||||
};
|
};
|
||||||
if (
|
if (
|
||||||
settingValues[settingId] === undefined &&
|
settingValues[settingId] === undefined
|
||||||
"value" in type.inputs[key]
|
&& 'value' in type.inputs[key]
|
||||||
) {
|
) {
|
||||||
settingValues[settingId] = type.inputs[key].value;
|
settingValues[settingId] = type.inputs[key].value;
|
||||||
}
|
}
|
||||||
@@ -267,14 +354,14 @@ export class GraphManager extends EventEmitter<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.settings = settingValues;
|
this.settings = settingValues;
|
||||||
this.emit("settings", { types: settingTypes, values: settingValues });
|
this.emit('settings', { types: settingTypes, values: settingValues });
|
||||||
|
|
||||||
this.history.reset();
|
this.history.reset();
|
||||||
this._init(this.graph);
|
this._init(this.graph);
|
||||||
|
|
||||||
this.save();
|
this.save();
|
||||||
|
|
||||||
this.status = "idle";
|
this.status = 'idle';
|
||||||
|
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
logger.log(`Graph loaded in ${performance.now() - a}ms`);
|
logger.log(`Graph loaded in ${performance.now() - a}ms`);
|
||||||
@@ -307,9 +394,9 @@ export class GraphManager extends EventEmitter<{
|
|||||||
if (settingId) {
|
if (settingId) {
|
||||||
settingTypes[settingId] = nodeType.inputs[key];
|
settingTypes[settingId] = nodeType.inputs[key];
|
||||||
if (
|
if (
|
||||||
settingValues &&
|
settingValues
|
||||||
settingValues?.[settingId] === undefined &&
|
&& settingValues?.[settingId] === undefined
|
||||||
"value" in nodeType.inputs[key]
|
&& 'value' in nodeType.inputs[key]
|
||||||
) {
|
) {
|
||||||
settingValues[settingId] = nodeType.inputs[key].value;
|
settingValues[settingId] = nodeType.inputs[key].value;
|
||||||
}
|
}
|
||||||
@@ -319,7 +406,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
|
|
||||||
this.settings = settingValues;
|
this.settings = settingValues;
|
||||||
this.settingTypes = settingTypes;
|
this.settingTypes = settingTypes;
|
||||||
this.emit("settings", { types: settingTypes, values: settingValues });
|
this.emit('settings', { types: settingTypes, values: settingValues });
|
||||||
}
|
}
|
||||||
|
|
||||||
getChildren(node: NodeInstance) {
|
getChildren(node: NodeInstance) {
|
||||||
@@ -368,7 +455,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
const inputType = to?.state?.type?.inputs?.[toSocket]?.type;
|
const inputType = to?.state?.type?.inputs?.[toSocket]?.type;
|
||||||
if (outputType === inputType) {
|
if (outputType === inputType) {
|
||||||
this.createEdge(from, fromSocket, to, toSocket, {
|
this.createEdge(from, fromSocket, to, toSocket, {
|
||||||
applyUpdate: false,
|
applyUpdate: false
|
||||||
});
|
});
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -403,7 +490,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
// map old ids to new ids
|
// map old ids to new ids
|
||||||
const idMap = new Map<number, number>();
|
const idMap = new Map<number, number>();
|
||||||
|
|
||||||
let startId = this.createNodeId()
|
let startId = this.createNodeId();
|
||||||
|
|
||||||
nodes = nodes.map((node) => {
|
nodes = nodes.map((node) => {
|
||||||
const id = startId++;
|
const id = startId++;
|
||||||
@@ -420,7 +507,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
const to = nodes.find((n) => n.id === idMap.get(edge[2]));
|
const to = nodes.find((n) => n.id === idMap.get(edge[2]));
|
||||||
|
|
||||||
if (!from || !to) {
|
if (!from || !to) {
|
||||||
throw new Error("Edge references non-existing node");
|
throw new Error('Edge references non-existing node');
|
||||||
}
|
}
|
||||||
|
|
||||||
to.state.parents = to.state.parents || [];
|
to.state.parents = to.state.parents || [];
|
||||||
@@ -445,11 +532,11 @@ export class GraphManager extends EventEmitter<{
|
|||||||
createNode({
|
createNode({
|
||||||
type,
|
type,
|
||||||
position,
|
position,
|
||||||
props = {},
|
props = {}
|
||||||
}: {
|
}: {
|
||||||
type: NodeInstance["type"];
|
type: NodeInstance['type'];
|
||||||
position: NodeInstance["position"];
|
position: NodeInstance['position'];
|
||||||
props: NodeInstance["props"];
|
props: NodeInstance['props'];
|
||||||
}) {
|
}) {
|
||||||
const nodeType = this.registry.getNode(type);
|
const nodeType = this.registry.getNode(type);
|
||||||
if (!nodeType) {
|
if (!nodeType) {
|
||||||
@@ -462,14 +549,14 @@ export class GraphManager extends EventEmitter<{
|
|||||||
type,
|
type,
|
||||||
position,
|
position,
|
||||||
state: { type: nodeType },
|
state: { type: nodeType },
|
||||||
props,
|
props
|
||||||
});
|
});
|
||||||
|
|
||||||
this.nodes.set(node.id, node);
|
this.nodes.set(node.id, node);
|
||||||
|
|
||||||
this.save();
|
this.save();
|
||||||
|
|
||||||
return node
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
createEdge(
|
createEdge(
|
||||||
@@ -477,17 +564,16 @@ export class GraphManager extends EventEmitter<{
|
|||||||
fromSocket: number,
|
fromSocket: number,
|
||||||
to: NodeInstance,
|
to: NodeInstance,
|
||||||
toSocket: string,
|
toSocket: string,
|
||||||
{ applyUpdate = true } = {},
|
{ applyUpdate = true } = {}
|
||||||
): Edge | undefined {
|
): Edge | undefined {
|
||||||
|
|
||||||
const existingEdges = this.getEdgesToNode(to);
|
const existingEdges = this.getEdgesToNode(to);
|
||||||
|
|
||||||
// check if this exact edge already exists
|
// check if this exact edge already exists
|
||||||
const existingEdge = existingEdges.find(
|
const existingEdge = existingEdges.find(
|
||||||
(e) => e[0].id === from.id && e[1] === fromSocket && e[3] === toSocket,
|
(e) => e[0].id === from.id && e[1] === fromSocket && e[3] === toSocket
|
||||||
);
|
);
|
||||||
if (existingEdge) {
|
if (existingEdge) {
|
||||||
logger.error("Edge already exists", existingEdge);
|
logger.error('Edge already exists', existingEdge);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -500,13 +586,13 @@ export class GraphManager extends EventEmitter<{
|
|||||||
|
|
||||||
if (!areSocketsCompatible(fromSocketType, toSocketType)) {
|
if (!areSocketsCompatible(fromSocketType, toSocketType)) {
|
||||||
logger.error(
|
logger.error(
|
||||||
`Socket types do not match: ${fromSocketType} !== ${toSocketType}`,
|
`Socket types do not match: ${fromSocketType} !== ${toSocketType}`
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const edgeToBeReplaced = this.edges.find(
|
const edgeToBeReplaced = this.edges.find(
|
||||||
(e) => e[2].id === to.id && e[3] === toSocket,
|
(e) => e[2].id === to.id && e[3] === toSocket
|
||||||
);
|
);
|
||||||
if (edgeToBeReplaced) {
|
if (edgeToBeReplaced) {
|
||||||
this.removeEdge(edgeToBeReplaced, { applyDeletion: false });
|
this.removeEdge(edgeToBeReplaced, { applyDeletion: false });
|
||||||
@@ -533,7 +619,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
const nextState = this.history.undo();
|
const nextState = this.history.undo();
|
||||||
if (nextState) {
|
if (nextState) {
|
||||||
this._init(nextState);
|
this._init(nextState);
|
||||||
this.emit("save", this.serialize());
|
this.emit('save', this.serialize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -541,7 +627,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
const nextState = this.history.redo();
|
const nextState = this.history.redo();
|
||||||
if (nextState) {
|
if (nextState) {
|
||||||
this._init(nextState);
|
this._init(nextState);
|
||||||
this.emit("save", this.serialize());
|
this.emit('save', this.serialize());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -558,8 +644,8 @@ export class GraphManager extends EventEmitter<{
|
|||||||
if (this.currentUndoGroup) return;
|
if (this.currentUndoGroup) return;
|
||||||
const state = this.serialize();
|
const state = this.serialize();
|
||||||
this.history.save(state);
|
this.history.save(state);
|
||||||
this.emit("save", state);
|
this.emit('save', state);
|
||||||
logger.log("saving graphs", state);
|
logger.log('saving graphs', state);
|
||||||
}
|
}
|
||||||
|
|
||||||
getParentsOfNode(node: NodeInstance) {
|
getParentsOfNode(node: NodeInstance) {
|
||||||
@@ -567,7 +653,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
const stack = node.state?.parents?.slice(0);
|
const stack = node.state?.parents?.slice(0);
|
||||||
while (stack?.length) {
|
while (stack?.length) {
|
||||||
if (parents.length > 1000000) {
|
if (parents.length > 1000000) {
|
||||||
logger.warn("Infinite loop detected");
|
logger.warn('Infinite loop detected');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
const parent = stack.pop();
|
const parent = stack.pop();
|
||||||
@@ -586,26 +672,28 @@ export class GraphManager extends EventEmitter<{
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
const definitions = typeof socket.index === "string"
|
const definitions = typeof socket.index === 'string'
|
||||||
? allDefinitions.filter(s => {
|
? allDefinitions.filter(s => {
|
||||||
return s.outputs?.find(_s => Object
|
return s.outputs?.find(_s =>
|
||||||
.values(nodeType?.inputs || {})
|
Object
|
||||||
.map(s => s.type)
|
.values(nodeType?.inputs || {})
|
||||||
.includes(_s as NodeInput["type"])
|
.map(s => s.type)
|
||||||
)
|
.includes(_s as NodeInput['type'])
|
||||||
|
);
|
||||||
})
|
})
|
||||||
: allDefinitions.filter(s => Object
|
: allDefinitions.filter(s =>
|
||||||
.values(s.inputs ?? {})
|
Object
|
||||||
.find(s => {
|
.values(s.inputs ?? {})
|
||||||
if (s.hidden) return false;
|
.find(s => {
|
||||||
if (nodeType.outputs?.includes(s.type)) {
|
if (s.hidden) return false;
|
||||||
return true
|
if (nodeType.outputs?.includes(s.type)) {
|
||||||
}
|
return true;
|
||||||
return s.accepts?.find(a => nodeType.outputs?.includes(a))
|
}
|
||||||
}))
|
return s.accepts?.find(a => nodeType.outputs?.includes(a));
|
||||||
|
})
|
||||||
return definitions
|
);
|
||||||
|
|
||||||
|
return definitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
getPossibleSockets({ node, index }: Socket): [NodeInstance, string | number][] {
|
getPossibleSockets({ node, index }: Socket): [NodeInstance, string | number][] {
|
||||||
@@ -615,11 +703,11 @@ export class GraphManager extends EventEmitter<{
|
|||||||
const sockets: [NodeInstance, string | number][] = [];
|
const sockets: [NodeInstance, string | number][] = [];
|
||||||
|
|
||||||
// if index is a string, we are an input looking for outputs
|
// if index is a string, we are an input looking for outputs
|
||||||
if (typeof index === "string") {
|
if (typeof index === 'string') {
|
||||||
// filter out self and child nodes
|
// filter out self and child nodes
|
||||||
const children = new Set(this.getChildren(node).map((n) => n.id));
|
const children = new Set(this.getChildren(node).map((n) => n.id));
|
||||||
const nodes = this.getAllNodes().filter(
|
const nodes = this.getAllNodes().filter(
|
||||||
(n) => n.id !== node.id && !children.has(n.id),
|
(n) => n.id !== node.id && !children.has(n.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
const ownType = nodeType?.inputs?.[index].type;
|
const ownType = nodeType?.inputs?.[index].type;
|
||||||
@@ -634,20 +722,20 @@ export class GraphManager extends EventEmitter<{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (typeof index === "number") {
|
} else if (typeof index === 'number') {
|
||||||
// if index is a number, we are an output looking for inputs
|
// if index is a number, we are an output looking for inputs
|
||||||
|
|
||||||
// filter out self and parent nodes
|
// filter out self and parent nodes
|
||||||
const parents = new Set(this.getParentsOfNode(node).map((n) => n.id));
|
const parents = new Set(this.getParentsOfNode(node).map((n) => n.id));
|
||||||
const nodes = this.getAllNodes().filter(
|
const nodes = this.getAllNodes().filter(
|
||||||
(n) => n.id !== node.id && !parents.has(n.id),
|
(n) => n.id !== node.id && !parents.has(n.id)
|
||||||
);
|
);
|
||||||
|
|
||||||
// get edges from this socket
|
// get edges from this socket
|
||||||
const edges = new Map(
|
const edges = new Map(
|
||||||
this.getEdgesFromNode(node)
|
this.getEdgesFromNode(node)
|
||||||
.filter((e) => e[1] === index)
|
.filter((e) => e[1] === index)
|
||||||
.map((e) => [e[2].id, e[3]]),
|
.map((e) => [e[2].id, e[3]])
|
||||||
);
|
);
|
||||||
|
|
||||||
const ownType = nodeType.outputs?.[index];
|
const ownType = nodeType.outputs?.[index];
|
||||||
@@ -660,8 +748,8 @@ export class GraphManager extends EventEmitter<{
|
|||||||
otherType.push(...(inputs[key].accepts || []));
|
otherType.push(...(inputs[key].accepts || []));
|
||||||
|
|
||||||
if (
|
if (
|
||||||
areSocketsCompatible(ownType, otherType) &&
|
areSocketsCompatible(ownType, otherType)
|
||||||
edges.get(node.id) !== key
|
&& edges.get(node.id) !== key
|
||||||
) {
|
) {
|
||||||
sockets.push([node, key]);
|
sockets.push([node, key]);
|
||||||
}
|
}
|
||||||
@@ -674,7 +762,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
|
|
||||||
removeEdge(
|
removeEdge(
|
||||||
edge: Edge,
|
edge: Edge,
|
||||||
{ applyDeletion = true }: { applyDeletion?: boolean } = {},
|
{ applyDeletion = true }: { applyDeletion?: boolean } = {}
|
||||||
) {
|
) {
|
||||||
const id0 = edge[0].id;
|
const id0 = edge[0].id;
|
||||||
const sid0 = edge[1];
|
const sid0 = edge[1];
|
||||||
@@ -682,21 +770,20 @@ export class GraphManager extends EventEmitter<{
|
|||||||
const sid2 = edge[3];
|
const sid2 = edge[3];
|
||||||
|
|
||||||
const _edge = this.edges.find(
|
const _edge = this.edges.find(
|
||||||
(e) =>
|
(e) => e[0].id === id0 && e[1] === sid0 && e[2].id === id2 && e[3] === sid2
|
||||||
e[0].id === id0 && e[1] === sid0 && e[2].id === id2 && e[3] === sid2,
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!_edge) return;
|
if (!_edge) return;
|
||||||
|
|
||||||
if (edge[0].state.children) {
|
if (edge[0].state.children) {
|
||||||
edge[0].state.children = edge[0].state.children.filter(
|
edge[0].state.children = edge[0].state.children.filter(
|
||||||
(n: NodeInstance) => n.id !== id2,
|
(n: NodeInstance) => n.id !== id2
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (edge[2].state.parents) {
|
if (edge[2].state.parents) {
|
||||||
edge[2].state.parents = edge[2].state.parents.filter(
|
edge[2].state.parents = edge[2].state.parents.filter(
|
||||||
(n: NodeInstance) => n.id !== id0,
|
(n: NodeInstance) => n.id !== id0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -705,7 +792,6 @@ export class GraphManager extends EventEmitter<{
|
|||||||
this.execute();
|
this.execute();
|
||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getEdgesToNode(node: NodeInstance) {
|
getEdgesToNode(node: NodeInstance) {
|
||||||
|
|||||||
@@ -1,36 +1,43 @@
|
|||||||
import type { NodeInstance, Socket } from "@nodarium/types";
|
import type { NodeInstance, Socket } from '@nodarium/types';
|
||||||
import { getContext, setContext } from "svelte";
|
import { getContext, setContext } from 'svelte';
|
||||||
import { SvelteSet } from "svelte/reactivity";
|
import { SvelteSet } from 'svelte/reactivity';
|
||||||
import type { GraphManager } from "./graph-manager.svelte";
|
import type { OrthographicCamera, Vector3 } from 'three';
|
||||||
import type { OrthographicCamera } from "three";
|
import type { GraphManager } from './graph-manager.svelte';
|
||||||
|
|
||||||
|
const graphStateKey = Symbol('graph-state');
|
||||||
const graphStateKey = Symbol("graph-state");
|
|
||||||
export function getGraphState() {
|
export function getGraphState() {
|
||||||
return getContext<GraphState>(graphStateKey);
|
return getContext<GraphState>(graphStateKey);
|
||||||
}
|
}
|
||||||
export function setGraphState(graphState: GraphState) {
|
export function setGraphState(graphState: GraphState) {
|
||||||
return setContext(graphStateKey, graphState)
|
return setContext(graphStateKey, graphState);
|
||||||
}
|
}
|
||||||
|
|
||||||
const graphManagerKey = Symbol("graph-manager");
|
const graphManagerKey = Symbol('graph-manager');
|
||||||
export function getGraphManager() {
|
export function getGraphManager() {
|
||||||
return getContext<GraphManager>(graphManagerKey)
|
return getContext<GraphManager>(graphManagerKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function setGraphManager(manager: GraphManager) {
|
export function setGraphManager(manager: GraphManager) {
|
||||||
return setContext(graphManagerKey, manager);
|
return setContext(graphManagerKey, manager);
|
||||||
}
|
}
|
||||||
|
|
||||||
export class GraphState {
|
type EdgeData = {
|
||||||
|
x1: number;
|
||||||
|
y1: number;
|
||||||
|
points: Vector3[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export class GraphState {
|
||||||
constructor(private graph: GraphManager) {
|
constructor(private graph: GraphManager) {
|
||||||
$effect.root(() => {
|
$effect.root(() => {
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
localStorage.setItem("cameraPosition", `[${this.cameraPosition[0]},${this.cameraPosition[1]},${this.cameraPosition[2]}]`)
|
localStorage.setItem(
|
||||||
})
|
'cameraPosition',
|
||||||
})
|
`[${this.cameraPosition[0]},${this.cameraPosition[1]},${this.cameraPosition[2]}]`
|
||||||
const storedPosition = localStorage.getItem("cameraPosition")
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const storedPosition = localStorage.getItem('cameraPosition');
|
||||||
if (storedPosition) {
|
if (storedPosition) {
|
||||||
try {
|
try {
|
||||||
const d = JSON.parse(storedPosition);
|
const d = JSON.parse(storedPosition);
|
||||||
@@ -38,7 +45,7 @@ export class GraphState {
|
|||||||
this.cameraPosition[1] = d[1];
|
this.cameraPosition[1] = d[1];
|
||||||
this.cameraPosition[2] = d[2];
|
this.cameraPosition[2] = d[2];
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log("Failed to parsed stored camera position", e);
|
console.log('Failed to parsed stored camera position', e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -46,13 +53,16 @@ export class GraphState {
|
|||||||
width = $state(100);
|
width = $state(100);
|
||||||
height = $state(100);
|
height = $state(100);
|
||||||
|
|
||||||
|
hoveredEdgeId = $state<string | null>(null);
|
||||||
|
edges = new Map<string, EdgeData>();
|
||||||
|
|
||||||
wrapper = $state<HTMLDivElement>(null!);
|
wrapper = $state<HTMLDivElement>(null!);
|
||||||
rect: DOMRect = $derived(
|
rect: DOMRect = $derived(
|
||||||
(this.wrapper && this.width && this.height) ? this.wrapper.getBoundingClientRect() : new DOMRect(0, 0, 0, 0),
|
(this.wrapper && this.width && this.height) ? this.wrapper.getBoundingClientRect() : new DOMRect(0, 0, 0, 0)
|
||||||
);
|
);
|
||||||
|
|
||||||
camera = $state<OrthographicCamera>(null!);
|
camera = $state<OrthographicCamera>(null!);
|
||||||
cameraPosition: [number, number, number] = $state([0, 0, 4]);
|
cameraPosition: [number, number, number] = $state([0, 0, 100]);
|
||||||
|
|
||||||
clipboard: null | {
|
clipboard: null | {
|
||||||
nodes: NodeInstance[];
|
nodes: NodeInstance[];
|
||||||
@@ -63,7 +73,7 @@ export class GraphState {
|
|||||||
this.cameraPosition[0] - this.width / this.cameraPosition[2] / 2,
|
this.cameraPosition[0] - this.width / this.cameraPosition[2] / 2,
|
||||||
this.cameraPosition[0] + this.width / this.cameraPosition[2] / 2,
|
this.cameraPosition[0] + this.width / this.cameraPosition[2] / 2,
|
||||||
this.cameraPosition[1] - this.height / this.cameraPosition[2] / 2,
|
this.cameraPosition[1] - this.height / this.cameraPosition[2] / 2,
|
||||||
this.cameraPosition[1] + this.height / this.cameraPosition[2] / 2,
|
this.cameraPosition[1] + this.height / this.cameraPosition[2] / 2
|
||||||
]);
|
]);
|
||||||
|
|
||||||
boxSelection = $state(false);
|
boxSelection = $state(false);
|
||||||
@@ -71,8 +81,8 @@ export class GraphState {
|
|||||||
addMenuPosition = $state<[number, number] | null>(null);
|
addMenuPosition = $state<[number, number] | null>(null);
|
||||||
|
|
||||||
snapToGrid = $state(false);
|
snapToGrid = $state(false);
|
||||||
showGrid = $state(true)
|
showGrid = $state(true);
|
||||||
showHelp = $state(false)
|
showHelp = $state(false);
|
||||||
|
|
||||||
cameraDown = [0, 0];
|
cameraDown = [0, 0];
|
||||||
mouseDownNodeId = -1;
|
mouseDownNodeId = -1;
|
||||||
@@ -88,33 +98,49 @@ export class GraphState {
|
|||||||
hoveredSocket = $state<Socket | null>(null);
|
hoveredSocket = $state<Socket | null>(null);
|
||||||
possibleSockets = $state<Socket[]>([]);
|
possibleSockets = $state<Socket[]>([]);
|
||||||
possibleSocketIds = $derived(
|
possibleSocketIds = $derived(
|
||||||
new Set(this.possibleSockets.map((s) => `${s.node.id}-${s.index}`)),
|
new Set(this.possibleSockets.map((s) => `${s.node.id}-${s.index}`))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
getEdges() {
|
||||||
|
return $state.snapshot(this.edges);
|
||||||
|
}
|
||||||
|
|
||||||
clearSelection() {
|
clearSelection() {
|
||||||
this.selectedNodes.clear();
|
this.selectedNodes.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
isBodyFocused = () => document?.activeElement?.nodeName !== "INPUT";
|
isBodyFocused = () => document?.activeElement?.nodeName !== 'INPUT';
|
||||||
|
|
||||||
|
setEdgeGeometry(edgeId: string, x1: number, y1: number, points: Vector3[]) {
|
||||||
|
this.edges.set(edgeId, { x1, y1, points });
|
||||||
|
}
|
||||||
|
|
||||||
|
removeEdgeGeometry(edgeId: string) {
|
||||||
|
this.edges.delete(edgeId);
|
||||||
|
}
|
||||||
|
|
||||||
|
getEdgeData() {
|
||||||
|
return this.edges;
|
||||||
|
}
|
||||||
|
|
||||||
updateNodePosition(node: NodeInstance) {
|
updateNodePosition(node: NodeInstance) {
|
||||||
if (
|
if (
|
||||||
node.state.x === node.position[0] &&
|
node.state.x === node.position[0]
|
||||||
node.state.y === node.position[1]
|
&& node.state.y === node.position[1]
|
||||||
) {
|
) {
|
||||||
delete node.state.x;
|
delete node.state.x;
|
||||||
delete node.state.y;
|
delete node.state.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.state["x"] !== undefined && node.state["y"] !== undefined) {
|
if (node.state['x'] !== undefined && node.state['y'] !== undefined) {
|
||||||
if (node.state.ref) {
|
if (node.state.ref) {
|
||||||
node.state.ref.style.setProperty("--nx", `${node.state.x * 10}px`);
|
node.state.ref.style.setProperty('--nx', `${node.state.x * 10}px`);
|
||||||
node.state.ref.style.setProperty("--ny", `${node.state.y * 10}px`);
|
node.state.ref.style.setProperty('--ny', `${node.state.y * 10}px`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (node.state.ref) {
|
if (node.state.ref) {
|
||||||
node.state.ref.style.setProperty("--nx", `${node.position[0] * 10}px`);
|
node.state.ref.style.setProperty('--nx', `${node.position[0] * 10}px`);
|
||||||
node.state.ref.style.setProperty("--ny", `${node.position[1] * 10}px`);
|
node.state.ref.style.setProperty('--ny', `${node.position[1] * 10}px`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,18 +160,18 @@ export class GraphState {
|
|||||||
|
|
||||||
getSocketPosition(
|
getSocketPosition(
|
||||||
node: NodeInstance,
|
node: NodeInstance,
|
||||||
index: string | number,
|
index: string | number
|
||||||
): [number, number] {
|
): [number, number] {
|
||||||
if (typeof index === "number") {
|
if (typeof index === 'number') {
|
||||||
return [
|
return [
|
||||||
(node?.state?.x ?? node.position[0]) + 20,
|
(node?.state?.x ?? node.position[0]) + 20,
|
||||||
(node?.state?.y ?? node.position[1]) + 2.5 + 10 * index,
|
(node?.state?.y ?? node.position[1]) + 2.5 + 10 * index
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
const _index = Object.keys(node.state?.type?.inputs || {}).indexOf(index);
|
const _index = Object.keys(node.state?.type?.inputs || {}).indexOf(index);
|
||||||
return [
|
return [
|
||||||
node?.state?.x ?? node.position[0],
|
node?.state?.x ?? node.position[0],
|
||||||
(node?.state?.y ?? node.position[1]) + 10 + 10 * _index,
|
(node?.state?.y ?? node.position[1]) + 10 + 10 * _index
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -159,26 +185,26 @@ export class GraphState {
|
|||||||
if (!node?.inputs) {
|
if (!node?.inputs) {
|
||||||
return 5;
|
return 5;
|
||||||
}
|
}
|
||||||
const height =
|
const height = 5
|
||||||
5 +
|
+ 10
|
||||||
10 *
|
* Object.keys(node.inputs).filter(
|
||||||
Object.keys(node.inputs).filter(
|
|
||||||
(p) =>
|
(p) =>
|
||||||
p !== "seed" &&
|
p !== 'seed'
|
||||||
node?.inputs &&
|
&& node?.inputs
|
||||||
!("setting" in node?.inputs?.[p]) &&
|
&& !('setting' in node?.inputs?.[p])
|
||||||
node.inputs[p].hidden !== true,
|
&& node.inputs[p].hidden !== true
|
||||||
).length;
|
).length;
|
||||||
this.nodeHeightCache[nodeTypeId] = height;
|
this.nodeHeightCache[nodeTypeId] = height;
|
||||||
return height;
|
return height;
|
||||||
}
|
}
|
||||||
|
|
||||||
copyNodes() {
|
copyNodes() {
|
||||||
if (this.activeNodeId === -1 && !this.selectedNodes?.size)
|
if (this.activeNodeId === -1 && !this.selectedNodes?.size) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
let nodes = [
|
let nodes = [
|
||||||
this.activeNodeId,
|
this.activeNodeId,
|
||||||
...(this.selectedNodes?.values() || []),
|
...(this.selectedNodes?.values() || [])
|
||||||
]
|
]
|
||||||
.map((id) => this.graph.getNode(id))
|
.map((id) => this.graph.getNode(id))
|
||||||
.filter(b => !!b);
|
.filter(b => !!b);
|
||||||
@@ -188,14 +214,14 @@ export class GraphState {
|
|||||||
...node,
|
...node,
|
||||||
position: [
|
position: [
|
||||||
this.mousePosition[0] - node.position[0],
|
this.mousePosition[0] - node.position[0],
|
||||||
this.mousePosition[1] - node.position[1],
|
this.mousePosition[1] - node.position[1]
|
||||||
],
|
],
|
||||||
tmp: undefined,
|
tmp: undefined
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.clipboard = {
|
this.clipboard = {
|
||||||
nodes: nodes,
|
nodes: nodes,
|
||||||
edges: edges,
|
edges: edges
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,14 +243,13 @@ export class GraphState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
setDownSocket(socket: Socket) {
|
setDownSocket(socket: Socket) {
|
||||||
this.activeSocket = socket;
|
this.activeSocket = socket;
|
||||||
|
|
||||||
let { node, index, position } = socket;
|
let { node, index, position } = socket;
|
||||||
|
|
||||||
// remove existing edge
|
// remove existing edge
|
||||||
if (typeof index === "string") {
|
if (typeof index === 'string') {
|
||||||
const edges = this.graph.getEdgesToNode(node);
|
const edges = this.graph.getEdgesToNode(node);
|
||||||
for (const edge of edges) {
|
for (const edge of edges) {
|
||||||
if (edge[3] === index) {
|
if (edge[3] === index) {
|
||||||
@@ -241,7 +266,7 @@ export class GraphState {
|
|||||||
this.activeSocket = {
|
this.activeSocket = {
|
||||||
node,
|
node,
|
||||||
index,
|
index,
|
||||||
position,
|
position
|
||||||
};
|
};
|
||||||
|
|
||||||
this.possibleSockets = this.graph
|
this.possibleSockets = this.graph
|
||||||
@@ -250,18 +275,17 @@ export class GraphState {
|
|||||||
return {
|
return {
|
||||||
node,
|
node,
|
||||||
index,
|
index,
|
||||||
position: this.getSocketPosition(node, index),
|
position: this.getSocketPosition(node, index)
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
projectScreenToWorld(x: number, y: number): [number, number] {
|
projectScreenToWorld(x: number, y: number): [number, number] {
|
||||||
return [
|
return [
|
||||||
this.cameraPosition[0] +
|
this.cameraPosition[0]
|
||||||
(x - this.width / 2) / this.cameraPosition[2],
|
+ (x - this.width / 2) / this.cameraPosition[2],
|
||||||
this.cameraPosition[1] +
|
this.cameraPosition[1]
|
||||||
(y - this.height / 2) / this.cameraPosition[2],
|
+ (y - this.height / 2) / this.cameraPosition[2]
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -274,8 +298,8 @@ export class GraphState {
|
|||||||
if (event.button === 0) {
|
if (event.button === 0) {
|
||||||
// check if the clicked element is a node
|
// check if the clicked element is a node
|
||||||
if (event.target instanceof HTMLElement) {
|
if (event.target instanceof HTMLElement) {
|
||||||
const nodeElement = event.target.closest(".node");
|
const nodeElement = event.target.closest('.node');
|
||||||
const nodeId = nodeElement?.getAttribute?.("data-node-id");
|
const nodeId = nodeElement?.getAttribute?.('data-node-id');
|
||||||
if (nodeId) {
|
if (nodeId) {
|
||||||
clickedNodeId = parseInt(nodeId, 10);
|
clickedNodeId = parseInt(nodeId, 10);
|
||||||
}
|
}
|
||||||
@@ -303,10 +327,10 @@ export class GraphState {
|
|||||||
const height = this.getNodeHeight(node.type);
|
const height = this.getNodeHeight(node.type);
|
||||||
const width = 20;
|
const width = 20;
|
||||||
return (
|
return (
|
||||||
node.position[0] > this.cameraBounds[0] - width &&
|
node.position[0] > this.cameraBounds[0] - width
|
||||||
node.position[0] < this.cameraBounds[1] &&
|
&& node.position[0] < this.cameraBounds[1]
|
||||||
node.position[1] > this.cameraBounds[2] - height &&
|
&& node.position[1] > this.cameraBounds[2] - height
|
||||||
node.position[1] < this.cameraBounds[3]
|
&& node.position[1] < this.cameraBounds[3]
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,8 +11,10 @@
|
|||||||
import HelpView from "../components/HelpView.svelte";
|
import HelpView from "../components/HelpView.svelte";
|
||||||
import { getGraphManager, getGraphState } from "../graph-state.svelte";
|
import { getGraphManager, getGraphState } from "../graph-state.svelte";
|
||||||
import { HTML } from "@threlte/extras";
|
import { HTML } from "@threlte/extras";
|
||||||
import { FileDropEventManager, MouseEventManager } from "./events";
|
|
||||||
import { maxZoom, minZoom } from "./constants";
|
import { maxZoom, minZoom } from "./constants";
|
||||||
|
import Debug from "../debug/Debug.svelte";
|
||||||
|
import { FileDropEventManager } from "./drop.events";
|
||||||
|
import { MouseEventManager } from "./mouse.events";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
keymap,
|
keymap,
|
||||||
@@ -103,6 +105,7 @@
|
|||||||
onwheel={(ev) => mouseEvents.handleMouseScroll(ev)}
|
onwheel={(ev) => mouseEvents.handleMouseScroll(ev)}
|
||||||
bind:this={graphState.wrapper}
|
bind:this={graphState.wrapper}
|
||||||
class="graph-wrapper"
|
class="graph-wrapper"
|
||||||
|
style="height: 100vh;"
|
||||||
class:is-panning={graphState.isPanning}
|
class:is-panning={graphState.isPanning}
|
||||||
class:is-hovering={graphState.hoveredNodeId !== -1}
|
class:is-hovering={graphState.hoveredNodeId !== -1}
|
||||||
aria-label="Graph"
|
aria-label="Graph"
|
||||||
@@ -174,9 +177,18 @@
|
|||||||
|
|
||||||
{#each graph.edges as edge}
|
{#each graph.edges as edge}
|
||||||
{@const [x1, y1, x2, y2] = getEdgePosition(edge)}
|
{@const [x1, y1, x2, y2] = getEdgePosition(edge)}
|
||||||
<EdgeEl z={graphState.cameraPosition[2]} {x1} {y1} {x2} {y2} />
|
<EdgeEl
|
||||||
|
id={graph.getEdgeId(edge)}
|
||||||
|
z={graphState.cameraPosition[2]}
|
||||||
|
{x1}
|
||||||
|
{y1}
|
||||||
|
{x2}
|
||||||
|
{y2}
|
||||||
|
/>
|
||||||
{/each}
|
{/each}
|
||||||
|
|
||||||
|
<Debug />
|
||||||
|
|
||||||
<HTML transform={false}>
|
<HTML transform={false}>
|
||||||
<div
|
<div
|
||||||
role="tree"
|
role="tree"
|
||||||
|
|||||||
107
app/src/lib/graph-interface/graph/drop.events.ts
Normal file
107
app/src/lib/graph-interface/graph/drop.events.ts
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
import { GraphSchema, type NodeId } from '@nodarium/types';
|
||||||
|
import type { GraphManager } from '../graph-manager.svelte';
|
||||||
|
import type { GraphState } from '../graph-state.svelte';
|
||||||
|
|
||||||
|
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()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
110
app/src/lib/graph-interface/graph/edge.events.ts
Normal file
110
app/src/lib/graph-interface/graph/edge.events.ts
Normal file
@@ -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<string, Box>();
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,127 +1,23 @@
|
|||||||
import { GraphSchema, type NodeId, type NodeInstance } from "@nodarium/types";
|
import { animate, lerp } from '$lib/helpers';
|
||||||
import type { GraphManager } from "../graph-manager.svelte";
|
import { type NodeInstance } from '@nodarium/types';
|
||||||
import type { GraphState } from "../graph-state.svelte";
|
import type { GraphManager } from '../graph-manager.svelte';
|
||||||
import { animate, lerp } from "$lib/helpers";
|
import type { GraphState } from '../graph-state.svelte';
|
||||||
import { snapToGrid as snapPointToGrid } from "../helpers";
|
import { snapToGrid as snapPointToGrid } from '../helpers';
|
||||||
import { maxZoom, minZoom, zoomSpeed } from "./constants";
|
import { maxZoom, minZoom, zoomSpeed } from './constants';
|
||||||
|
import { EdgeInteractionManager } from './edge.events';
|
||||||
|
|
||||||
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(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
export class MouseEventManager {
|
export class MouseEventManager {
|
||||||
|
edgeInteractionManager: EdgeInteractionManager;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private graph: GraphManager,
|
private graph: GraphManager,
|
||||||
private state: GraphState
|
private state: GraphState
|
||||||
) { }
|
) {
|
||||||
|
this.edgeInteractionManager = new EdgeInteractionManager(graph, state);
|
||||||
|
}
|
||||||
|
|
||||||
handleMouseUp(event: MouseEvent) {
|
handleMouseUp(event: MouseEvent) {
|
||||||
|
this.edgeInteractionManager.handleMouseUp();
|
||||||
this.state.isPanning = false;
|
this.state.isPanning = false;
|
||||||
if (!this.state.mouseDown) return;
|
if (!this.state.mouseDown) return;
|
||||||
|
|
||||||
@@ -145,25 +41,23 @@ export class MouseEventManager {
|
|||||||
const snapLevel = this.state.getSnapLevel();
|
const snapLevel = this.state.getSnapLevel();
|
||||||
activeNode.position[0] = snapPointToGrid(
|
activeNode.position[0] = snapPointToGrid(
|
||||||
activeNode?.state?.x ?? activeNode.position[0],
|
activeNode?.state?.x ?? activeNode.position[0],
|
||||||
5 / snapLevel,
|
5 / snapLevel
|
||||||
);
|
);
|
||||||
activeNode.position[1] = snapPointToGrid(
|
activeNode.position[1] = snapPointToGrid(
|
||||||
activeNode?.state?.y ?? activeNode.position[1],
|
activeNode?.state?.y ?? activeNode.position[1],
|
||||||
5 / snapLevel,
|
5 / snapLevel
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
activeNode.position[0] = activeNode?.state?.x ?? activeNode.position[0];
|
activeNode.position[0] = activeNode?.state?.x ?? activeNode.position[0];
|
||||||
activeNode.position[1] = activeNode?.state?.y ?? activeNode.position[1];
|
activeNode.position[1] = activeNode?.state?.y ?? activeNode.position[1];
|
||||||
}
|
}
|
||||||
const nodes = [
|
const nodes = [
|
||||||
...[...(this.state.selectedNodes?.values() || [])].map((id) =>
|
...[...(this.state.selectedNodes?.values() || [])].map((id) => this.graph.getNode(id))
|
||||||
this.graph.getNode(id),
|
|
||||||
),
|
|
||||||
] as NodeInstance[];
|
] as NodeInstance[];
|
||||||
|
|
||||||
const vec = [
|
const vec = [
|
||||||
activeNode.position[0] - (activeNode?.state.x || 0),
|
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) {
|
for (const node of nodes) {
|
||||||
@@ -179,9 +73,9 @@ export class MouseEventManager {
|
|||||||
animate(500, (a: number) => {
|
animate(500, (a: number) => {
|
||||||
for (const node of nodes) {
|
for (const node of nodes) {
|
||||||
if (
|
if (
|
||||||
node?.state &&
|
node?.state
|
||||||
node.state["x"] !== undefined &&
|
&& node.state['x'] !== undefined
|
||||||
node.state["y"] !== undefined
|
&& node.state['y'] !== undefined
|
||||||
) {
|
) {
|
||||||
node.state.x = lerp(node.state.x, node.position[0], a);
|
node.state.x = lerp(node.state.x, node.position[0], a);
|
||||||
node.state.y = lerp(node.state.y, node.position[1], a);
|
node.state.y = lerp(node.state.y, node.position[1], a);
|
||||||
@@ -195,24 +89,24 @@ export class MouseEventManager {
|
|||||||
this.graph.save();
|
this.graph.save();
|
||||||
} else if (this.state.hoveredSocket && this.state.activeSocket) {
|
} else if (this.state.hoveredSocket && this.state.activeSocket) {
|
||||||
if (
|
if (
|
||||||
typeof this.state.hoveredSocket.index === "number" &&
|
typeof this.state.hoveredSocket.index === 'number'
|
||||||
typeof this.state.activeSocket.index === "string"
|
&& typeof this.state.activeSocket.index === 'string'
|
||||||
) {
|
) {
|
||||||
this.graph.createEdge(
|
this.graph.createEdge(
|
||||||
this.state.hoveredSocket.node,
|
this.state.hoveredSocket.node,
|
||||||
this.state.hoveredSocket.index || 0,
|
this.state.hoveredSocket.index || 0,
|
||||||
this.state.activeSocket.node,
|
this.state.activeSocket.node,
|
||||||
this.state.activeSocket.index,
|
this.state.activeSocket.index
|
||||||
);
|
);
|
||||||
} else if (
|
} else if (
|
||||||
typeof this.state.activeSocket.index == "number" &&
|
typeof this.state.activeSocket.index == 'number'
|
||||||
typeof this.state.hoveredSocket.index === "string"
|
&& typeof this.state.hoveredSocket.index === 'string'
|
||||||
) {
|
) {
|
||||||
this.graph.createEdge(
|
this.graph.createEdge(
|
||||||
this.state.activeSocket.node,
|
this.state.activeSocket.node,
|
||||||
this.state.activeSocket.index || 0,
|
this.state.activeSocket.index || 0,
|
||||||
this.state.hoveredSocket.node,
|
this.state.hoveredSocket.node,
|
||||||
this.state.hoveredSocket.index,
|
this.state.hoveredSocket.index
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
this.graph.save();
|
this.graph.save();
|
||||||
@@ -220,18 +114,18 @@ export class MouseEventManager {
|
|||||||
// Handle automatic adding of nodes on ctrl+mouseUp
|
// Handle automatic adding of nodes on ctrl+mouseUp
|
||||||
this.state.edgeEndPosition = [
|
this.state.edgeEndPosition = [
|
||||||
this.state.mousePosition[0],
|
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.addMenuPosition = [
|
||||||
this.state.mousePosition[0],
|
this.state.mousePosition[0],
|
||||||
this.state.mousePosition[1] - 25 / this.state.cameraPosition[2],
|
this.state.mousePosition[1] - 25 / this.state.cameraPosition[2]
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
this.state.addMenuPosition = [
|
this.state.addMenuPosition = [
|
||||||
this.state.mousePosition[0] - 155 / this.state.cameraPosition[2],
|
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;
|
return;
|
||||||
@@ -239,11 +133,11 @@ export class MouseEventManager {
|
|||||||
|
|
||||||
// check if camera moved
|
// check if camera moved
|
||||||
if (
|
if (
|
||||||
clickedNodeId === -1 &&
|
clickedNodeId === -1
|
||||||
!this.state.boxSelection &&
|
&& !this.state.boxSelection
|
||||||
this.state.cameraDown[0] === this.state.cameraPosition[0] &&
|
&& this.state.cameraDown[0] === this.state.cameraPosition[0]
|
||||||
this.state.cameraDown[1] === this.state.cameraPosition[1] &&
|
&& this.state.cameraDown[1] === this.state.cameraPosition[1]
|
||||||
this.state.isBodyFocused()
|
&& this.state.isBodyFocused()
|
||||||
) {
|
) {
|
||||||
this.state.activeNodeId = -1;
|
this.state.activeNodeId = -1;
|
||||||
this.state.clearSelection();
|
this.state.clearSelection();
|
||||||
@@ -257,16 +151,15 @@ export class MouseEventManager {
|
|||||||
this.state.addMenuPosition = null;
|
this.state.addMenuPosition = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
handleMouseDown(event: MouseEvent) {
|
handleMouseDown(event: MouseEvent) {
|
||||||
if (this.state.mouseDown) return;
|
if (this.state.mouseDown) return;
|
||||||
this.state.edgeEndPosition = null;
|
this.state.edgeEndPosition = null;
|
||||||
|
|
||||||
if (event.target instanceof HTMLElement) {
|
if (event.target instanceof HTMLElement) {
|
||||||
if (
|
if (
|
||||||
event.target.nodeName !== "CANVAS" &&
|
event.target.nodeName !== 'CANVAS'
|
||||||
!event.target.classList.contains("node") &&
|
&& !event.target.classList.contains('node')
|
||||||
!event.target.classList.contains("content")
|
&& !event.target.classList.contains('content')
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -288,7 +181,7 @@ export class MouseEventManager {
|
|||||||
this.state.activeNodeId = clickedNodeId;
|
this.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 (this.state.activeNodeId === clickedNodeId) {
|
} else if (this.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) {
|
||||||
this.state.selectedNodes.add(this.state.activeNodeId);
|
this.state.selectedNodes.add(this.state.activeNodeId);
|
||||||
@@ -312,6 +205,7 @@ export class MouseEventManager {
|
|||||||
this.state.activeNodeId = clickedNodeId;
|
this.state.activeNodeId = clickedNodeId;
|
||||||
this.state.clearSelection();
|
this.state.clearSelection();
|
||||||
}
|
}
|
||||||
|
this.edgeInteractionManager.handleMouseDown();
|
||||||
} else if (event.ctrlKey) {
|
} else if (event.ctrlKey) {
|
||||||
this.state.boxSelection = true;
|
this.state.boxSelection = true;
|
||||||
}
|
}
|
||||||
@@ -335,7 +229,6 @@ export class MouseEventManager {
|
|||||||
this.state.edgeEndPosition = null;
|
this.state.edgeEndPosition = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
handleMouseMove(event: MouseEvent) {
|
handleMouseMove(event: MouseEvent) {
|
||||||
let mx = event.clientX - this.state.rect.x;
|
let mx = event.clientX - this.state.rect.x;
|
||||||
let my = event.clientY - this.state.rect.y;
|
let my = event.clientY - this.state.rect.y;
|
||||||
@@ -351,8 +244,8 @@ export class MouseEventManager {
|
|||||||
let _socket;
|
let _socket;
|
||||||
for (const socket of this.state.possibleSockets) {
|
for (const socket of this.state.possibleSockets) {
|
||||||
const dist = Math.sqrt(
|
const dist = Math.sqrt(
|
||||||
(socket.position[0] - this.state.mousePosition[0]) ** 2 +
|
(socket.position[0] - this.state.mousePosition[0]) ** 2
|
||||||
(socket.position[1] - this.state.mousePosition[1]) ** 2,
|
+ (socket.position[1] - this.state.mousePosition[1]) ** 2
|
||||||
);
|
);
|
||||||
if (dist < smallestDist) {
|
if (dist < smallestDist) {
|
||||||
smallestDist = dist;
|
smallestDist = dist;
|
||||||
@@ -375,7 +268,7 @@ export class MouseEventManager {
|
|||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
const mouseD = this.state.projectScreenToWorld(
|
const mouseD = this.state.projectScreenToWorld(
|
||||||
this.state.mouseDown[0],
|
this.state.mouseDown[0],
|
||||||
this.state.mouseDown[1],
|
this.state.mouseDown[1]
|
||||||
);
|
);
|
||||||
const x1 = Math.min(mouseD[0], this.state.mousePosition[0]);
|
const x1 = Math.min(mouseD[0], this.state.mousePosition[0]);
|
||||||
const x2 = Math.max(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
|
// here we are handling dragging of nodes
|
||||||
if (this.state.activeNodeId !== -1 && this.state.mouseDownNodeId !== -1) {
|
if (this.state.activeNodeId !== -1 && this.state.mouseDownNodeId !== -1) {
|
||||||
|
this.edgeInteractionManager.handleMouseMove();
|
||||||
const node = this.graph.getNode(this.state.activeNodeId);
|
const node = this.graph.getNode(this.state.activeNodeId);
|
||||||
if (!node || event.buttons !== 1) return;
|
if (!node || event.buttons !== 1) return;
|
||||||
|
|
||||||
@@ -405,10 +299,8 @@ export class MouseEventManager {
|
|||||||
const oldX = node.state.downX || 0;
|
const oldX = node.state.downX || 0;
|
||||||
const oldY = node.state.downY || 0;
|
const oldY = node.state.downY || 0;
|
||||||
|
|
||||||
let newX =
|
let newX = oldX + (mx - this.state.mouseDown[0]) / this.state.cameraPosition[2];
|
||||||
oldX + (mx - this.state.mouseDown[0]) / this.state.cameraPosition[2];
|
let newY = oldY + (my - this.state.mouseDown[1]) / this.state.cameraPosition[2];
|
||||||
let newY =
|
|
||||||
oldY + (my - this.state.mouseDown[1]) / this.state.cameraPosition[2];
|
|
||||||
|
|
||||||
if (event.ctrlKey) {
|
if (event.ctrlKey) {
|
||||||
const snapLevel = this.state.getSnapLevel();
|
const snapLevel = this.state.getSnapLevel();
|
||||||
@@ -448,23 +340,19 @@ export class MouseEventManager {
|
|||||||
|
|
||||||
// here we are handling panning of camera
|
// here we are handling panning of camera
|
||||||
this.state.isPanning = true;
|
this.state.isPanning = true;
|
||||||
let newX =
|
let newX = this.state.cameraDown[0]
|
||||||
this.state.cameraDown[0] -
|
- (mx - this.state.mouseDown[0]) / this.state.cameraPosition[2];
|
||||||
(mx - this.state.mouseDown[0]) / this.state.cameraPosition[2];
|
let newY = this.state.cameraDown[1]
|
||||||
let newY =
|
- (my - this.state.mouseDown[1]) / this.state.cameraPosition[2];
|
||||||
this.state.cameraDown[1] -
|
|
||||||
(my - this.state.mouseDown[1]) / this.state.cameraPosition[2];
|
|
||||||
|
|
||||||
this.state.cameraPosition[0] = newX;
|
this.state.cameraPosition[0] = newX;
|
||||||
this.state.cameraPosition[1] = newY;
|
this.state.cameraPosition[1] = newY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
handleMouseScroll(event: WheelEvent) {
|
handleMouseScroll(event: WheelEvent) {
|
||||||
const bodyIsFocused =
|
const bodyIsFocused = document.activeElement === document.body
|
||||||
document.activeElement === document.body ||
|
|| document.activeElement === this.state.wrapper
|
||||||
document.activeElement === this.state.wrapper ||
|
|| document?.activeElement?.id === 'graph';
|
||||||
document?.activeElement?.id === "graph";
|
|
||||||
if (!bodyIsFocused) return;
|
if (!bodyIsFocused) return;
|
||||||
|
|
||||||
// Define zoom speed and clamp it between -1 and 1
|
// Define zoom speed and clamp it between -1 and 1
|
||||||
@@ -479,21 +367,19 @@ export class MouseEventManager {
|
|||||||
maxZoom,
|
maxZoom,
|
||||||
isNegative
|
isNegative
|
||||||
? this.state.cameraPosition[2] / delta
|
? 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
|
// Calculate the ratio of the new zoom to the original zoom
|
||||||
const zoomRatio = newZoom / this.state.cameraPosition[2];
|
const zoomRatio = newZoom / this.state.cameraPosition[2];
|
||||||
|
|
||||||
// Update camera position and zoom level
|
// Update camera position and zoom level
|
||||||
this.state.cameraPosition[0] = this.state.mousePosition[0] -
|
this.state.cameraPosition[0] = this.state.mousePosition[0]
|
||||||
(this.state.mousePosition[0] - this.state.cameraPosition[0]) /
|
- (this.state.mousePosition[0] - this.state.cameraPosition[0])
|
||||||
zoomRatio;
|
/ zoomRatio;
|
||||||
this.state.cameraPosition[1] = this.state.mousePosition[1] -
|
this.state.cameraPosition[1] = this.state.mousePosition[1]
|
||||||
(this.state.mousePosition[1] - this.state.cameraPosition[1]) /
|
- (this.state.mousePosition[1] - this.state.cameraPosition[1])
|
||||||
zoomRatio,
|
/ zoomRatio, this.state.cameraPosition[2] = newZoom;
|
||||||
this.state.cameraPosition[2] = newZoom;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ export function lerp(a: number, b: number, t: number) {
|
|||||||
|
|
||||||
export function animate(
|
export function animate(
|
||||||
duration: number,
|
duration: number,
|
||||||
callback: (progress: number) => void | false,
|
callback: (progress: number) => void | false
|
||||||
) {
|
) {
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
const loop = (time: number) => {
|
const loop = (time: number) => {
|
||||||
@@ -33,41 +33,37 @@ export function createNodePath({
|
|||||||
cornerBottom = 0,
|
cornerBottom = 0,
|
||||||
leftBump = false,
|
leftBump = false,
|
||||||
rightBump = false,
|
rightBump = false,
|
||||||
aspectRatio = 1,
|
aspectRatio = 1
|
||||||
} = {}) {
|
} = {}) {
|
||||||
return `M0,${cornerTop}
|
return `M0,${cornerTop}
|
||||||
${
|
${cornerTop
|
||||||
cornerTop
|
? ` V${cornerTop}
|
||||||
? ` V${cornerTop}
|
|
||||||
Q0,0 ${cornerTop * aspectRatio},0
|
Q0,0 ${cornerTop * aspectRatio},0
|
||||||
H${100 - cornerTop * aspectRatio}
|
H${100 - cornerTop * aspectRatio}
|
||||||
Q100,0 100,${cornerTop}
|
Q100,0 100,${cornerTop}
|
||||||
`
|
`
|
||||||
: ` V0
|
: ` V0
|
||||||
H100
|
H100
|
||||||
`
|
`
|
||||||
}
|
}
|
||||||
V${y - height / 2}
|
V${y - height / 2}
|
||||||
${
|
${rightBump
|
||||||
rightBump
|
? ` C${100 - depth},${y - height / 2} ${100 - depth},${y + height / 2} 100,${y + height / 2}`
|
||||||
? ` C${100 - depth},${y - height / 2} ${100 - depth},${y + height / 2} 100,${y + height / 2}`
|
: ` H100`
|
||||||
: ` H100`
|
}
|
||||||
}
|
${cornerBottom
|
||||||
${
|
? ` V${100 - cornerBottom}
|
||||||
cornerBottom
|
|
||||||
? ` V${100 - cornerBottom}
|
|
||||||
Q100,100 ${100 - cornerBottom * aspectRatio},100
|
Q100,100 ${100 - cornerBottom * aspectRatio},100
|
||||||
H${cornerBottom * aspectRatio}
|
H${cornerBottom * aspectRatio}
|
||||||
Q0,100 0,${100 - cornerBottom}
|
Q0,100 0,${100 - cornerBottom}
|
||||||
`
|
`
|
||||||
: `${leftBump ? `V100 H0` : `V100`}`
|
: `${leftBump ? `V100 H0` : `V100`}`
|
||||||
}
|
}
|
||||||
${
|
${leftBump
|
||||||
leftBump
|
? ` V${y + height / 2} C${depth},${y + height / 2} ${depth},${y - height / 2} 0,${y - height / 2}`
|
||||||
? ` V${y + height / 2} C${depth},${y + height / 2} ${depth},${y - height / 2} 0,${y - height / 2}`
|
: ` H0`
|
||||||
: ` H0`
|
}
|
||||||
}
|
Z`.replace(/\s+/g, ' ');
|
||||||
Z`.replace(/\s+/g, " ");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const debounce = (fn: Function, ms = 300) => {
|
export const debounce = (fn: Function, ms = 300) => {
|
||||||
@@ -78,14 +74,13 @@ export const debounce = (fn: Function, ms = 300) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const clone: <T>(v: T) => T =
|
export const clone: <T>(v: T) => T = 'structedClone' in globalThis
|
||||||
"structedClone" in globalThis
|
? globalThis.structuredClone
|
||||||
? globalThis.structuredClone
|
: (obj) => JSON.parse(JSON.stringify(obj));
|
||||||
: (obj) => JSON.parse(JSON.stringify(obj));
|
|
||||||
|
|
||||||
export function withSubComponents<A, B extends Record<string, any>>(
|
export function withSubComponents<A, B extends Record<string, any>>(
|
||||||
component: A,
|
component: A,
|
||||||
subcomponents: B,
|
subcomponents: B
|
||||||
): A & B {
|
): A & B {
|
||||||
Object.keys(subcomponents).forEach((key) => {
|
Object.keys(subcomponents).forEach((key) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@@ -93,3 +88,27 @@ export function withSubComponents<A, B extends Record<string, any>>(
|
|||||||
});
|
});
|
||||||
return component as A & B;
|
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);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,16 +1,15 @@
|
|||||||
import { animate, lerp } from "$lib/helpers";
|
import { animate, lerp } from '$lib/helpers';
|
||||||
import type { createKeyMap } from "$lib/helpers/createKeyMap";
|
import type { createKeyMap } from '$lib/helpers/createKeyMap';
|
||||||
import FileSaver from "file-saver";
|
import { panelState } from '$lib/sidebar/PanelState.svelte';
|
||||||
import type { GraphManager } from "./graph-manager.svelte";
|
import FileSaver from 'file-saver';
|
||||||
import type { GraphState } from "./graph-state.svelte";
|
import type { GraphManager } from './graph-manager.svelte';
|
||||||
|
import type { GraphState } from './graph-state.svelte';
|
||||||
|
|
||||||
type Keymap = ReturnType<typeof createKeyMap>;
|
type Keymap = ReturnType<typeof createKeyMap>;
|
||||||
export function setupKeymaps(keymap: Keymap, graph: GraphManager, graphState: GraphState) {
|
export function setupKeymaps(keymap: Keymap, graph: GraphManager, graphState: GraphState) {
|
||||||
|
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: "l",
|
key: 'l',
|
||||||
description: "Select linked nodes",
|
description: 'Select linked nodes',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
const activeNode = graph.getNode(graphState.activeNodeId);
|
const activeNode = graph.getNode(graphState.activeNodeId);
|
||||||
if (activeNode) {
|
if (activeNode) {
|
||||||
@@ -20,56 +19,54 @@ export function setupKeymaps(keymap: Keymap, graph: GraphManager, graphState: Gr
|
|||||||
graphState.selectedNodes.add(node.id);
|
graphState.selectedNodes.add(node.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: "?",
|
key: '?',
|
||||||
description: "Toggle Help",
|
description: 'Toggle Help',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
// TODO: fix this
|
panelState.setActivePanel('shortcuts');
|
||||||
// showHelp = !showHelp;
|
}
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: "c",
|
key: 'c',
|
||||||
ctrl: true,
|
ctrl: true,
|
||||||
description: "Copy active nodes",
|
description: 'Copy active nodes',
|
||||||
callback: () => graphState.copyNodes(),
|
callback: () => graphState.copyNodes()
|
||||||
});
|
});
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: "v",
|
key: 'v',
|
||||||
ctrl: true,
|
ctrl: true,
|
||||||
description: "Paste nodes",
|
description: 'Paste nodes',
|
||||||
callback: () => graphState.pasteNodes(),
|
callback: () => graphState.pasteNodes()
|
||||||
});
|
});
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: "Escape",
|
key: 'Escape',
|
||||||
description: "Deselect nodes",
|
description: 'Deselect nodes',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
graphState.activeNodeId = -1;
|
graphState.activeNodeId = -1;
|
||||||
graphState.clearSelection();
|
graphState.clearSelection();
|
||||||
graphState.edgeEndPosition = null;
|
graphState.edgeEndPosition = null;
|
||||||
(document.activeElement as HTMLElement)?.blur();
|
(document.activeElement as HTMLElement)?.blur();
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: "A",
|
key: 'A',
|
||||||
shift: true,
|
shift: true,
|
||||||
description: "Add new Node",
|
description: 'Add new Node',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
graphState.addMenuPosition = [graphState.mousePosition[0], graphState.mousePosition[1]];
|
graphState.addMenuPosition = [graphState.mousePosition[0], graphState.mousePosition[1]];
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: ".",
|
key: '.',
|
||||||
description: "Center camera",
|
description: 'Center camera',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
if (!graphState.isBodyFocused()) return;
|
if (!graphState.isBodyFocused()) return;
|
||||||
|
|
||||||
@@ -90,67 +87,67 @@ export function setupKeymaps(keymap: Keymap, graph: GraphManager, graphState: Gr
|
|||||||
animate(500, (a: number) => {
|
animate(500, (a: number) => {
|
||||||
graphState.cameraPosition[0] = lerp(camX, average[0], ease(a));
|
graphState.cameraPosition[0] = lerp(camX, average[0], ease(a));
|
||||||
graphState.cameraPosition[1] = lerp(camY, average[1], ease(a));
|
graphState.cameraPosition[1] = lerp(camY, average[1], ease(a));
|
||||||
graphState.cameraPosition[2] = lerp(camZ, 2, ease(a))
|
graphState.cameraPosition[2] = lerp(camZ, 2, ease(a));
|
||||||
if (graphState.mouseDown) return false;
|
if (graphState.mouseDown) return false;
|
||||||
});
|
});
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: "a",
|
key: 'a',
|
||||||
ctrl: true,
|
ctrl: true,
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
description: "Select all nodes",
|
description: 'Select all nodes',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
if (!graphState.isBodyFocused()) return;
|
if (!graphState.isBodyFocused()) return;
|
||||||
for (const node of graph.nodes.keys()) {
|
for (const node of graph.nodes.keys()) {
|
||||||
graphState.selectedNodes.add(node);
|
graphState.selectedNodes.add(node);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: "z",
|
key: 'z',
|
||||||
ctrl: true,
|
ctrl: true,
|
||||||
description: "Undo",
|
description: 'Undo',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
if (!graphState.isBodyFocused()) return;
|
if (!graphState.isBodyFocused()) return;
|
||||||
graph.undo();
|
graph.undo();
|
||||||
for (const node of graph.nodes.values()) {
|
for (const node of graph.nodes.values()) {
|
||||||
graphState.updateNodePosition(node);
|
graphState.updateNodePosition(node);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: "y",
|
key: 'y',
|
||||||
ctrl: true,
|
ctrl: true,
|
||||||
description: "Redo",
|
description: 'Redo',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
graph.redo();
|
graph.redo();
|
||||||
for (const node of graph.nodes.values()) {
|
for (const node of graph.nodes.values()) {
|
||||||
graphState.updateNodePosition(node);
|
graphState.updateNodePosition(node);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: "s",
|
key: 's',
|
||||||
ctrl: true,
|
ctrl: true,
|
||||||
description: "Save",
|
description: 'Save',
|
||||||
preventDefault: true,
|
preventDefault: true,
|
||||||
callback: () => {
|
callback: () => {
|
||||||
const state = graph.serialize();
|
const state = graph.serialize();
|
||||||
const blob = new Blob([JSON.stringify(state)], {
|
const blob = new Blob([JSON.stringify(state)], {
|
||||||
type: "application/json;charset=utf-8",
|
type: 'application/json;charset=utf-8'
|
||||||
});
|
});
|
||||||
FileSaver.saveAs(blob, "nodarium-graph.json");
|
FileSaver.saveAs(blob, 'nodarium-graph.json');
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: ["Delete", "Backspace", "x"],
|
key: ['Delete', 'Backspace', 'x'],
|
||||||
description: "Delete selected nodes",
|
description: 'Delete selected nodes',
|
||||||
callback: (event) => {
|
callback: (event) => {
|
||||||
if (!graphState.isBodyFocused()) return;
|
if (!graphState.isBodyFocused()) return;
|
||||||
graph.startUndoGroup();
|
graph.startUndoGroup();
|
||||||
@@ -171,20 +168,18 @@ export function setupKeymaps(keymap: Keymap, graph: GraphManager, graphState: Gr
|
|||||||
graphState.clearSelection();
|
graphState.clearSelection();
|
||||||
}
|
}
|
||||||
graph.saveUndoGroup();
|
graph.saveUndoGroup();
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
keymap.addShortcut({
|
keymap.addShortcut({
|
||||||
key: "f",
|
key: 'f',
|
||||||
description: "Smart Connect Nodes",
|
description: 'Smart Connect Nodes',
|
||||||
callback: () => {
|
callback: () => {
|
||||||
const nodes = [...graphState.selectedNodes.values()]
|
const nodes = [...graphState.selectedNodes.values()]
|
||||||
.map((g) => graph.getNode(g))
|
.map((g) => graph.getNode(g))
|
||||||
.filter((n) => !!n);
|
.filter((n) => !!n);
|
||||||
const edge = graph.smartConnect(nodes[0], nodes[1]);
|
const edge = graph.smartConnect(nodes[0], nodes[1]);
|
||||||
if (!edge) graph.smartConnect(nodes[1], nodes[0]);
|
if (!edge) graph.smartConnect(nodes[1], nodes[0]);
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,8 +73,14 @@
|
|||||||
{#key id && graphId}
|
{#key id && graphId}
|
||||||
<div class="content" class:disabled={graph?.inputSockets?.has(socketId)}>
|
<div class="content" class:disabled={graph?.inputSockets?.has(socketId)}>
|
||||||
{#if inputType.label !== ""}
|
{#if inputType.label !== ""}
|
||||||
<label for={elementId}>{input.label || id}</label>
|
<label for={elementId} title={input.description}
|
||||||
|
>{input.label || id}</label
|
||||||
|
>
|
||||||
{/if}
|
{/if}
|
||||||
|
<span
|
||||||
|
class="absolute i-[tabler--help-circle] size-4 block top-2 right-2 opacity-30"
|
||||||
|
title={JSON.stringify(input, null, 2)}
|
||||||
|
></span>
|
||||||
{#if inputType.external !== true}
|
{#if inputType.external !== true}
|
||||||
<NodeInputEl {graph} {elementId} bind:node {input} {id} />
|
<NodeInputEl {graph} {elementId} bind:node {input} {id} />
|
||||||
{/if}
|
{/if}
|
||||||
@@ -181,9 +187,6 @@
|
|||||||
.content.disabled {
|
.content.disabled {
|
||||||
opacity: 0.2;
|
opacity: 0.2;
|
||||||
}
|
}
|
||||||
.content.disabled > * {
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disabled svg path {
|
.disabled svg path {
|
||||||
d: var(--hover-path-disabled) !important;
|
d: var(--hover-path-disabled) !important;
|
||||||
|
|||||||
@@ -95,12 +95,14 @@
|
|||||||
<SmallPerformanceViewer {fps} store={perf} />
|
<SmallPerformanceViewer {fps} store={perf} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<Canvas>
|
<div style="height: 100vh">
|
||||||
<Scene
|
<Canvas>
|
||||||
bind:this={sceneComponent}
|
<Scene
|
||||||
{lines}
|
bind:this={sceneComponent}
|
||||||
{centerCamera}
|
{lines}
|
||||||
bind:scene
|
{centerCamera}
|
||||||
bind:fps
|
bind:scene
|
||||||
/>
|
bind:fps
|
||||||
</Canvas>
|
/>
|
||||||
|
</Canvas>
|
||||||
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { getContext, type Snippet } from "svelte";
|
import { type Snippet } from "svelte";
|
||||||
import type { PanelState } from "./PanelState.svelte";
|
import { panelState } from "./PanelState.svelte";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
id,
|
id,
|
||||||
@@ -18,8 +18,6 @@
|
|||||||
children?: Snippet;
|
children?: Snippet;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const panelState = getContext<PanelState>("panel-state");
|
|
||||||
|
|
||||||
const panel = panelState.registerPanel(id, icon, classes, hidden);
|
const panel = panelState.registerPanel(id, icon, classes, hidden);
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
panel.hidden = hidden;
|
panel.hidden = hidden;
|
||||||
|
|||||||
@@ -1,15 +1,14 @@
|
|||||||
import { localState } from "$lib/helpers/localState.svelte";
|
import { localState } from '$lib/helpers/localState.svelte';
|
||||||
|
|
||||||
type Panel = {
|
type Panel = {
|
||||||
icon: string;
|
icon: string;
|
||||||
classes: string;
|
classes: string;
|
||||||
hidden?: boolean;
|
hidden?: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export class PanelState {
|
|
||||||
|
|
||||||
|
class PanelState {
|
||||||
panels = $state<Record<string, Panel>>({});
|
panels = $state<Record<string, Panel>>({});
|
||||||
activePanel = localState<string | boolean>("node.activePanel", "")
|
activePanel = localState<string | boolean>('node.activePanel', '');
|
||||||
|
|
||||||
get keys() {
|
get keys() {
|
||||||
return Object.keys(this.panels);
|
return Object.keys(this.panels);
|
||||||
@@ -19,7 +18,7 @@ export class PanelState {
|
|||||||
const state = $state({
|
const state = $state({
|
||||||
icon: icon,
|
icon: icon,
|
||||||
classes: classes,
|
classes: classes,
|
||||||
hidden: hidden,
|
hidden: hidden
|
||||||
});
|
});
|
||||||
this.panels[id] = state;
|
this.panels[id] = state;
|
||||||
return state;
|
return state;
|
||||||
@@ -29,7 +28,13 @@ export class PanelState {
|
|||||||
if (this.activePanel.value) {
|
if (this.activePanel.value) {
|
||||||
this.activePanel.value = false;
|
this.activePanel.value = false;
|
||||||
} else {
|
} else {
|
||||||
this.activePanel.value = this.keys[0]
|
this.activePanel.value = this.keys[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public setActivePanel(panelId: string) {
|
||||||
|
this.activePanel.value = panelId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const panelState = new PanelState();
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { setContext, type Snippet } from "svelte";
|
import { type Snippet } from "svelte";
|
||||||
import { PanelState } from "./PanelState.svelte";
|
import { panelState as state } from "./PanelState.svelte";
|
||||||
|
|
||||||
const state = new PanelState();
|
|
||||||
setContext("panel-state", state);
|
|
||||||
|
|
||||||
const { children } = $props<{ children?: Snippet }>();
|
const { children } = $props<{ children?: Snippet }>();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ This guide will help you developing your first Nodarium Node written in Rust. As
|
|||||||
|
|
||||||
## Prerequesites
|
## Prerequesites
|
||||||
|
|
||||||
You need to have [Rust](https://www.rust-lang.org/tools/install) and [wasm-pack](https://rustwasm.github.io/wasm-pack/book/) installed. Rust is the language we are going to develop our node in and wasm-pack helps us compile our rust code into a webassembly file.
|
You need to have [Rust](https://www.rust-lang.org/tools/install) and [wasm-pack](https://rustwasm.github.io/docs/wasm-pack/) installed. Rust is the language we are going to develop our node in and wasm-pack helps us compile our rust code into a webassembly file.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# install rust
|
# install rust
|
||||||
@@ -26,6 +26,7 @@ Now we create the definition file of the node.
|
|||||||
Here we define what kind of inputs our node will expect and what kind of output it produces. If you want to dive deeper into this topic, have a look at [NODE_DEFINITION.md](./NODE_DEFINITION.md).
|
Here we define what kind of inputs our node will expect and what kind of output it produces. If you want to dive deeper into this topic, have a look at [NODE_DEFINITION.md](./NODE_DEFINITION.md).
|
||||||
|
|
||||||
`src/definition.json`
|
`src/definition.json`
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"id": "my-name/my-namespace/zylinder-node",
|
"id": "my-name/my-namespace/zylinder-node",
|
||||||
@@ -35,7 +36,7 @@ Here we define what kind of inputs our node will expect and what kind of output
|
|||||||
"inputs": {
|
"inputs": {
|
||||||
"height": {
|
"height": {
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"value": 2,
|
"value": 2
|
||||||
},
|
},
|
||||||
"radius": {
|
"radius": {
|
||||||
"type": "float",
|
"type": "float",
|
||||||
@@ -44,6 +45,7 @@ Here we define what kind of inputs our node will expect and what kind of output
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
If we take a look at the `src/lib.rs` file we see that `src/definition.json` is included with the following line:
|
If we take a look at the `src/lib.rs` file we see that `src/definition.json` is included with the following line:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
pkgs.typescript-language-server
|
pkgs.typescript-language-server
|
||||||
pkgs.prettier
|
pkgs.prettier
|
||||||
pkgs.tailwindcss-language-server
|
pkgs.tailwindcss-language-server
|
||||||
|
pkgs.svelte-language-server
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,9 +4,10 @@
|
|||||||
"build:story": "pnpm -r --filter 'ui' story:build",
|
"build:story": "pnpm -r --filter 'ui' story:build",
|
||||||
"build:app": "BASE_PATH=/ui pnpm -r --filter 'ui' build && pnpm -r --filter 'app' build",
|
"build:app": "BASE_PATH=/ui pnpm -r --filter 'ui' build && pnpm -r --filter 'app' build",
|
||||||
"build:nodes": "pnpm -r --filter './nodes/**' build",
|
"build:nodes": "pnpm -r --filter './nodes/**' build",
|
||||||
"dev:nodes": "pnpm -r --parallel --filter './nodes/**' dev",
|
|
||||||
"build:deploy": "pnpm build",
|
"build:deploy": "pnpm build",
|
||||||
"dev": "pnpm -r --filter 'app' --filter './packages/node-registry' dev"
|
"dev:all": "pnpm -r dev",
|
||||||
|
"dev:nodes": "pnpm -r --parallel --filter './nodes/**' dev",
|
||||||
|
"dev": "pnpm -r --filter 'app' --filter './packages/ui' dev"
|
||||||
},
|
},
|
||||||
"packageManager": "pnpm@10.24.0"
|
"packageManager": "pnpm@10.24.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,5 @@
|
|||||||
export type { NodeInput } from "./inputs";
|
export type { AsyncCache, NodeRegistry, RuntimeExecutor, SyncCache } from './components';
|
||||||
export type {
|
export type { NodeInput } from './inputs';
|
||||||
NodeRegistry,
|
export type { Box, Edge, Graph, NodeDefinition, NodeId, NodeInstance, SerializedNode, Socket } from './types';
|
||||||
RuntimeExecutor,
|
export { GraphSchema, NodeSchema } from './types';
|
||||||
SyncCache,
|
export { NodeDefinitionSchema } from './types';
|
||||||
AsyncCache,
|
|
||||||
} from "./components";
|
|
||||||
export type {
|
|
||||||
SerializedNode,
|
|
||||||
NodeInstance,
|
|
||||||
NodeDefinition,
|
|
||||||
Socket,
|
|
||||||
NodeId,
|
|
||||||
Edge,
|
|
||||||
Graph,
|
|
||||||
} from "./types";
|
|
||||||
export { NodeSchema, GraphSchema } from "./types";
|
|
||||||
export { NodeDefinitionSchema } from "./types";
|
|
||||||
|
|||||||
@@ -1,9 +1,16 @@
|
|||||||
import { z } from "zod";
|
import { z } from 'zod';
|
||||||
import { NodeInputSchema } from "./inputs";
|
import { NodeInputSchema } from './inputs';
|
||||||
|
|
||||||
|
export type Box = {
|
||||||
|
minX: number;
|
||||||
|
maxX: number;
|
||||||
|
minY: number;
|
||||||
|
maxY: number;
|
||||||
|
};
|
||||||
|
|
||||||
export const NodeIdSchema = z
|
export const NodeIdSchema = z
|
||||||
.string()
|
.string()
|
||||||
.regex(/^[^/]+\/[^/]+\/[^/]+$/, "Invalid NodeId format")
|
.regex(/^[^/]+\/[^/]+\/[^/]+$/, 'Invalid NodeId format')
|
||||||
.transform((value) => value as `${string}/${string}/${string}`);
|
.transform((value) => value as `${string}/${string}/${string}`);
|
||||||
|
|
||||||
export type NodeId = z.infer<typeof NodeIdSchema>;
|
export type NodeId = z.infer<typeof NodeIdSchema>;
|
||||||
@@ -35,9 +42,9 @@ export const NodeDefinitionSchema = z.object({
|
|||||||
meta: z
|
meta: z
|
||||||
.object({
|
.object({
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
title: z.string().optional(),
|
title: z.string().optional()
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional()
|
||||||
});
|
});
|
||||||
|
|
||||||
export const NodeSchema = z.object({
|
export const NodeSchema = z.object({
|
||||||
@@ -49,13 +56,12 @@ export const NodeSchema = z.object({
|
|||||||
meta: z
|
meta: z
|
||||||
.object({
|
.object({
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
lastModified: z.string().optional(),
|
lastModified: z.string().optional()
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
position: z.tuple([z.number(), z.number()]),
|
position: z.tuple([z.number(), z.number()])
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
export type SerializedNode = z.infer<typeof NodeSchema>;
|
export type SerializedNode = z.infer<typeof NodeSchema>;
|
||||||
|
|
||||||
export type NodeDefinition = z.infer<typeof NodeDefinitionSchema> & {
|
export type NodeDefinition = z.infer<typeof NodeDefinitionSchema> & {
|
||||||
@@ -75,12 +81,12 @@ export const GraphSchema = z.object({
|
|||||||
meta: z
|
meta: z
|
||||||
.object({
|
.object({
|
||||||
title: z.string().optional(),
|
title: z.string().optional(),
|
||||||
lastModified: z.string().optional(),
|
lastModified: z.string().optional()
|
||||||
})
|
})
|
||||||
.optional(),
|
.optional(),
|
||||||
settings: z.record(z.string(), z.any()).optional(),
|
settings: z.record(z.string(), z.any()).optional(),
|
||||||
nodes: z.array(NodeSchema),
|
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<typeof GraphSchema>;
|
export type Graph = z.infer<typeof GraphSchema>;
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"name": "@nodarium/ui",
|
"name": "@nodarium/ui",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "chokidar './src/**' --initial -c 'pnpm build'",
|
||||||
"build": "vite build && npm run package",
|
"build": "vite build && npm run package",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"package": "svelte-kit sync && svelte-package && publint",
|
"package": "svelte-kit sync && svelte-package && publint",
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
"@types/three": "^0.182.0",
|
"@types/three": "^0.182.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
"@typescript-eslint/eslint-plugin": "^8.53.0",
|
||||||
"@typescript-eslint/parser": "^8.53.0",
|
"@typescript-eslint/parser": "^8.53.0",
|
||||||
|
"chokidar-cli": "^3.0.0",
|
||||||
"eslint": "^9.39.2",
|
"eslint": "^9.39.2",
|
||||||
"eslint-plugin-svelte": "^3.14.0",
|
"eslint-plugin-svelte": "^3.14.0",
|
||||||
"publint": "^0.3.16",
|
"publint": "^0.3.16",
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Checkbox from './inputs/Checkbox.svelte';
|
|
||||||
import Float from './inputs/Float.svelte';
|
|
||||||
import Integer from './inputs/Integer.svelte';
|
|
||||||
import Select from './inputs/Select.svelte';
|
|
||||||
|
|
||||||
import type { NodeInput } from '@nodarium/types';
|
import type { NodeInput } from '@nodarium/types';
|
||||||
|
|
||||||
|
import Checkbox from './inputs/Checkbox.svelte';
|
||||||
|
import Number from './inputs/Number.svelte';
|
||||||
|
import Select from './inputs/Select.svelte';
|
||||||
import Vec3 from './inputs/Vec3.svelte';
|
import Vec3 from './inputs/Vec3.svelte';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
input: NodeInput;
|
input: NodeInput;
|
||||||
value: any;
|
value: any;
|
||||||
|
id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { input, value = $bindable() }: Props = $props();
|
let { input, value = $bindable(), id }: Props = $props();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if input.type === 'float'}
|
{#if input.type === 'float'}
|
||||||
<Float bind:value min={input?.min} max={input?.max} />
|
<Number bind:value min={input?.min} max={input?.max} step={0.01} />
|
||||||
{:else if input.type === 'integer'}
|
{:else if input.type === 'integer'}
|
||||||
<Integer bind:value min={input?.min} max={input?.max} />
|
<Number bind:value min={input?.min} max={input?.max} />
|
||||||
{:else if input.type === 'boolean'}
|
{:else if input.type === 'boolean'}
|
||||||
<Checkbox bind:value />
|
<Checkbox bind:value {id} />
|
||||||
{:else if input.type === 'select'}
|
{:else if input.type === 'select'}
|
||||||
<Select bind:value options={input.options} />
|
<Select bind:value options={input.options} {id} />
|
||||||
{:else if input.type === 'vec3'}
|
{:else if input.type === 'vec3'}
|
||||||
<Vec3 bind:value />
|
<Vec3 bind:value {id} />
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
value: boolean;
|
value: boolean;
|
||||||
|
id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
let { value = $bindable(false) }: Props = $props();
|
let { value = $bindable(false), id }: Props = $props();
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (typeof value === 'string') {
|
if (typeof value === 'string') {
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
type="checkbox"
|
type="checkbox"
|
||||||
bind:checked={value}
|
bind:checked={value}
|
||||||
class="peer absolute h-px w-px overflow-hidden whitespace-nowrap border-0 p-0 [clip:rect(0,0,0,0)]"
|
class="peer absolute h-px w-px overflow-hidden whitespace-nowrap border-0 p-0 [clip:rect(0,0,0,0)]"
|
||||||
|
{id}
|
||||||
/>
|
/>
|
||||||
<span
|
<span
|
||||||
class="absolute opacity-0 peer-checked:opacity-100 transition-opacity duration-100 flex w-full h-full items-center justify-center"
|
class="absolute opacity-0 peer-checked:opacity-100 transition-opacity duration-100 flex w-full h-full items-center justify-center"
|
||||||
|
|||||||
@@ -4,13 +4,15 @@
|
|||||||
step?: number;
|
step?: number;
|
||||||
min?: number;
|
min?: number;
|
||||||
max?: number;
|
max?: number;
|
||||||
|
id?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
value = $bindable(0.5),
|
value = $bindable(0.5),
|
||||||
step = 0.01,
|
step = 0.01,
|
||||||
min = $bindable(0),
|
min = $bindable(0),
|
||||||
max = $bindable(1)
|
max = $bindable(1),
|
||||||
|
id
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
if (min > max) {
|
if (min > max) {
|
||||||
@@ -110,6 +112,7 @@
|
|||||||
<input
|
<input
|
||||||
bind:value
|
bind:value
|
||||||
bind:this={inputEl}
|
bind:this={inputEl}
|
||||||
|
{id}
|
||||||
{step}
|
{step}
|
||||||
{max}
|
{max}
|
||||||
{min}
|
{min}
|
||||||
|
|||||||
@@ -1,41 +1,48 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createEventDispatcher } from 'svelte';
|
|
||||||
const dispatch = createEventDispatcher();
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
value?: number;
|
||||||
|
step?: number;
|
||||||
min?: number | undefined;
|
min?: number | undefined;
|
||||||
max?: number | undefined;
|
max?: number | undefined;
|
||||||
step?: number;
|
|
||||||
value?: number;
|
|
||||||
id?: string;
|
id?: string;
|
||||||
|
change?: (arg: number) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
let {
|
let {
|
||||||
min = undefined,
|
|
||||||
max = undefined,
|
|
||||||
step = 1,
|
|
||||||
value = $bindable(0),
|
value = $bindable(0),
|
||||||
id = ''
|
step = 1,
|
||||||
|
min = $bindable(0),
|
||||||
|
max = $bindable(1),
|
||||||
|
id,
|
||||||
|
change
|
||||||
}: Props = $props();
|
}: Props = $props();
|
||||||
|
|
||||||
if (!value) {
|
if (min > max) {
|
||||||
value = 0;
|
[min, max] = [max, min];
|
||||||
|
}
|
||||||
|
if (value > max) {
|
||||||
|
max = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
let inputEl: HTMLInputElement | undefined = $state();
|
function strip(input: number) {
|
||||||
let wrapper: HTMLDivElement | undefined = $state();
|
return +parseFloat(input + '').toPrecision(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputEl = $state() as HTMLInputElement;
|
||||||
|
let wrapper = $state() as HTMLDivElement;
|
||||||
|
|
||||||
let prev = -1;
|
let prev = -1;
|
||||||
function update() {
|
function update() {
|
||||||
if (prev === value) return;
|
if (prev === value) return;
|
||||||
prev = value;
|
prev = value;
|
||||||
dispatch('change', parseFloat(value + ''));
|
change?.(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleChange(change: number) {
|
function handleChange(change: number) {
|
||||||
value = Math.max(min ?? -Infinity, Math.min(+value + change, max ?? Infinity));
|
value = Math.max(min ?? -Infinity, Math.min(+value + change, max ?? Infinity));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let isMouseDown = $state(false);
|
||||||
let downX = 0;
|
let downX = 0;
|
||||||
let downV = 0;
|
let downV = 0;
|
||||||
let rect: DOMRect;
|
let rect: DOMRect;
|
||||||
@@ -65,6 +72,13 @@
|
|||||||
window.removeEventListener('mousemove', handleMouseMove);
|
window.removeEventListener('mousemove', handleMouseMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleKeyDown(ev: KeyboardEvent) {
|
||||||
|
if (ev.key === 'Escape' || ev.key === 'Enter') {
|
||||||
|
handleMouseUp();
|
||||||
|
inputEl?.blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function handleMouseMove(ev: MouseEvent) {
|
function handleMouseMove(ev: MouseEvent) {
|
||||||
if (!ev.ctrlKey && typeof min === 'number' && typeof max === 'number') {
|
if (!ev.ctrlKey && typeof min === 'number' && typeof max === 'number') {
|
||||||
const vx = (ev.clientX - rect.left) / rect.width;
|
const vx = (ev.clientX - rect.left) / rect.width;
|
||||||
@@ -76,8 +90,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
|
if ((value || 0).toString().length > 5) {
|
||||||
|
value = strip(value || 0);
|
||||||
|
}
|
||||||
value !== undefined && update();
|
value !== undefined && update();
|
||||||
});
|
});
|
||||||
|
|
||||||
let width = $derived(
|
let width = $derived(
|
||||||
Number.isFinite(value) ? Math.max((value?.toString().length ?? 1) * 8, 30) + 'px' : '20px'
|
Number.isFinite(value) ? Math.max((value?.toString().length ?? 1) * 8, 30) + 'px' : '20px'
|
||||||
);
|
);
|
||||||
@@ -86,29 +104,33 @@
|
|||||||
<div
|
<div
|
||||||
class="component-wrapper"
|
class="component-wrapper"
|
||||||
bind:this={wrapper}
|
bind:this={wrapper}
|
||||||
|
class:is-down={isMouseDown}
|
||||||
role="slider"
|
role="slider"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
aria-valuenow={value}
|
aria-valuenow={value}
|
||||||
|
onkeydown={handleKeyDown}
|
||||||
onmousedown={handleMouseDown}
|
onmousedown={handleMouseDown}
|
||||||
onmouseup={handleMouseUp}
|
onmouseup={handleMouseUp}
|
||||||
>
|
>
|
||||||
|
<div class="">
|
||||||
|
<button onclick={() => handleChange(-step)}>-</button>
|
||||||
|
<input
|
||||||
|
bind:value
|
||||||
|
bind:this={inputEl}
|
||||||
|
{id}
|
||||||
|
{step}
|
||||||
|
{max}
|
||||||
|
{min}
|
||||||
|
type="number"
|
||||||
|
style={`width:${width};`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<button onclick={() => handleChange(+step)}>+</button>
|
||||||
|
</div>
|
||||||
{#if typeof min !== 'undefined' && typeof max !== 'undefined'}
|
{#if typeof min !== 'undefined' && typeof max !== 'undefined'}
|
||||||
<span class="overlay" style={`width: ${Math.min((value - min) / (max - min), 1) * 100}%`}
|
<span class="overlay" style={`width: ${Math.min((value - min) / (max - min), 1) * 100}%`}
|
||||||
></span>
|
></span>
|
||||||
{/if}
|
{/if}
|
||||||
<button onclick={() => handleChange(-step)}>-</button>
|
|
||||||
<input
|
|
||||||
bind:value
|
|
||||||
bind:this={inputEl}
|
|
||||||
{id}
|
|
||||||
{step}
|
|
||||||
{max}
|
|
||||||
{min}
|
|
||||||
type="number"
|
|
||||||
style={`width:${width};`}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button onclick={() => handleChange(+step)}>+</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
@@ -124,21 +146,32 @@
|
|||||||
border-radius: var(--border-radius, 2px);
|
border-radius: var(--border-radius, 2px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type='number']::-webkit-inner-spin-button,
|
||||||
|
input[type='number']::-webkit-outer-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
input[type='number'] {
|
input[type='number'] {
|
||||||
-webkit-appearance: textfield;
|
-webkit-appearance: textfield;
|
||||||
-moz-appearance: textfield;
|
-moz-appearance: textfield;
|
||||||
appearance: textfield;
|
appearance: textfield;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 1em;
|
|
||||||
font-family: var(--font-family);
|
font-family: var(--font-family);
|
||||||
padding-top: 8px;
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: var(--text-color);
|
||||||
|
background-color: transparent;
|
||||||
|
padding: var(--padding, 6px);
|
||||||
|
font-size: 1em;
|
||||||
|
padding-inline: 10px;
|
||||||
|
text-align: center;
|
||||||
|
border: none;
|
||||||
|
border-style: none;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
width: 72%;
|
width: 72%;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type='number']::-webkit-inner-spin-button,
|
.is-down > input {
|
||||||
input[type='number']::-webkit-outer-spin-button {
|
cursor: ew-resize !important;
|
||||||
-webkit-appearance: none;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.overlay {
|
.overlay {
|
||||||
@@ -151,6 +184,10 @@
|
|||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.is-down > .overlay {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
button {
|
button {
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
177
packages/ui/src/lib/inputs/Number.svelte
Normal file
177
packages/ui/src/lib/inputs/Number.svelte
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
interface Props {
|
||||||
|
value?: number;
|
||||||
|
step?: number;
|
||||||
|
min?: number;
|
||||||
|
max?: number;
|
||||||
|
id?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
let {
|
||||||
|
value = $bindable(1),
|
||||||
|
step = 1,
|
||||||
|
min = $bindable(0),
|
||||||
|
max = $bindable(1),
|
||||||
|
id: _id
|
||||||
|
}: Props = $props();
|
||||||
|
const uid = $props.id();
|
||||||
|
const id = $derived(_id || uid);
|
||||||
|
|
||||||
|
if (min > max) {
|
||||||
|
[min, max] = [max, min];
|
||||||
|
}
|
||||||
|
if (value > max) {
|
||||||
|
max = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function strip(input: number) {
|
||||||
|
return +parseFloat(input + '').toPrecision(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
let inputEl = $state() as HTMLInputElement;
|
||||||
|
|
||||||
|
let prev = -1;
|
||||||
|
function update() {
|
||||||
|
if (prev === value) return;
|
||||||
|
if (value.toString().length > 5) {
|
||||||
|
value = strip(value);
|
||||||
|
}
|
||||||
|
prev = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(change: number) {
|
||||||
|
value = Math.max(min ?? -Infinity, Math.min(+value + change, max ?? Infinity));
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleKeyDown(ev: KeyboardEvent) {
|
||||||
|
if (ev.key === 'Escape' || ev.key === 'Enter') {
|
||||||
|
inputEl?.blur();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
|
||||||
|
let ratio = $derived(((value - min) / (max - min)) * 100);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<div class="component-wrapper">
|
||||||
|
<button onclick={() => handleChange(-step)}>-</button>
|
||||||
|
<input
|
||||||
|
bind:value
|
||||||
|
bind:this={inputEl}
|
||||||
|
{id}
|
||||||
|
{step}
|
||||||
|
{max}
|
||||||
|
{min}
|
||||||
|
type="number"
|
||||||
|
onkeydown={handleKeyDown}
|
||||||
|
/>
|
||||||
|
<button onclick={() => handleChange(+step)}>+</button>
|
||||||
|
</div>
|
||||||
|
<div class="slider">
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
bind:value
|
||||||
|
{min}
|
||||||
|
{max}
|
||||||
|
{step}
|
||||||
|
style={`background: linear-gradient(90deg, var(--text-color) ${ratio}%, var(--layer-2, #4b4b4b) ${ratio}%)`}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
--slider-height: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.component-wrapper {
|
||||||
|
display: flex;
|
||||||
|
background-color: var(--layer-2, #4b4b4b);
|
||||||
|
user-select: none;
|
||||||
|
transition: box-shadow 0.3s ease;
|
||||||
|
border: solid 1px var(--outline);
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 0 var(--border-radius, 2px); /* only top */
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='number']::-webkit-inner-spin-button,
|
||||||
|
input[type='number']::-webkit-outer-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='number'] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
appearance: textfield;
|
||||||
|
cursor: pointer;
|
||||||
|
font-family: var(--font-family);
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: var(--text-color);
|
||||||
|
background-color: transparent;
|
||||||
|
padding: var(--padding, 6px);
|
||||||
|
font-size: 1em;
|
||||||
|
padding-inline: 10px;
|
||||||
|
text-align: center;
|
||||||
|
border: none;
|
||||||
|
border-style: none;
|
||||||
|
flex: 1;
|
||||||
|
width: 72%;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
background-color: transparent;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 0px;
|
||||||
|
margin: 0;
|
||||||
|
color: var(--text-color);
|
||||||
|
margin-inline: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div input[type='number'] {
|
||||||
|
color: var(--text-color);
|
||||||
|
background-color: transparent;
|
||||||
|
padding: var(--padding, 6px);
|
||||||
|
padding-inline: 0px;
|
||||||
|
text-align: center;
|
||||||
|
border: none;
|
||||||
|
border-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider {
|
||||||
|
position: relative;
|
||||||
|
margin-top: -1px; /* hide edge */
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='range'] {
|
||||||
|
position: absolute;
|
||||||
|
appearance: none;
|
||||||
|
width: 100%;
|
||||||
|
height: var(--slider-height);
|
||||||
|
background: var(--layer-2, #4b4b4b);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thumb: for Chrome, Safari, Edge */
|
||||||
|
input[type='range']::-webkit-slider-thumb {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
width: 12px;
|
||||||
|
height: var(--slider-height);
|
||||||
|
background: var(--text-color);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Thumb: for Firefox */
|
||||||
|
input[type='range']::-moz-range-thumb {
|
||||||
|
border: none;
|
||||||
|
width: 12px;
|
||||||
|
height: var(--slider-height);
|
||||||
|
background: var(--text-color);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
309
pnpm-lock.yaml
generated
309
pnpm-lock.yaml
generated
@@ -197,6 +197,9 @@ importers:
|
|||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
specifier: ^8.53.0
|
specifier: ^8.53.0
|
||||||
version: 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
version: 8.53.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||||
|
chokidar-cli:
|
||||||
|
specifier: ^3.0.0
|
||||||
|
version: 3.0.0
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^9.39.2
|
specifier: ^9.39.2
|
||||||
version: 9.39.2(jiti@2.6.1)
|
version: 9.39.2(jiti@2.6.1)
|
||||||
@@ -1248,10 +1251,22 @@ packages:
|
|||||||
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
|
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
ansi-regex@4.1.1:
|
||||||
|
resolution: {integrity: sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
ansi-styles@3.2.1:
|
||||||
|
resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
anymatch@3.1.3:
|
||||||
|
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||||
|
engines: {node: '>= 8'}
|
||||||
|
|
||||||
arg@4.1.3:
|
arg@4.1.3:
|
||||||
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
|
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
|
||||||
|
|
||||||
@@ -1279,6 +1294,10 @@ packages:
|
|||||||
bidi-js@1.0.3:
|
bidi-js@1.0.3:
|
||||||
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
|
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
|
||||||
|
|
||||||
|
binary-extensions@2.3.0:
|
||||||
|
resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
boolbase@1.0.0:
|
boolbase@1.0.0:
|
||||||
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
|
||||||
|
|
||||||
@@ -1288,6 +1307,10 @@ packages:
|
|||||||
brace-expansion@2.0.2:
|
brace-expansion@2.0.2:
|
||||||
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
|
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
|
||||||
|
|
||||||
|
braces@3.0.3:
|
||||||
|
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
buffer-from@1.1.2:
|
buffer-from@1.1.2:
|
||||||
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
|
||||||
|
|
||||||
@@ -1311,6 +1334,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
camelcase@5.3.1:
|
||||||
|
resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
camera-controls@3.1.2:
|
camera-controls@3.1.2:
|
||||||
resolution: {integrity: sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==}
|
resolution: {integrity: sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==}
|
||||||
engines: {node: '>=22.0.0', npm: '>=10.5.1'}
|
engines: {node: '>=22.0.0', npm: '>=10.5.1'}
|
||||||
@@ -1325,6 +1352,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
chokidar-cli@3.0.0:
|
||||||
|
resolution: {integrity: sha512-xVW+Qeh7z15uZRxHOkP93Ux8A0xbPzwK4GaqD8dQOYc34TlkqUhVSS59fK36DOp5WdJlrRzlYSy02Ht99FjZqQ==}
|
||||||
|
engines: {node: '>= 8.10.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
|
chokidar@3.6.0:
|
||||||
|
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||||
|
engines: {node: '>= 8.10.0'}
|
||||||
|
|
||||||
chokidar@4.0.3:
|
chokidar@4.0.3:
|
||||||
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
|
||||||
engines: {node: '>= 14.16.0'}
|
engines: {node: '>= 14.16.0'}
|
||||||
@@ -1336,14 +1372,23 @@ packages:
|
|||||||
citty@0.1.6:
|
citty@0.1.6:
|
||||||
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
|
resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==}
|
||||||
|
|
||||||
|
cliui@5.0.0:
|
||||||
|
resolution: {integrity: sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==}
|
||||||
|
|
||||||
clsx@2.1.1:
|
clsx@2.1.1:
|
||||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
color-convert@1.9.3:
|
||||||
|
resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
|
||||||
engines: {node: '>=7.0.0'}
|
engines: {node: '>=7.0.0'}
|
||||||
|
|
||||||
|
color-name@1.1.3:
|
||||||
|
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
|
||||||
|
|
||||||
color-name@1.1.4:
|
color-name@1.1.4:
|
||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
|
|
||||||
@@ -1437,6 +1482,10 @@ packages:
|
|||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
decamelize@1.2.0:
|
||||||
|
resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
decimal.js@10.6.0:
|
decimal.js@10.6.0:
|
||||||
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
|
||||||
|
|
||||||
@@ -1510,6 +1559,9 @@ packages:
|
|||||||
earcut@2.2.4:
|
earcut@2.2.4:
|
||||||
resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==}
|
resolution: {integrity: sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ==}
|
||||||
|
|
||||||
|
emoji-regex@7.0.3:
|
||||||
|
resolution: {integrity: sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==}
|
||||||
|
|
||||||
enhanced-resolve@5.18.4:
|
enhanced-resolve@5.18.4:
|
||||||
resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==}
|
resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
@@ -1658,6 +1710,14 @@ packages:
|
|||||||
file-saver@2.0.5:
|
file-saver@2.0.5:
|
||||||
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
|
resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==}
|
||||||
|
|
||||||
|
fill-range@7.1.1:
|
||||||
|
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
find-up@3.0.0:
|
||||||
|
resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
find-up@5.0.0:
|
find-up@5.0.0:
|
||||||
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -1681,6 +1741,10 @@ packages:
|
|||||||
function-bind@1.1.2:
|
function-bind@1.1.2:
|
||||||
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
|
||||||
|
|
||||||
|
get-caller-file@2.0.5:
|
||||||
|
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||||
|
engines: {node: 6.* || 8.* || >= 10.*}
|
||||||
|
|
||||||
get-intrinsic@1.3.0:
|
get-intrinsic@1.3.0:
|
||||||
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
@@ -1696,6 +1760,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==}
|
resolution: {integrity: sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
glob-parent@5.1.2:
|
||||||
|
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||||
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
glob-parent@6.0.2:
|
glob-parent@6.0.2:
|
||||||
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
@@ -1774,6 +1842,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
|
||||||
engines: {node: '>=0.8.19'}
|
engines: {node: '>=0.8.19'}
|
||||||
|
|
||||||
|
is-binary-path@2.1.0:
|
||||||
|
resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
is-docker@3.0.0:
|
is-docker@3.0.0:
|
||||||
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
|
resolution: {integrity: sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==}
|
||||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||||
@@ -1783,6 +1855,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
is-fullwidth-code-point@2.0.0:
|
||||||
|
resolution: {integrity: sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
is-glob@4.0.3:
|
is-glob@4.0.3:
|
||||||
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@@ -1796,6 +1872,10 @@ packages:
|
|||||||
engines: {node: '>=14.16'}
|
engines: {node: '>=14.16'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
is-number@7.0.0:
|
||||||
|
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||||
|
engines: {node: '>=0.12.0'}
|
||||||
|
|
||||||
is-potential-custom-element-name@1.0.1:
|
is-potential-custom-element-name@1.0.1:
|
||||||
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
|
resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
|
||||||
|
|
||||||
@@ -1944,13 +2024,23 @@ packages:
|
|||||||
locate-character@3.0.0:
|
locate-character@3.0.0:
|
||||||
resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
|
resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
|
||||||
|
|
||||||
|
locate-path@3.0.0:
|
||||||
|
resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
locate-path@6.0.0:
|
locate-path@6.0.0:
|
||||||
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
lodash.debounce@4.0.8:
|
||||||
|
resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
|
||||||
|
|
||||||
lodash.merge@4.6.2:
|
lodash.merge@4.6.2:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
|
|
||||||
|
lodash.throttle@4.1.1:
|
||||||
|
resolution: {integrity: sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==}
|
||||||
|
|
||||||
lodash@4.17.21:
|
lodash@4.17.21:
|
||||||
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
|
||||||
|
|
||||||
@@ -2049,6 +2139,10 @@ packages:
|
|||||||
node-fetch-native@1.6.7:
|
node-fetch-native@1.6.7:
|
||||||
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
|
resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==}
|
||||||
|
|
||||||
|
normalize-path@3.0.0:
|
||||||
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
nth-check@2.1.1:
|
nth-check@2.1.1:
|
||||||
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
|
||||||
|
|
||||||
@@ -2074,14 +2168,26 @@ packages:
|
|||||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
|
||||||
|
p-limit@2.3.0:
|
||||||
|
resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
p-limit@3.1.0:
|
p-limit@3.1.0:
|
||||||
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
p-locate@3.0.0:
|
||||||
|
resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
p-locate@5.0.0:
|
p-locate@5.0.0:
|
||||||
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
p-try@2.2.0:
|
||||||
|
resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
package-manager-detector@1.6.0:
|
package-manager-detector@1.6.0:
|
||||||
resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
|
resolution: {integrity: sha512-61A5ThoTiDG/C8s8UMZwSorAGwMJ0ERVGj2OjoW5pAalsNOg15+iQiPzrLJ4jhZ1HJzmC2PIHT2oEiH3R5fzNA==}
|
||||||
|
|
||||||
@@ -2096,6 +2202,10 @@ packages:
|
|||||||
parse5@7.3.0:
|
parse5@7.3.0:
|
||||||
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
|
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
|
||||||
|
|
||||||
|
path-exists@3.0.0:
|
||||||
|
resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
path-exists@4.0.0:
|
path-exists@4.0.0:
|
||||||
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -2113,6 +2223,10 @@ packages:
|
|||||||
picocolors@1.1.1:
|
picocolors@1.1.1:
|
||||||
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
|
||||||
|
|
||||||
|
picomatch@2.3.1:
|
||||||
|
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
|
||||||
|
engines: {node: '>=8.6'}
|
||||||
|
|
||||||
picomatch@4.0.3:
|
picomatch@4.0.3:
|
||||||
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
@@ -2182,6 +2296,10 @@ packages:
|
|||||||
rc9@2.1.2:
|
rc9@2.1.2:
|
||||||
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
|
resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==}
|
||||||
|
|
||||||
|
readdirp@3.6.0:
|
||||||
|
resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
|
||||||
|
engines: {node: '>=8.10.0'}
|
||||||
|
|
||||||
readdirp@4.1.2:
|
readdirp@4.1.2:
|
||||||
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
|
||||||
engines: {node: '>= 14.18.0'}
|
engines: {node: '>= 14.18.0'}
|
||||||
@@ -2190,10 +2308,17 @@ packages:
|
|||||||
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
|
resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==}
|
||||||
engines: {node: '>= 20.19.0'}
|
engines: {node: '>= 20.19.0'}
|
||||||
|
|
||||||
|
require-directory@2.1.1:
|
||||||
|
resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
require-from-string@2.0.2:
|
require-from-string@2.0.2:
|
||||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
require-main-filename@2.0.0:
|
||||||
|
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
|
||||||
|
|
||||||
resolve-from@4.0.0:
|
resolve-from@4.0.0:
|
||||||
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -2248,6 +2373,9 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
set-blocking@2.0.0:
|
||||||
|
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
|
||||||
|
|
||||||
set-cookie-parser@2.7.2:
|
set-cookie-parser@2.7.2:
|
||||||
resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
|
resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
|
||||||
|
|
||||||
@@ -2287,6 +2415,14 @@ packages:
|
|||||||
std-env@3.10.0:
|
std-env@3.10.0:
|
||||||
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==}
|
||||||
|
|
||||||
|
string-width@3.1.0:
|
||||||
|
resolution: {integrity: sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
strip-ansi@5.2.0:
|
||||||
|
resolution: {integrity: sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
strip-json-comments@3.1.1:
|
strip-json-comments@3.1.1:
|
||||||
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@@ -2387,6 +2523,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==}
|
resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
to-regex-range@5.0.1:
|
||||||
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
|
engines: {node: '>=8.0'}
|
||||||
|
|
||||||
totalist@3.0.1:
|
totalist@3.0.1:
|
||||||
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -2601,6 +2741,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
|
resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
|
which-module@2.0.1:
|
||||||
|
resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
@@ -2615,6 +2758,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
wrap-ansi@5.1.0:
|
||||||
|
resolution: {integrity: sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
ws@8.19.0:
|
ws@8.19.0:
|
||||||
resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
|
resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==}
|
||||||
engines: {node: '>=10.0.0'}
|
engines: {node: '>=10.0.0'}
|
||||||
@@ -2638,10 +2785,19 @@ packages:
|
|||||||
xmlchars@2.2.0:
|
xmlchars@2.2.0:
|
||||||
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
|
resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
|
||||||
|
|
||||||
|
y18n@4.0.3:
|
||||||
|
resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
|
||||||
|
|
||||||
yaml@1.10.2:
|
yaml@1.10.2:
|
||||||
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
|
resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
|
|
||||||
|
yargs-parser@13.1.2:
|
||||||
|
resolution: {integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==}
|
||||||
|
|
||||||
|
yargs@13.3.2:
|
||||||
|
resolution: {integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw==}
|
||||||
|
|
||||||
yn@3.1.1:
|
yn@3.1.1:
|
||||||
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
|
resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -3564,10 +3720,21 @@ snapshots:
|
|||||||
|
|
||||||
ansi-colors@4.1.3: {}
|
ansi-colors@4.1.3: {}
|
||||||
|
|
||||||
|
ansi-regex@4.1.1: {}
|
||||||
|
|
||||||
|
ansi-styles@3.2.1:
|
||||||
|
dependencies:
|
||||||
|
color-convert: 1.9.3
|
||||||
|
|
||||||
ansi-styles@4.3.0:
|
ansi-styles@4.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-convert: 2.0.1
|
color-convert: 2.0.1
|
||||||
|
|
||||||
|
anymatch@3.1.3:
|
||||||
|
dependencies:
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
picomatch: 2.3.1
|
||||||
|
|
||||||
arg@4.1.3:
|
arg@4.1.3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -3588,6 +3755,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
require-from-string: 2.0.2
|
require-from-string: 2.0.2
|
||||||
|
|
||||||
|
binary-extensions@2.3.0: {}
|
||||||
|
|
||||||
boolbase@1.0.0: {}
|
boolbase@1.0.0: {}
|
||||||
|
|
||||||
brace-expansion@1.1.12:
|
brace-expansion@1.1.12:
|
||||||
@@ -3599,6 +3768,10 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
|
|
||||||
|
braces@3.0.3:
|
||||||
|
dependencies:
|
||||||
|
fill-range: 7.1.1
|
||||||
|
|
||||||
buffer-from@1.1.2:
|
buffer-from@1.1.2:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -3629,6 +3802,8 @@ snapshots:
|
|||||||
|
|
||||||
callsites@3.1.0: {}
|
callsites@3.1.0: {}
|
||||||
|
|
||||||
|
camelcase@5.3.1: {}
|
||||||
|
|
||||||
camera-controls@3.1.2(three@0.182.0):
|
camera-controls@3.1.2(three@0.182.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
three: 0.182.0
|
three: 0.182.0
|
||||||
@@ -3640,6 +3815,25 @@ snapshots:
|
|||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
supports-color: 7.2.0
|
supports-color: 7.2.0
|
||||||
|
|
||||||
|
chokidar-cli@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
chokidar: 3.6.0
|
||||||
|
lodash.debounce: 4.0.8
|
||||||
|
lodash.throttle: 4.1.1
|
||||||
|
yargs: 13.3.2
|
||||||
|
|
||||||
|
chokidar@3.6.0:
|
||||||
|
dependencies:
|
||||||
|
anymatch: 3.1.3
|
||||||
|
braces: 3.0.3
|
||||||
|
glob-parent: 5.1.2
|
||||||
|
is-binary-path: 2.1.0
|
||||||
|
is-glob: 4.0.3
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
readdirp: 3.6.0
|
||||||
|
optionalDependencies:
|
||||||
|
fsevents: 2.3.3
|
||||||
|
|
||||||
chokidar@4.0.3:
|
chokidar@4.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
readdirp: 4.1.2
|
readdirp: 4.1.2
|
||||||
@@ -3652,12 +3846,24 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
consola: 3.4.2
|
consola: 3.4.2
|
||||||
|
|
||||||
|
cliui@5.0.0:
|
||||||
|
dependencies:
|
||||||
|
string-width: 3.1.0
|
||||||
|
strip-ansi: 5.2.0
|
||||||
|
wrap-ansi: 5.1.0
|
||||||
|
|
||||||
clsx@2.1.1: {}
|
clsx@2.1.1: {}
|
||||||
|
|
||||||
|
color-convert@1.9.3:
|
||||||
|
dependencies:
|
||||||
|
color-name: 1.1.3
|
||||||
|
|
||||||
color-convert@2.0.1:
|
color-convert@2.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
color-name: 1.1.4
|
color-name: 1.1.4
|
||||||
|
|
||||||
|
color-name@1.1.3: {}
|
||||||
|
|
||||||
color-name@1.1.4: {}
|
color-name@1.1.4: {}
|
||||||
|
|
||||||
color-support@1.1.3: {}
|
color-support@1.1.3: {}
|
||||||
@@ -3742,6 +3948,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.3
|
ms: 2.1.3
|
||||||
|
|
||||||
|
decamelize@1.2.0: {}
|
||||||
|
|
||||||
decimal.js@10.6.0:
|
decimal.js@10.6.0:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -3805,6 +4013,8 @@ snapshots:
|
|||||||
|
|
||||||
earcut@2.2.4: {}
|
earcut@2.2.4: {}
|
||||||
|
|
||||||
|
emoji-regex@7.0.3: {}
|
||||||
|
|
||||||
enhanced-resolve@5.18.4:
|
enhanced-resolve@5.18.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
@@ -4021,6 +4231,14 @@ snapshots:
|
|||||||
|
|
||||||
file-saver@2.0.5: {}
|
file-saver@2.0.5: {}
|
||||||
|
|
||||||
|
fill-range@7.1.1:
|
||||||
|
dependencies:
|
||||||
|
to-regex-range: 5.0.1
|
||||||
|
|
||||||
|
find-up@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
locate-path: 3.0.0
|
||||||
|
|
||||||
find-up@5.0.0:
|
find-up@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
locate-path: 6.0.0
|
locate-path: 6.0.0
|
||||||
@@ -4048,6 +4266,8 @@ snapshots:
|
|||||||
function-bind@1.1.2:
|
function-bind@1.1.2:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
get-caller-file@2.0.5: {}
|
||||||
|
|
||||||
get-intrinsic@1.3.0:
|
get-intrinsic@1.3.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind-apply-helpers: 1.0.2
|
call-bind-apply-helpers: 1.0.2
|
||||||
@@ -4082,6 +4302,10 @@ snapshots:
|
|||||||
nypm: 0.6.2
|
nypm: 0.6.2
|
||||||
pathe: 2.0.3
|
pathe: 2.0.3
|
||||||
|
|
||||||
|
glob-parent@5.1.2:
|
||||||
|
dependencies:
|
||||||
|
is-glob: 4.0.3
|
||||||
|
|
||||||
glob-parent@6.0.2:
|
glob-parent@6.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
@@ -4155,10 +4379,16 @@ snapshots:
|
|||||||
|
|
||||||
imurmurhash@0.1.4: {}
|
imurmurhash@0.1.4: {}
|
||||||
|
|
||||||
|
is-binary-path@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
binary-extensions: 2.3.0
|
||||||
|
|
||||||
is-docker@3.0.0: {}
|
is-docker@3.0.0: {}
|
||||||
|
|
||||||
is-extglob@2.1.1: {}
|
is-extglob@2.1.1: {}
|
||||||
|
|
||||||
|
is-fullwidth-code-point@2.0.0: {}
|
||||||
|
|
||||||
is-glob@4.0.3:
|
is-glob@4.0.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
is-extglob: 2.1.1
|
is-extglob: 2.1.1
|
||||||
@@ -4169,6 +4399,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
is-docker: 3.0.0
|
is-docker: 3.0.0
|
||||||
|
|
||||||
|
is-number@7.0.0: {}
|
||||||
|
|
||||||
is-potential-custom-element-name@1.0.1:
|
is-potential-custom-element-name@1.0.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -4313,12 +4545,21 @@ snapshots:
|
|||||||
|
|
||||||
locate-character@3.0.0: {}
|
locate-character@3.0.0: {}
|
||||||
|
|
||||||
|
locate-path@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
p-locate: 3.0.0
|
||||||
|
path-exists: 3.0.0
|
||||||
|
|
||||||
locate-path@6.0.0:
|
locate-path@6.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
p-locate: 5.0.0
|
p-locate: 5.0.0
|
||||||
|
|
||||||
|
lodash.debounce@4.0.8: {}
|
||||||
|
|
||||||
lodash.merge@4.6.2: {}
|
lodash.merge@4.6.2: {}
|
||||||
|
|
||||||
|
lodash.throttle@4.1.1: {}
|
||||||
|
|
||||||
lodash@4.17.21: {}
|
lodash@4.17.21: {}
|
||||||
|
|
||||||
lru-cache@10.4.3:
|
lru-cache@10.4.3:
|
||||||
@@ -4406,6 +4647,8 @@ snapshots:
|
|||||||
|
|
||||||
node-fetch-native@1.6.7: {}
|
node-fetch-native@1.6.7: {}
|
||||||
|
|
||||||
|
normalize-path@3.0.0: {}
|
||||||
|
|
||||||
nth-check@2.1.1:
|
nth-check@2.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
boolbase: 1.0.0
|
boolbase: 1.0.0
|
||||||
@@ -4443,14 +4686,24 @@ snapshots:
|
|||||||
type-check: 0.4.0
|
type-check: 0.4.0
|
||||||
word-wrap: 1.2.5
|
word-wrap: 1.2.5
|
||||||
|
|
||||||
|
p-limit@2.3.0:
|
||||||
|
dependencies:
|
||||||
|
p-try: 2.2.0
|
||||||
|
|
||||||
p-limit@3.1.0:
|
p-limit@3.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
yocto-queue: 0.1.0
|
yocto-queue: 0.1.0
|
||||||
|
|
||||||
|
p-locate@3.0.0:
|
||||||
|
dependencies:
|
||||||
|
p-limit: 2.3.0
|
||||||
|
|
||||||
p-locate@5.0.0:
|
p-locate@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
p-limit: 3.1.0
|
p-limit: 3.1.0
|
||||||
|
|
||||||
|
p-try@2.2.0: {}
|
||||||
|
|
||||||
package-manager-detector@1.6.0: {}
|
package-manager-detector@1.6.0: {}
|
||||||
|
|
||||||
parent-module@1.0.1:
|
parent-module@1.0.1:
|
||||||
@@ -4465,6 +4718,8 @@ snapshots:
|
|||||||
entities: 6.0.1
|
entities: 6.0.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
path-exists@3.0.0: {}
|
||||||
|
|
||||||
path-exists@4.0.0: {}
|
path-exists@4.0.0: {}
|
||||||
|
|
||||||
path-key@3.1.1: {}
|
path-key@3.1.1: {}
|
||||||
@@ -4475,6 +4730,8 @@ snapshots:
|
|||||||
|
|
||||||
picocolors@1.1.1: {}
|
picocolors@1.1.1: {}
|
||||||
|
|
||||||
|
picomatch@2.3.1: {}
|
||||||
|
|
||||||
picomatch@4.0.3: {}
|
picomatch@4.0.3: {}
|
||||||
|
|
||||||
pify@4.0.1:
|
pify@4.0.1:
|
||||||
@@ -4540,12 +4797,20 @@ snapshots:
|
|||||||
defu: 6.1.4
|
defu: 6.1.4
|
||||||
destr: 2.0.5
|
destr: 2.0.5
|
||||||
|
|
||||||
|
readdirp@3.6.0:
|
||||||
|
dependencies:
|
||||||
|
picomatch: 2.3.1
|
||||||
|
|
||||||
readdirp@4.1.2: {}
|
readdirp@4.1.2: {}
|
||||||
|
|
||||||
readdirp@5.0.0: {}
|
readdirp@5.0.0: {}
|
||||||
|
|
||||||
|
require-directory@2.1.1: {}
|
||||||
|
|
||||||
require-from-string@2.0.2: {}
|
require-from-string@2.0.2: {}
|
||||||
|
|
||||||
|
require-main-filename@2.0.0: {}
|
||||||
|
|
||||||
resolve-from@4.0.0: {}
|
resolve-from@4.0.0: {}
|
||||||
|
|
||||||
resolve-pkg-maps@1.0.0:
|
resolve-pkg-maps@1.0.0:
|
||||||
@@ -4620,6 +4885,8 @@ snapshots:
|
|||||||
|
|
||||||
semver@7.7.3: {}
|
semver@7.7.3: {}
|
||||||
|
|
||||||
|
set-blocking@2.0.0: {}
|
||||||
|
|
||||||
set-cookie-parser@2.7.2: {}
|
set-cookie-parser@2.7.2: {}
|
||||||
|
|
||||||
shebang-command@2.0.0:
|
shebang-command@2.0.0:
|
||||||
@@ -4653,6 +4920,16 @@ snapshots:
|
|||||||
|
|
||||||
std-env@3.10.0: {}
|
std-env@3.10.0: {}
|
||||||
|
|
||||||
|
string-width@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
emoji-regex: 7.0.3
|
||||||
|
is-fullwidth-code-point: 2.0.0
|
||||||
|
strip-ansi: 5.2.0
|
||||||
|
|
||||||
|
strip-ansi@5.2.0:
|
||||||
|
dependencies:
|
||||||
|
ansi-regex: 4.1.1
|
||||||
|
|
||||||
strip-json-comments@3.1.1: {}
|
strip-json-comments@3.1.1: {}
|
||||||
|
|
||||||
supports-color@7.2.0:
|
supports-color@7.2.0:
|
||||||
@@ -4772,6 +5049,10 @@ snapshots:
|
|||||||
tldts-core: 6.1.86
|
tldts-core: 6.1.86
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
to-regex-range@5.0.1:
|
||||||
|
dependencies:
|
||||||
|
is-number: 7.0.0
|
||||||
|
|
||||||
totalist@3.0.1: {}
|
totalist@3.0.1: {}
|
||||||
|
|
||||||
tough-cookie@5.1.2:
|
tough-cookie@5.1.2:
|
||||||
@@ -4958,6 +5239,8 @@ snapshots:
|
|||||||
webidl-conversions: 7.0.0
|
webidl-conversions: 7.0.0
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
which-module@2.0.1: {}
|
||||||
|
|
||||||
which@2.0.2:
|
which@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
@@ -4969,6 +5252,12 @@ snapshots:
|
|||||||
|
|
||||||
word-wrap@1.2.5: {}
|
word-wrap@1.2.5: {}
|
||||||
|
|
||||||
|
wrap-ansi@5.1.0:
|
||||||
|
dependencies:
|
||||||
|
ansi-styles: 3.2.1
|
||||||
|
string-width: 3.1.0
|
||||||
|
strip-ansi: 5.2.0
|
||||||
|
|
||||||
ws@8.19.0:
|
ws@8.19.0:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
@@ -4983,8 +5272,28 @@ snapshots:
|
|||||||
xmlchars@2.2.0:
|
xmlchars@2.2.0:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
y18n@4.0.3: {}
|
||||||
|
|
||||||
yaml@1.10.2: {}
|
yaml@1.10.2: {}
|
||||||
|
|
||||||
|
yargs-parser@13.1.2:
|
||||||
|
dependencies:
|
||||||
|
camelcase: 5.3.1
|
||||||
|
decamelize: 1.2.0
|
||||||
|
|
||||||
|
yargs@13.3.2:
|
||||||
|
dependencies:
|
||||||
|
cliui: 5.0.0
|
||||||
|
find-up: 3.0.0
|
||||||
|
get-caller-file: 2.0.5
|
||||||
|
require-directory: 2.1.1
|
||||||
|
require-main-filename: 2.0.0
|
||||||
|
set-blocking: 2.0.0
|
||||||
|
string-width: 3.1.0
|
||||||
|
which-module: 2.0.1
|
||||||
|
y18n: 4.0.3
|
||||||
|
yargs-parser: 13.1.2
|
||||||
|
|
||||||
yn@3.1.1:
|
yn@3.1.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user