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

This commit is contained in:
2026-05-04 14:50:11 +02:00
parent 2a54fa7590
commit e695c76490
11 changed files with 49 additions and 59 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
"dev": "vite dev",
"predev": "rm static/CHANGELOG.md && ln -s ../../CHANGELOG.md static/CHANGELOG.md",
"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:e2e": "playwright test",
"preview": "vite preview",
@@ -19,12 +19,12 @@ import EventEmitter from './helpers/EventEmitter';
import { HistoryManager } from './history-manager';
const logger = createLogger('graph-manager');
// logger.mute();
logger.mute();
const remoteRegistry = new RemoteNodeRegistry('');
const clone = 'structuredClone' in self
? self.structuredClone
const clone = 'structuredClone' in globalThis
? globalThis.structuredClone
: (args: unknown) => JSON.parse(JSON.stringify(args));
function areSocketsCompatible(
@@ -104,7 +104,7 @@ export class GraphManager extends EventEmitter<{
history: HistoryManager = new HistoryManager();
private serializeFullGraph(): Graph {
public serializeFullGraph(): Graph {
if (this.graphStack.length === 0) return this.serialize();
let merged = this.serialize();
for (let i = this.graphStack.length - 1; i >= 0; i--) {
@@ -538,7 +538,7 @@ export class GraphManager extends EventEmitter<{
const inputs = {
'groupId': {
type: 'select',
label: '',
label: 'Group',
value: node.props?.groupId,
internal: true,
options: this.graph.groups.map((g, i) => ({
@@ -551,6 +551,10 @@ export class GraphManager extends EventEmitter<{
const groupType = {
...node.state.type,
meta: {
title: 'Group',
...node?.state?.type?.meta || {}
},
inputs,
outputs: groupDefinition?.outputs?.map(o => o.type)
} as NodeDefinition;
@@ -620,7 +624,6 @@ export class GraphManager extends EventEmitter<{
}
removeNode(node: NodeInstance, { restoreEdges = false } = {}) {
console.log('REMOVING NODE', $state.snapshot({ node }));
const edgesToNode = this.edges.filter((edge) => edge[2].id === node.id);
const edgesFromNode = this.edges.filter((edge) => edge[0].id === node.id);
for (const edge of [...edgesToNode, ...edgesFromNode]) {
@@ -686,7 +689,7 @@ export class GraphManager extends EventEmitter<{
this.graphStack.push({
rootGraph: this.serialize(),
savedNodes: new Map(this.nodes),
savedNodes: new SvelteMap(this.nodes),
savedEdges: [...this.edges],
outerGraph: this.graph,
groupId,
@@ -743,8 +746,6 @@ export class GraphManager extends EventEmitter<{
...this.graph.groups.flatMap(g => g.nodes.map(n => n.id))
];
console.log('CREATE NODE ID', ids);
let id = 0;
while (ids.includes(id)) {
id++;
@@ -796,7 +797,7 @@ export class GraphManager extends EventEmitter<{
}
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;
this.graph.groups = this.graph.groups.filter(g => usedGroups.has(g.id));
this.save();
@@ -808,14 +809,14 @@ export class GraphManager extends EventEmitter<{
this.removeUnusedGroups();
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[];
if (!nodes.length) return;
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
// ┌──internal_a
@@ -823,14 +824,14 @@ export class GraphManager extends EventEmitter<{
// └──internal_b
// 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 groupInputs = new Map<string, Edge>();
const groupInputs = new SvelteMap<string, Edge>();
for (const edge of incomingEdges) {
groupInputs.set(`${edge[0].id}-${edge[1]}`, edge);
}
// And the same for the outputs
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) {
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
// 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));
// 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.graph.groups.map(g => g.id),
...this.graph.groups.flatMap(g => g.nodes.map(n => n.id))
@@ -949,7 +950,6 @@ export class GraphManager extends EventEmitter<{
this.removeNode(node);
}
console.log('FINISHED', this.serialize());
this.saveUndoGroup();
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 { GraphState } from './graph-state.svelte';
import {
createMockNodeRegistry,
mockFloatInputNode,
mockFloatOutputNode
} from './test-utils';
import { createMockNodeRegistry, mockFloatInputNode, mockFloatOutputNode } from './test-utils';
// GraphState constructor reads localStorage synchronously — mock before any instantiation
Object.defineProperty(globalThis, 'localStorage', {
+10 -13
View File
@@ -136,6 +136,12 @@
/>
<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}>
<Camera
bind:camera={graphState.camera}
@@ -169,14 +175,6 @@
{/if}
{#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}
<AddMenu
onnode={handleNodeCreation}
@@ -252,11 +250,10 @@
height: 100%;
}
:global(.exit-group) {
position: fixed;
.exit-group {
position: absolute;
top: 12px;
left: 50%;
transform: translateX(-50%);
left: 12px;
z-index: 1000;
padding: 4px 12px;
background: var(--color-layer-2);
@@ -268,7 +265,7 @@
opacity: 0.85;
}
:global(.exit-group:hover) {
.exit-group:hover {
opacity: 1;
}
@@ -1,7 +1,6 @@
<script lang="ts">
import { createKeyMap } from '$lib/helpers/createKeyMap';
import type { Graph, NodeInstance, NodeRegistry } from '@nodarium/types';
import { onMount } from 'svelte';
import { GraphManager } from '../graph-manager.svelte';
import { GraphState, setGraphManager, setGraphState } from '../graph-state.svelte';
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) {
const input = node.inputs?.[inputKey];
@@ -71,7 +71,7 @@
{#if appSettings.value.debug.advancedMode}
<span class="bg-white text-black! mr-2 px-1 rounded-sm opacity-30">{node.id}</span>
{/if}
{node.type.split('/').pop()}
{nodeType?.meta?.title || node.type?.split('/').pop()}
</div>
<div
class="target"
+13 -13
View File
@@ -6,11 +6,22 @@ import type {
RuntimeExecutor,
SyncCache
} 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 {
if (!graph.groups || graph.groups.length === 0) return graph;
let nodes = [...graph.nodes];
const nodes = [...graph.nodes];
let edges = [...graph.edges];
let changed = true;
@@ -87,17 +98,6 @@ export function expandGroups(graph: Graph): Graph {
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) {
if (value === undefined && 'value' in input) {
@@ -160,7 +160,7 @@ export class MemoryRuntimeExecutor implements RuntimeExecutor {
const nonVirtualTypes = graph.nodes
.map(node => node.type)
.filter(t => !t.startsWith('__internal/'));
await this.registry.load(nonVirtualTypes as any);
await this.registry.load(nonVirtualTypes);
const typeMap = new Map<string, NodeDefinition>();
for (const node of graph.nodes) {
@@ -15,10 +15,7 @@
isGroupInstance ? manager.getGroup(node!.props?.groupId as number) : undefined
);
let groupName = $state('');
$effect(() => {
groupName = activeGroup?.name ?? '';
});
const groupName = $derived(activeGroup?.name ?? '');
function handleRename(e: Event) {
const name = (e.target as HTMLInputElement).value;
+1 -1
View File
@@ -321,7 +321,7 @@
hidden={!appSettings.value.debug.advancedMode}
icon="i-[tabler--code]"
>
<GraphSource graph={manager?.serialize()} />
<GraphSource graph={manager?.serializeFullGraph()} />
</Panel>
<Panel
id="benchmark"
+2 -1
View File
@@ -1,5 +1,6 @@
<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> {
if (!cache.has(root)) {