Let a Sequence grow an output as you connect the last one

The Outputs setting is gone. A Sequence now keeps one free output below the
last connected one: nothing plugged in is one output, connecting the spare
grows a new one, unplugging the bottom edge shrinks it back.

It counts the last *connected* port rather than the number of edges, so
unplugging a middle one leaves a gap instead of silently dropping an edge.

Removing the setting also removes a bug it caused. Typing in the Outputs field
did nothing and the arrow keys did nothing, while dragging the value worked:
onInput fires per keystroke, the preview writes into node.params, that emits
'project', and Inspector.refreshValues() sees the node's signature change --
the port list is part of it -- so it rebuilds and destroys the row being typed
in. A drag survives because it keeps receiving globalpointermove on the old
widget. refreshValues() has no focus guard where refreshLive() does; the trap
is still there for the next typed field that changes a node's shape.

Ports that depend on the edges around a node mean derivePorts needs the graph,
not just the params, so resolveDef and portSignature take it too. And a node
whose ports are derived cannot know how many it has from its type, so run is
handed the definition resolved for it.
This commit is contained in:
root committed 2026-09-10 08:32:14 +02:00
1 parent b6a609ae24
commit 3c76106fd2
15 files changed
+159 -111

No files matched your search

+1 -1
View File
@@ -235,7 +235,7 @@ export class GraphCanvas extends Widget {
const seen = new Set<string>();
const lookup = behaviorLookup(this.state.project);
for (const node of asset.graph.nodes) {
const def = resolveDef(node, lookup);
const def = resolveDef(node, asset.graph, lookup);
if (!def) continue;
seen.add(node.id);
let view = this.views.get(node.id);
+3 -1
View File
@@ -1,6 +1,7 @@
import {
allNodeDefs,
BEHAVIOR_PARAM,
createEmptyGraph,
isTerminalType,
type NodeDef,
resolveDef,
@@ -39,7 +40,8 @@ export function paletteEntries(project: Project, asset: BehaviorAsset): PaletteE
// A behavior running itself is an endless loop, so it is not on offer.
if (other.type !== 'behavior' || other.id === asset.id) continue;
const params = { [BEHAVIOR_PARAM]: other.id };
const def = resolveDef({ id: '', type: RUN_BEHAVIOR, x: 0, y: 0, params }, lookup);
// The entry is a preview of a node that sits in no graph yet, hence the empty one.
const def = resolveDef({ id: '', type: RUN_BEHAVIOR, x: 0, y: 0, params }, createEmptyGraph(), lookup);
if (def) entries.push({ id: `${RUN_BEHAVIOR}:${other.id}`, hint: 'behavior', def, params });
}
return entries;