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:
1 parent
b6a609ae24
commit
3c76106fd2
15 files changed
+159
-111
No files matched your search
@@ -18,8 +18,8 @@ export const RunBehavior: NodeDef = {
|
||||
inputs: [exec()],
|
||||
outputs: [exec()],
|
||||
params: [{ key: BEHAVIOR_PARAM, label: 'Behavior', kind: 'asset', default: '', assetTypes: ['behavior'] }],
|
||||
derivePorts: (source) => {
|
||||
const program = source.behaviors(textOf(source.params[BEHAVIOR_PARAM] ?? ''));
|
||||
derivePorts: ({ node, behaviors }) => {
|
||||
const program = behaviors(textOf(node.params[BEHAVIOR_PARAM] ?? ''));
|
||||
return {
|
||||
// The graph reads as what it does, not as a row of identical "Run behavior" boxes.
|
||||
label: program?.name,
|
||||
|
||||
@@ -1,21 +1,28 @@
|
||||
import type { PropertyBag } from '../../../model/Properties';
|
||||
import type { Graph, GraphNode } from '../../Graph';
|
||||
import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asNumber } from '../../values';
|
||||
import { exec } from '../shared';
|
||||
|
||||
const COUNT = 'count';
|
||||
/** Below two outputs a Sequence is just a link, so two are fixed and the rest are derived. */
|
||||
const FIXED = 2;
|
||||
/** One letter per output, so `a` stays `a` – the alphabet is what sets the ceiling. */
|
||||
const MAX = 26;
|
||||
|
||||
export function sequenceKey(index: number): string {
|
||||
function sequenceKey(index: number): string {
|
||||
return String.fromCharCode(97 + index);
|
||||
}
|
||||
|
||||
export function sequenceCount(params: PropertyBag): number {
|
||||
const count = Math.round(asNumber(params[COUNT] ?? 3));
|
||||
return Math.max(FIXED, Math.min(MAX, count));
|
||||
function sequenceIndex(key: string): number {
|
||||
return key.length === 1 ? key.charCodeAt(0) - 97 : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* One free output below the last connected one: plugging into the spare grows the node, unplugging
|
||||
* the bottom edge shrinks it again. No output can vanish while an edge hangs on it, which is why
|
||||
* this counts the last *connected* port rather than the connected ones.
|
||||
*/
|
||||
function outputCount(node: GraphNode, graph: Graph): number {
|
||||
let last = -1;
|
||||
for (const edge of graph.edges)
|
||||
if (edge.from.node === node.id) last = Math.max(last, sequenceIndex(edge.from.port));
|
||||
return Math.min(MAX, last + 2);
|
||||
}
|
||||
|
||||
function outputAt(index: number) {
|
||||
@@ -23,23 +30,19 @@ function outputAt(index: number) {
|
||||
}
|
||||
|
||||
/**
|
||||
* The keys are `a`, `b`, `c`, … rather than `out1`…`outN`: those three are what every Sequence
|
||||
* saved so far names its outputs, and renaming them would drop the edges hanging on them.
|
||||
* The keys are `a`, `b`, `c`, … rather than `out1`…`outN`: that is what every Sequence saved so far
|
||||
* names its outputs, and renaming them would drop the edges hanging on them.
|
||||
*/
|
||||
export const Sequence: NodeDef = {
|
||||
type: 'Sequence',
|
||||
label: 'Sequence',
|
||||
category: 'flow',
|
||||
description: 'Runs its outputs one after another.',
|
||||
description: 'Runs its outputs one after another. A new output appears as you connect the last one.',
|
||||
inputs: [exec()],
|
||||
outputs: [outputAt(0), outputAt(1)],
|
||||
params: [
|
||||
{ key: COUNT, label: 'Outputs', kind: 'number', default: 3, min: FIXED, max: MAX, step: 1, decimals: 0 },
|
||||
],
|
||||
derivePorts: ({ params }) => ({
|
||||
outputs: Array.from({ length: sequenceCount(params) - FIXED }, (_, i) => outputAt(FIXED + i)),
|
||||
}),
|
||||
run: (_ctx, _inputs, params) => ({
|
||||
next: Array.from({ length: sequenceCount(params) }, (_, i) => sequenceKey(i)),
|
||||
outputs: [outputAt(0)],
|
||||
params: [],
|
||||
derivePorts: ({ node, graph }) => ({
|
||||
outputs: Array.from({ length: outputCount(node, graph) - 1 }, (_, i) => outputAt(i + 1)),
|
||||
}),
|
||||
run: (_ctx, _inputs, _params, def) => ({ next: def.outputs.map((p) => p.key) }),
|
||||
};
|
||||
Reference in new issue
Block a user