Fix findUsages/undo/node-lookup bugs, split Viewport and Editor, share command and inspector helpers
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1eb28314cf
commit
f140c2fd77
58 files changed
+1969
-1171
No files matched your search
@@ -1,12 +1,13 @@
|
||||
import { edgesInto, findNode, type GraphEdge, type GraphNode, portsCompatible } from '../graph/Graph';
|
||||
import { requireNodeDef } from '../graph/NodeRegistry';
|
||||
import { edgesInto, edgesOfNode, findNode, type GraphEdge, type GraphNode, portsCompatible } from '../graph/Graph';
|
||||
import { nodeDef } from '../graph/NodeRegistry';
|
||||
import { newId } from '../ids';
|
||||
import { type PropertyBag, type PropertyValue, setBagValue } from '../model/Properties';
|
||||
import type { PropertyBag, PropertyValue } from '../model/Properties';
|
||||
import type { Project } from '../model/Project';
|
||||
import { coerceValue, defaultsOf } from '../properties/PropertySchema';
|
||||
import { type Removed, removeWhere, restoreAt } from '../util/list';
|
||||
import { type Removed, removeById, removeFirst, removeWhere, restoreAt } from '../util/list';
|
||||
import { type Command, CommandError } from './Command';
|
||||
import { requireBehavior, requireNode } from './require';
|
||||
import { requireBehavior, requireNode, requireNodeDef } from './require';
|
||||
import { SetBagValueCommand } from './SetBagValueCommand';
|
||||
|
||||
/** Adds a node of the given type at graph coordinates. Params start at their defaults. */
|
||||
export class AddNodeCommand implements Command {
|
||||
@@ -14,18 +15,21 @@ export class AddNodeCommand implements Command {
|
||||
readonly node: GraphNode;
|
||||
|
||||
constructor(private readonly assetId: string, type: string, x: number, y: number, params: PropertyBag = {}) {
|
||||
const def = requireNodeDef(type);
|
||||
this.node = { id: newId(), type, x: Math.round(x), y: Math.round(y), params: { ...defaultsOf(def.params), ...params } };
|
||||
this.label = `Add ${def.label}`;
|
||||
// An unknown type is rejected in `execute`, not here: constructors run outside the
|
||||
// editor's error handling, so throwing here would crash instead of showing a notice.
|
||||
const def = nodeDef(type);
|
||||
this.node = { id: newId(), type, x: Math.round(x), y: Math.round(y), params: { ...defaultsOf(def?.params ?? []), ...params } };
|
||||
this.label = def ? `Add ${def.label}` : `Add ${type}`;
|
||||
}
|
||||
|
||||
execute(project: Project): void {
|
||||
requireNodeDef(this.node.type);
|
||||
requireBehavior(project, this.assetId).graph.nodes.push(this.node);
|
||||
}
|
||||
|
||||
undo(project: Project): void {
|
||||
const graph = requireBehavior(project, this.assetId).graph;
|
||||
graph.nodes = graph.nodes.filter((n) => n.id !== this.node.id);
|
||||
graph.nodes = removeById(graph.nodes, this.node.id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,12 +42,11 @@ export class RemoveNodeCommand implements Command {
|
||||
|
||||
execute(project: Project): void {
|
||||
const graph = requireBehavior(project, this.assetId).graph;
|
||||
const index = graph.nodes.findIndex((n) => n.id === this.nodeId);
|
||||
const node = graph.nodes[index];
|
||||
const node = removeFirst(graph.nodes, (n) => n.id === this.nodeId);
|
||||
if (!node) throw new CommandError(`Node ${this.nodeId} not found`);
|
||||
const edges = removeWhere(graph.edges, (e) => e.from.node === this.nodeId || e.to.node === this.nodeId);
|
||||
graph.nodes.splice(index, 1);
|
||||
this.removed = { node: { item: node, index }, edges };
|
||||
// `edgesOfNode` reads the edge list only, so it still finds them after the node is gone.
|
||||
const touching = edgesOfNode(graph, this.nodeId);
|
||||
this.removed = { node, edges: removeWhere(graph.edges, (e) => touching.includes(e)) };
|
||||
}
|
||||
|
||||
undo(project: Project): void {
|
||||
@@ -79,24 +82,23 @@ export class MoveNodeCommand implements Command {
|
||||
}
|
||||
|
||||
/** Sets a node param or the inline value of an unconnected data input. */
|
||||
export class SetNodeParamCommand implements Command {
|
||||
readonly label: string;
|
||||
private previous: PropertyValue | undefined;
|
||||
|
||||
constructor(private readonly assetId: string, private readonly nodeId: string, private readonly key: string, private readonly value: PropertyValue) {
|
||||
this.label = `Set ${key}`;
|
||||
export class SetNodeParamCommand extends SetBagValueCommand<GraphNode> {
|
||||
constructor(private readonly assetId: string, private readonly nodeId: string, key: string, private readonly value: PropertyValue) {
|
||||
super(key, `Set ${key}`);
|
||||
}
|
||||
|
||||
execute(project: Project): void {
|
||||
const node = requireNode(project, this.assetId, this.nodeId);
|
||||
protected override resolveTarget(project: Project): GraphNode {
|
||||
return requireNode(project, this.assetId, this.nodeId);
|
||||
}
|
||||
|
||||
protected override bagOf(node: GraphNode): PropertyBag {
|
||||
return node.params;
|
||||
}
|
||||
|
||||
protected override computeNext(node: GraphNode): PropertyValue {
|
||||
const param = requireNodeDef(node.type).params.find((p) => p.key === this.key);
|
||||
this.previous = node.params[this.key];
|
||||
// Inline input values are stored as-is; declared params are coerced against their definition.
|
||||
node.params[this.key] = param && param.kind !== 'enum' ? coerceValue(param, this.value) : this.value;
|
||||
}
|
||||
|
||||
undo(project: Project): void {
|
||||
setBagValue(requireNode(project, this.assetId, this.nodeId).params, this.key, this.previous);
|
||||
return param && param.kind !== 'enum' ? coerceValue(param, this.value) : this.value;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +133,7 @@ export class ConnectCommand implements Command {
|
||||
|
||||
undo(project: Project): void {
|
||||
const graph = requireBehavior(project, this.assetId).graph;
|
||||
graph.edges = graph.edges.filter((e) => e.id !== this.edge.id);
|
||||
graph.edges = removeById(graph.edges, this.edge.id);
|
||||
restoreAt(graph.edges, this.replaced);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user