feat: debounce cameraPosition saving
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
import { clone } from '$lib/helpers';
|
import { clone, debounce } from '$lib/helpers';
|
||||||
import throttle from '$lib/helpers/throttle';
|
import throttle from '$lib/helpers/throttle';
|
||||||
import { RemoteNodeRegistry } from '$lib/node-registry/index';
|
import { RemoteNodeRegistry } from '$lib/node-registry/index';
|
||||||
import type {
|
import type {
|
||||||
@@ -309,17 +309,18 @@ export class GraphManager extends EventEmitter<{
|
|||||||
this.nodes.set(n.id, n);
|
this.nodes.set(n.id, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.edges = graph.edges.map((edge) => {
|
this.edges = graph.edges.flatMap((edge) => {
|
||||||
const from = this.nodes.get(edge[0]);
|
const from = this.nodes.get(edge[0]);
|
||||||
const to = this.nodes.get(edge[2]);
|
const to = this.nodes.get(edge[2]);
|
||||||
if (!from || !to) {
|
if (!from || !to) {
|
||||||
throw new Error('Edge references non-existing node');
|
log.warn('Dropping orphaned edge', edge);
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
from.state.children = from.state.children || [];
|
from.state.children = from.state.children || [];
|
||||||
from.state.children.push(to);
|
from.state.children.push(to);
|
||||||
to.state.parents = to.state.parents || [];
|
to.state.parents = to.state.parents || [];
|
||||||
to.state.parents.push(from);
|
to.state.parents.push(from);
|
||||||
return [from, edge[1], to, edge[3]] as Edge;
|
return [[from, edge[1], to, edge[3]] as Edge];
|
||||||
});
|
});
|
||||||
|
|
||||||
this.execute();
|
this.execute();
|
||||||
@@ -657,9 +658,9 @@ export class GraphManager extends EventEmitter<{
|
|||||||
const inputs = Object.entries(to.state?.type?.inputs ?? {});
|
const inputs = Object.entries(to.state?.type?.inputs ?? {});
|
||||||
const outputs = from.state?.type?.outputs ?? [];
|
const outputs = from.state?.type?.outputs ?? [];
|
||||||
for (let i = 0; i < inputs.length; i++) {
|
for (let i = 0; i < inputs.length; i++) {
|
||||||
const [inputName, input] = inputs[0];
|
const [inputName, input] = inputs[i];
|
||||||
for (let o = 0; o < outputs.length; o++) {
|
for (let o = 0; o < outputs.length; o++) {
|
||||||
const output = outputs[0];
|
const output = outputs[o];
|
||||||
if (input.type === output) {
|
if (input.type === output) {
|
||||||
return this.createEdge(from, o, to, inputName);
|
return this.createEdge(from, o, to, inputName);
|
||||||
}
|
}
|
||||||
@@ -1239,20 +1240,18 @@ export class GraphManager extends EventEmitter<{
|
|||||||
this.save();
|
this.save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _emitSave = debounce(() => {
|
||||||
|
if (this.nodes.size === 0 && this.edges.length === 0) return;
|
||||||
|
const state = this.serialize();
|
||||||
|
this.emit('save', state);
|
||||||
|
log.log('saving graphs', state);
|
||||||
|
}, 300);
|
||||||
|
|
||||||
save() {
|
save() {
|
||||||
if (this.currentUndoGroup) return;
|
if (this.currentUndoGroup) return;
|
||||||
const state = this.serialize();
|
// History snapshot is immediate; the IDB emit is debounced.
|
||||||
this.history.save(state);
|
this.history.save(this.serialize());
|
||||||
|
this._emitSave();
|
||||||
// This is some stupid race condition where the graph-manager emits a save event
|
|
||||||
// when the graph is not fully loaded
|
|
||||||
if (this.nodes.size === 0 && this.edges.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const fullState = this.serialize();
|
|
||||||
this.emit('save', fullState);
|
|
||||||
log.log('saving graphs', fullState);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getParentsOfNode(node: NodeInstance) {
|
getParentsOfNode(node: NodeInstance) {
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { animate, lerp } from '$lib/helpers';
|
import { animate, debounce, lerp } from '$lib/helpers';
|
||||||
import type { NodeInstance, SerializedEdge, SerializedNode, Socket } from '@nodarium/types';
|
import type { NodeInstance, SerializedEdge, SerializedNode, Socket } from '@nodarium/types';
|
||||||
import { getContext, setContext } from 'svelte';
|
import { getContext, setContext } from 'svelte';
|
||||||
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
import { SvelteMap, SvelteSet } from 'svelte/reactivity';
|
||||||
@@ -62,12 +62,20 @@ export class GraphState {
|
|||||||
colors = new ColorGenerator(predefinedColors);
|
colors = new ColorGenerator(predefinedColors);
|
||||||
|
|
||||||
constructor(private graph: GraphManager) {
|
constructor(private graph: GraphManager) {
|
||||||
|
const saveCameraPosition = debounce(() => {
|
||||||
|
localStorage.setItem(
|
||||||
|
'cameraPosition',
|
||||||
|
`[${this.cameraPosition[0]},${this.cameraPosition[1]},${this.cameraPosition[2]}]`
|
||||||
|
);
|
||||||
|
}, 500);
|
||||||
|
|
||||||
$effect.root(() => {
|
$effect.root(() => {
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
localStorage.setItem(
|
// Read values to subscribe to reactivity, then flush lazily.
|
||||||
'cameraPosition',
|
void this.cameraPosition[0];
|
||||||
`[${this.cameraPosition[0]},${this.cameraPosition[1]},${this.cameraPosition[2]}]`
|
void this.cameraPosition[1];
|
||||||
);
|
void this.cameraPosition[2];
|
||||||
|
saveCameraPosition();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
const storedPosition = localStorage.getItem('cameraPosition');
|
const storedPosition = localStorage.getItem('cameraPosition');
|
||||||
@@ -157,6 +165,27 @@ export class GraphState {
|
|||||||
this.edges.delete(edgeId);
|
this.edges.delete(edgeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _dirtyPositions = new Set<NodeInstance>();
|
||||||
|
private _positionFlushPending = false;
|
||||||
|
|
||||||
|
private _flushPositions() {
|
||||||
|
for (const node of this._dirtyPositions) {
|
||||||
|
if (node.state['x'] !== undefined && node.state['y'] !== undefined) {
|
||||||
|
if (node.state.ref) {
|
||||||
|
node.state.ref.style.setProperty('--nx', `${node.state.x * 10}px`);
|
||||||
|
node.state.ref.style.setProperty('--ny', `${node.state.y * 10}px`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (node.state.ref) {
|
||||||
|
node.state.ref.style.setProperty('--nx', `${node.position[0] * 10}px`);
|
||||||
|
node.state.ref.style.setProperty('--ny', `${node.position[1] * 10}px`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._dirtyPositions.clear();
|
||||||
|
this._positionFlushPending = false;
|
||||||
|
}
|
||||||
|
|
||||||
updateNodePosition(node: NodeInstance) {
|
updateNodePosition(node: NodeInstance) {
|
||||||
if (
|
if (
|
||||||
node.state.x === node.position[0]
|
node.state.x === node.position[0]
|
||||||
@@ -166,16 +195,10 @@ export class GraphState {
|
|||||||
delete node.state.y;
|
delete node.state.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node.state['x'] !== undefined && node.state['y'] !== undefined) {
|
this._dirtyPositions.add(node);
|
||||||
if (node.state.ref) {
|
if (!this._positionFlushPending) {
|
||||||
node.state.ref.style.setProperty('--nx', `${node.state.x * 10}px`);
|
this._positionFlushPending = true;
|
||||||
node.state.ref.style.setProperty('--ny', `${node.state.y * 10}px`);
|
requestAnimationFrame(() => this._flushPositions());
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (node.state.ref) {
|
|
||||||
node.state.ref.style.setProperty('--nx', `${node.position[0] * 10}px`);
|
|
||||||
node.state.ref.style.setProperty('--ny', `${node.position[1] * 10}px`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user