Let a graph node collapse to its title bar

A chevron in front of the title toggles it without touching the selection,
and right-click offers Collapse node / Expand node for the whole selection
as one undo step. A collapsed node draws its title bar alone; every link of
one side meets at a single point on it, and hidden links there share one
mark per side, since per-port marks would stack on the same pixels. It has
no ports to grab, so a link dropped on it connects nothing.

Like hiding a link this is drawing only: collapsed is saved on the node and
an expanded node leaves no trace in the project file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Opus 5 committed 2026-09-11 08:59:19 +02:00
1 parent 631cf54c72
commit 1e2285c545
12 files changed
+366 -14

No files matched your search

+30
View File
@@ -242,6 +242,36 @@ export class FoldEdgeCommand implements Command {
}
}
/** One undo step for the whole selection; every node is found before any of them changes. */
export class CollapseNodesCommand implements Command {
readonly label: string;
private previous: boolean[] = [];
constructor(
private readonly assetId: string,
private readonly nodeIds: readonly string[],
private readonly collapsed: boolean,
) {
const noun = nodeIds.length === 1 ? 'node' : 'nodes';
this.label = collapsed ? `Collapse ${noun}` : `Expand ${noun}`;
}
execute(project: Project): void {
const nodes = this.nodeIds.map((id) => requireNode(project, this.assetId, id));
this.previous = nodes.map((n) => n.collapsed === true);
for (const node of nodes) setCollapsed(node, this.collapsed);
}
undo(project: Project): void {
this.nodeIds.forEach((id, i) => setCollapsed(requireNode(project, this.assetId, id), this.previous[i]!));
}
}
function setCollapsed(node: GraphNode, collapsed: boolean): void {
if (collapsed) node.collapsed = true;
else delete node.collapsed;
}
export class DisconnectCommand implements Command {
readonly label = 'Disconnect';
private removed: Removed<GraphEdge>[] = [];
+2
View File
@@ -24,6 +24,8 @@ export interface GraphNode {
* behavior its signature; an Expand/Pack node is what gives a struct its fields.
*/
ports?: DeclaredPort[];
/** Drawn as its title bar alone, every link meeting at one point per side. Purely visual. */
collapsed?: boolean;
}
/** A port a start/end node declares. `kind` maps to the PortType of the same name (`text` → string). */
+1
View File
@@ -244,6 +244,7 @@ function normalizeGraph(raw: unknown): Graph {
const node: GraphNode = { id, type, x: toNumber(n.x), y: toNumber(n.y), params };
const ports = normalizePorts(n.ports);
if (ports.length > 0) node.ports = ports;
if (n.collapsed === true) node.collapsed = true;
graph.nodes.push(node);
}
const edgeIds = new Set<string>();