chore: make eslint happy
📊 Benchmark the Runtime / benchmark (pull_request) Successful in 1m6s
🚀 Lint & Test & Deploy / quality (pull_request) Failing after 1m7s
🚀 Lint & Test & Deploy / test-unit (pull_request) Successful in 31s
🚀 Lint & Test & Deploy / test-e2e (pull_request) Failing after 32s
🚀 Lint & Test & Deploy / deploy (pull_request) Has been skipped
📊 Benchmark the Runtime / benchmark (pull_request) Successful in 1m6s
🚀 Lint & Test & Deploy / quality (pull_request) Failing after 1m7s
🚀 Lint & Test & Deploy / test-unit (pull_request) Successful in 31s
🚀 Lint & Test & Deploy / test-e2e (pull_request) Failing after 32s
🚀 Lint & Test & Deploy / deploy (pull_request) Has been skipped
This commit is contained in:
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"predev": "rm static/CHANGELOG.md && ln -s ../../CHANGELOG.md static/CHANGELOG.md",
|
"predev": "rm static/CHANGELOG.md && ln -s ../../CHANGELOG.md static/CHANGELOG.md",
|
||||||
"build": "svelte-kit sync && vite build",
|
"build": "svelte-kit sync && vite build",
|
||||||
"test:unit": "vitest",
|
"test:unit": "vitest --browser=false",
|
||||||
"test": "npm run test:unit -- --run && npm run test:e2e",
|
"test": "npm run test:unit -- --run && npm run test:e2e",
|
||||||
"test:e2e": "playwright test",
|
"test:e2e": "playwright test",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
|
|||||||
@@ -19,12 +19,12 @@ import EventEmitter from './helpers/EventEmitter';
|
|||||||
import { HistoryManager } from './history-manager';
|
import { HistoryManager } from './history-manager';
|
||||||
|
|
||||||
const logger = createLogger('graph-manager');
|
const logger = createLogger('graph-manager');
|
||||||
// logger.mute();
|
logger.mute();
|
||||||
|
|
||||||
const remoteRegistry = new RemoteNodeRegistry('');
|
const remoteRegistry = new RemoteNodeRegistry('');
|
||||||
|
|
||||||
const clone = 'structuredClone' in self
|
const clone = 'structuredClone' in globalThis
|
||||||
? self.structuredClone
|
? globalThis.structuredClone
|
||||||
: (args: unknown) => JSON.parse(JSON.stringify(args));
|
: (args: unknown) => JSON.parse(JSON.stringify(args));
|
||||||
|
|
||||||
function areSocketsCompatible(
|
function areSocketsCompatible(
|
||||||
@@ -104,7 +104,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
|
|
||||||
history: HistoryManager = new HistoryManager();
|
history: HistoryManager = new HistoryManager();
|
||||||
|
|
||||||
private serializeFullGraph(): Graph {
|
public serializeFullGraph(): Graph {
|
||||||
if (this.graphStack.length === 0) return this.serialize();
|
if (this.graphStack.length === 0) return this.serialize();
|
||||||
let merged = this.serialize();
|
let merged = this.serialize();
|
||||||
for (let i = this.graphStack.length - 1; i >= 0; i--) {
|
for (let i = this.graphStack.length - 1; i >= 0; i--) {
|
||||||
@@ -538,7 +538,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
const inputs = {
|
const inputs = {
|
||||||
'groupId': {
|
'groupId': {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
label: '',
|
label: 'Group',
|
||||||
value: node.props?.groupId,
|
value: node.props?.groupId,
|
||||||
internal: true,
|
internal: true,
|
||||||
options: this.graph.groups.map((g, i) => ({
|
options: this.graph.groups.map((g, i) => ({
|
||||||
@@ -551,6 +551,10 @@ export class GraphManager extends EventEmitter<{
|
|||||||
|
|
||||||
const groupType = {
|
const groupType = {
|
||||||
...node.state.type,
|
...node.state.type,
|
||||||
|
meta: {
|
||||||
|
title: 'Group',
|
||||||
|
...node?.state?.type?.meta || {}
|
||||||
|
},
|
||||||
inputs,
|
inputs,
|
||||||
outputs: groupDefinition?.outputs?.map(o => o.type)
|
outputs: groupDefinition?.outputs?.map(o => o.type)
|
||||||
} as NodeDefinition;
|
} as NodeDefinition;
|
||||||
@@ -620,7 +624,6 @@ export class GraphManager extends EventEmitter<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeNode(node: NodeInstance, { restoreEdges = false } = {}) {
|
removeNode(node: NodeInstance, { restoreEdges = false } = {}) {
|
||||||
console.log('REMOVING NODE', $state.snapshot({ node }));
|
|
||||||
const edgesToNode = this.edges.filter((edge) => edge[2].id === node.id);
|
const edgesToNode = this.edges.filter((edge) => edge[2].id === node.id);
|
||||||
const edgesFromNode = this.edges.filter((edge) => edge[0].id === node.id);
|
const edgesFromNode = this.edges.filter((edge) => edge[0].id === node.id);
|
||||||
for (const edge of [...edgesToNode, ...edgesFromNode]) {
|
for (const edge of [...edgesToNode, ...edgesFromNode]) {
|
||||||
@@ -686,7 +689,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
|
|
||||||
this.graphStack.push({
|
this.graphStack.push({
|
||||||
rootGraph: this.serialize(),
|
rootGraph: this.serialize(),
|
||||||
savedNodes: new Map(this.nodes),
|
savedNodes: new SvelteMap(this.nodes),
|
||||||
savedEdges: [...this.edges],
|
savedEdges: [...this.edges],
|
||||||
outerGraph: this.graph,
|
outerGraph: this.graph,
|
||||||
groupId,
|
groupId,
|
||||||
@@ -743,8 +746,6 @@ export class GraphManager extends EventEmitter<{
|
|||||||
...this.graph.groups.flatMap(g => g.nodes.map(n => n.id))
|
...this.graph.groups.flatMap(g => g.nodes.map(n => n.id))
|
||||||
];
|
];
|
||||||
|
|
||||||
console.log('CREATE NODE ID', ids);
|
|
||||||
|
|
||||||
let id = 0;
|
let id = 0;
|
||||||
while (ids.includes(id)) {
|
while (ids.includes(id)) {
|
||||||
id++;
|
id++;
|
||||||
@@ -796,7 +797,7 @@ export class GraphManager extends EventEmitter<{
|
|||||||
}
|
}
|
||||||
|
|
||||||
removeUnusedGroups() {
|
removeUnusedGroups() {
|
||||||
const usedGroups = new Set(this.getAllNodes().map(n => n.props?.groupId));
|
const usedGroups = new SvelteSet(this.getAllNodes().map(n => n.props?.groupId));
|
||||||
const unusedGroupAmount = this.graph.groups.length - usedGroups.size;
|
const unusedGroupAmount = this.graph.groups.length - usedGroups.size;
|
||||||
this.graph.groups = this.graph.groups.filter(g => usedGroups.has(g.id));
|
this.graph.groups = this.graph.groups.filter(g => usedGroups.has(g.id));
|
||||||
this.save();
|
this.save();
|
||||||
@@ -808,14 +809,14 @@ export class GraphManager extends EventEmitter<{
|
|||||||
this.removeUnusedGroups();
|
this.removeUnusedGroups();
|
||||||
|
|
||||||
const nodes = [
|
const nodes = [
|
||||||
...new Set(nodeIds).values().map(id => this.getNode(id)).filter(Boolean)
|
...new SvelteSet(nodeIds).values().map(id => this.getNode(id)).filter(Boolean)
|
||||||
] as NodeInstance[];
|
] as NodeInstance[];
|
||||||
|
|
||||||
if (!nodes.length) return;
|
if (!nodes.length) return;
|
||||||
|
|
||||||
logger.log(`Grouping ${nodes.length} nodes`, { nodes });
|
logger.log(`Grouping ${nodes.length} nodes`, { nodes });
|
||||||
|
|
||||||
const ids = new Set(nodes.map(n => n.id));
|
const ids = new SvelteSet(nodes.map(n => n.id));
|
||||||
|
|
||||||
// We use the map to dedupe when one external node is connected to multiple internal nodes
|
// We use the map to dedupe when one external node is connected to multiple internal nodes
|
||||||
// ┌──internal_a
|
// ┌──internal_a
|
||||||
@@ -823,14 +824,14 @@ export class GraphManager extends EventEmitter<{
|
|||||||
// └──internal_b
|
// └──internal_b
|
||||||
// This should only result in one group input not two
|
// This should only result in one group input not two
|
||||||
const incomingEdges = this.edges.filter((edge) => ids.has(edge[2].id) && !ids.has(edge[0].id));
|
const incomingEdges = this.edges.filter((edge) => ids.has(edge[2].id) && !ids.has(edge[0].id));
|
||||||
const groupInputs = new Map<string, Edge>();
|
const groupInputs = new SvelteMap<string, Edge>();
|
||||||
for (const edge of incomingEdges) {
|
for (const edge of incomingEdges) {
|
||||||
groupInputs.set(`${edge[0].id}-${edge[1]}`, edge);
|
groupInputs.set(`${edge[0].id}-${edge[1]}`, edge);
|
||||||
}
|
}
|
||||||
|
|
||||||
// And the same for the outputs
|
// And the same for the outputs
|
||||||
const outgoingEdges = this.edges.filter((edge) => ids.has(edge[0].id) && !ids.has(edge[2].id));
|
const outgoingEdges = this.edges.filter((edge) => ids.has(edge[0].id) && !ids.has(edge[2].id));
|
||||||
const groupOutputs = new Map<string, Edge>();
|
const groupOutputs = new SvelteMap<string, Edge>();
|
||||||
for (const edge of outgoingEdges) {
|
for (const edge of outgoingEdges) {
|
||||||
groupOutputs.set(`${edge[2].id}-${edge[3]}`, edge);
|
groupOutputs.set(`${edge[2].id}-${edge[3]}`, edge);
|
||||||
}
|
}
|
||||||
@@ -864,11 +865,11 @@ export class GraphManager extends EventEmitter<{
|
|||||||
|
|
||||||
// Map from deduped edge source key → group input index, used for both
|
// Map from deduped edge source key → group input index, used for both
|
||||||
// internal edge wiring and external edge socket naming.
|
// internal edge wiring and external edge socket naming.
|
||||||
const inputIndexByEdgeKey = new Map<string, number>();
|
const inputIndexByEdgeKey = new SvelteMap<string, number>();
|
||||||
[...groupInputs.keys()].forEach((key, i) => inputIndexByEdgeKey.set(key, i));
|
[...groupInputs.keys()].forEach((key, i) => inputIndexByEdgeKey.set(key, i));
|
||||||
|
|
||||||
// Allocate all needed IDs up front so sequential calls never collide.
|
// Allocate all needed IDs up front so sequential calls never collide.
|
||||||
const usedIds = new Set<number>([
|
const usedIds = new SvelteSet<number>([
|
||||||
...this.nodes.keys(),
|
...this.nodes.keys(),
|
||||||
...this.graph.groups.map(g => g.id),
|
...this.graph.groups.map(g => g.id),
|
||||||
...this.graph.groups.flatMap(g => g.nodes.map(n => n.id))
|
...this.graph.groups.flatMap(g => g.nodes.map(n => n.id))
|
||||||
@@ -949,7 +950,6 @@ export class GraphManager extends EventEmitter<{
|
|||||||
this.removeNode(node);
|
this.removeNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('FINISHED', this.serialize());
|
|
||||||
this.saveUndoGroup();
|
this.saveUndoGroup();
|
||||||
|
|
||||||
return groupNode;
|
return groupNode;
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
import { assert, beforeEach, describe, expect, it } from 'vitest';
|
import { assert, describe, expect, it } from 'vitest';
|
||||||
import { GraphManager } from './graph-manager.svelte';
|
import { GraphManager } from './graph-manager.svelte';
|
||||||
import { GraphState } from './graph-state.svelte';
|
import { GraphState } from './graph-state.svelte';
|
||||||
import {
|
import { createMockNodeRegistry, mockFloatInputNode, mockFloatOutputNode } from './test-utils';
|
||||||
createMockNodeRegistry,
|
|
||||||
mockFloatInputNode,
|
|
||||||
mockFloatOutputNode
|
|
||||||
} from './test-utils';
|
|
||||||
|
|
||||||
// GraphState constructor reads localStorage synchronously — mock before any instantiation
|
// GraphState constructor reads localStorage synchronously — mock before any instantiation
|
||||||
Object.defineProperty(globalThis, 'localStorage', {
|
Object.defineProperty(globalThis, 'localStorage', {
|
||||||
|
|||||||
@@ -136,6 +136,12 @@
|
|||||||
/>
|
/>
|
||||||
<label for="drop-zone"></label>
|
<label for="drop-zone"></label>
|
||||||
|
|
||||||
|
{#if graph.isInsideGroup}
|
||||||
|
<button class="exit-group" onclick={() => graphState.exitGroupNode()}>
|
||||||
|
↑ Exit Group
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
|
||||||
<Canvas shadows={false} renderMode="on-demand" colorManagementEnabled={false}>
|
<Canvas shadows={false} renderMode="on-demand" colorManagementEnabled={false}>
|
||||||
<Camera
|
<Camera
|
||||||
bind:camera={graphState.camera}
|
bind:camera={graphState.camera}
|
||||||
@@ -169,14 +175,6 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if graph.status === 'idle'}
|
{#if graph.status === 'idle'}
|
||||||
{#if graph.isInsideGroup}
|
|
||||||
<HTML transform={false}>
|
|
||||||
<button class="exit-group" onclick={() => graphState.exitGroupNode()}>
|
|
||||||
↑ Exit Group
|
|
||||||
</button>
|
|
||||||
</HTML>
|
|
||||||
{/if}
|
|
||||||
|
|
||||||
{#if graphState.addMenuPosition}
|
{#if graphState.addMenuPosition}
|
||||||
<AddMenu
|
<AddMenu
|
||||||
onnode={handleNodeCreation}
|
onnode={handleNodeCreation}
|
||||||
@@ -252,11 +250,10 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.exit-group) {
|
.exit-group {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
top: 12px;
|
top: 12px;
|
||||||
left: 50%;
|
left: 12px;
|
||||||
transform: translateX(-50%);
|
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
padding: 4px 12px;
|
padding: 4px 12px;
|
||||||
background: var(--color-layer-2);
|
background: var(--color-layer-2);
|
||||||
@@ -268,7 +265,7 @@
|
|||||||
opacity: 0.85;
|
opacity: 0.85;
|
||||||
}
|
}
|
||||||
|
|
||||||
:global(.exit-group:hover) {
|
.exit-group:hover {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { createKeyMap } from '$lib/helpers/createKeyMap';
|
import { createKeyMap } from '$lib/helpers/createKeyMap';
|
||||||
import type { Graph, NodeInstance, NodeRegistry } from '@nodarium/types';
|
import type { Graph, NodeInstance, NodeRegistry } from '@nodarium/types';
|
||||||
import { onMount } from 'svelte';
|
|
||||||
import { GraphManager } from '../graph-manager.svelte';
|
import { GraphManager } from '../graph-manager.svelte';
|
||||||
import { GraphState, setGraphManager, setGraphState } from '../graph-state.svelte';
|
import { GraphState, setGraphManager, setGraphState } from '../graph-state.svelte';
|
||||||
import { setupKeymaps } from '../keymaps';
|
import { setupKeymaps } from '../keymaps';
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { NodeDefinition, NodeInstance } from '@nodarium/types';
|
import type { NodeDefinition } from '@nodarium/types';
|
||||||
|
|
||||||
export function getParameterHeight(node: NodeDefinition, inputKey: string) {
|
export function getParameterHeight(node: NodeDefinition, inputKey: string) {
|
||||||
const input = node.inputs?.[inputKey];
|
const input = node.inputs?.[inputKey];
|
||||||
|
|||||||
@@ -71,7 +71,7 @@
|
|||||||
{#if appSettings.value.debug.advancedMode}
|
{#if appSettings.value.debug.advancedMode}
|
||||||
<span class="bg-white text-black! mr-2 px-1 rounded-sm opacity-30">{node.id}</span>
|
<span class="bg-white text-black! mr-2 px-1 rounded-sm opacity-30">{node.id}</span>
|
||||||
{/if}
|
{/if}
|
||||||
{node.type.split('/').pop()}
|
{nodeType?.meta?.title || node.type?.split('/').pop()}
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="target"
|
class="target"
|
||||||
|
|||||||
@@ -6,11 +6,22 @@ import type {
|
|||||||
RuntimeExecutor,
|
RuntimeExecutor,
|
||||||
SyncCache
|
SyncCache
|
||||||
} from '@nodarium/types';
|
} from '@nodarium/types';
|
||||||
|
import {
|
||||||
|
concatEncodedArrays,
|
||||||
|
createLogger,
|
||||||
|
encodeFloat,
|
||||||
|
fastHashArrayBuffer,
|
||||||
|
type PerformanceStore
|
||||||
|
} from '@nodarium/utils';
|
||||||
|
import type { RuntimeNode } from './types';
|
||||||
|
|
||||||
|
const log = createLogger('runtime-executor');
|
||||||
|
log.mute();
|
||||||
|
|
||||||
export function expandGroups(graph: Graph): Graph {
|
export function expandGroups(graph: Graph): Graph {
|
||||||
if (!graph.groups || graph.groups.length === 0) return graph;
|
if (!graph.groups || graph.groups.length === 0) return graph;
|
||||||
|
|
||||||
let nodes = [...graph.nodes];
|
const nodes = [...graph.nodes];
|
||||||
let edges = [...graph.edges];
|
let edges = [...graph.edges];
|
||||||
|
|
||||||
let changed = true;
|
let changed = true;
|
||||||
@@ -87,17 +98,6 @@ export function expandGroups(graph: Graph): Graph {
|
|||||||
|
|
||||||
return { ...graph, nodes, edges };
|
return { ...graph, nodes, edges };
|
||||||
}
|
}
|
||||||
import {
|
|
||||||
concatEncodedArrays,
|
|
||||||
createLogger,
|
|
||||||
encodeFloat,
|
|
||||||
fastHashArrayBuffer,
|
|
||||||
type PerformanceStore
|
|
||||||
} from '@nodarium/utils';
|
|
||||||
import type { RuntimeNode } from './types';
|
|
||||||
|
|
||||||
const log = createLogger('runtime-executor');
|
|
||||||
log.mute();
|
|
||||||
|
|
||||||
function getValue(input: NodeInput, value?: unknown) {
|
function getValue(input: NodeInput, value?: unknown) {
|
||||||
if (value === undefined && 'value' in input) {
|
if (value === undefined && 'value' in input) {
|
||||||
@@ -160,7 +160,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
|
|||||||
const nonVirtualTypes = graph.nodes
|
const nonVirtualTypes = graph.nodes
|
||||||
.map(node => node.type)
|
.map(node => node.type)
|
||||||
.filter(t => !t.startsWith('__internal/'));
|
.filter(t => !t.startsWith('__internal/'));
|
||||||
await this.registry.load(nonVirtualTypes as any);
|
await this.registry.load(nonVirtualTypes);
|
||||||
|
|
||||||
const typeMap = new Map<string, NodeDefinition>();
|
const typeMap = new Map<string, NodeDefinition>();
|
||||||
for (const node of graph.nodes) {
|
for (const node of graph.nodes) {
|
||||||
|
|||||||
@@ -15,10 +15,7 @@
|
|||||||
isGroupInstance ? manager.getGroup(node!.props?.groupId as number) : undefined
|
isGroupInstance ? manager.getGroup(node!.props?.groupId as number) : undefined
|
||||||
);
|
);
|
||||||
|
|
||||||
let groupName = $state('');
|
const groupName = $derived(activeGroup?.name ?? '');
|
||||||
$effect(() => {
|
|
||||||
groupName = activeGroup?.name ?? '';
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleRename(e: Event) {
|
function handleRename(e: Event) {
|
||||||
const name = (e.target as HTMLInputElement).value;
|
const name = (e.target as HTMLInputElement).value;
|
||||||
|
|||||||
@@ -321,7 +321,7 @@
|
|||||||
hidden={!appSettings.value.debug.advancedMode}
|
hidden={!appSettings.value.debug.advancedMode}
|
||||||
icon="i-[tabler--code]"
|
icon="i-[tabler--code]"
|
||||||
>
|
>
|
||||||
<GraphSource graph={manager?.serialize()} />
|
<GraphSource graph={manager?.serializeFullGraph()} />
|
||||||
</Panel>
|
</Panel>
|
||||||
<Panel
|
<Panel
|
||||||
id="benchmark"
|
id="benchmark"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script module>
|
<script module>
|
||||||
const cache = new Map<string, Record<string, boolean>>();
|
import { SvelteMap } from 'svelte/reactivity';
|
||||||
|
const cache = new SvelteMap<string, Record<string, boolean>>();
|
||||||
|
|
||||||
function getStore(root: string): Record<string, boolean> {
|
function getStore(root: string): Record<string, boolean> {
|
||||||
if (!cache.has(root)) {
|
if (!cache.has(root)) {
|
||||||
|
|||||||
Reference in New Issue
Block a user