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
@@ -1,6 +1,7 @@
|
||||
import { RemoveNodeCommand, SetNodeParamCommand } from '../../core/commands/graphCommands';
|
||||
import {
|
||||
callProblem,
|
||||
type Graph,
|
||||
edgesInto,
|
||||
type GraphNode,
|
||||
inlineInputDef,
|
||||
@@ -25,14 +26,14 @@ export function nodeSignature(project: Project, assetId: string, nodeId: string)
|
||||
const node = asset.graph.nodes.find((n) => n.id === nodeId);
|
||||
if (!node) return 'missing';
|
||||
const lookup = behaviorLookup(project);
|
||||
const def = resolveDef(node, lookup);
|
||||
const def = resolveDef(node, asset.graph, lookup);
|
||||
const connected = def
|
||||
? inlineInputs(def)
|
||||
.filter((p) => edgesInto(asset.graph, node.id, p.key).length > 0)
|
||||
.map((p) => p.key)
|
||||
: [];
|
||||
const declared = (node.ports ?? []).map((p) => `${p.key}:${p.kind}`).join(',');
|
||||
return `${portSignature(node, lookup, assetId)}|${declared}|${connected.join(',')}|${asset.variables
|
||||
return `${portSignature(node, asset.graph, lookup, assetId)}|${declared}|${connected.join(',')}|${asset.variables
|
||||
.map((v) => v.key)
|
||||
.join(',')}`;
|
||||
}
|
||||
@@ -42,7 +43,7 @@ export function buildNodeSection(host: InspectorHost, assetId: string, nodeId: s
|
||||
const asset = findAsset(host.state.project, assetId);
|
||||
if (!asset || asset.type !== 'behavior') return;
|
||||
const node = asset.graph.nodes.find((n) => n.id === nodeId);
|
||||
const def = node && resolveDef(node, behaviorLookup(host.state.project));
|
||||
const def = node && resolveDef(node, asset.graph, behaviorLookup(host.state.project));
|
||||
if (!node || !def) return;
|
||||
|
||||
addTitle(host, `${def.category.toUpperCase()} NODE`);
|
||||
@@ -90,9 +91,9 @@ export function buildNodeSection(host: InspectorHost, assetId: string, nodeId: s
|
||||
|
||||
/** Pushes the node's current params into the rows. False when the node is gone (rebuild). */
|
||||
export function refreshNodeSection(host: InspectorHost, assetId: string, nodeId: string): boolean {
|
||||
const node = currentNode(host.state.project, assetId, nodeId);
|
||||
const def = node && resolveDef(node, behaviorLookup(host.state.project));
|
||||
if (!node || !def) return false;
|
||||
const found = currentNode(host.state.project, assetId, nodeId);
|
||||
const def = found && resolveDef(found.node, found.graph, behaviorLookup(host.state.project));
|
||||
if (!found || !def) return false;
|
||||
for (const [key, row] of host.fields) {
|
||||
const [scope, name] = key.split(':') as ['param' | 'input', string];
|
||||
const port = def.inputs.find((p) => p.key === name);
|
||||
@@ -100,7 +101,7 @@ export function refreshNodeSection(host: InspectorHost, assetId: string, nodeId:
|
||||
scope === 'param'
|
||||
? def.params.find((p) => p.key === name)?.default
|
||||
: port && inlineInputDef(port).default;
|
||||
row.setValue(node.params[name] ?? fallback ?? '');
|
||||
row.setValue(found.node.params[name] ?? fallback ?? '');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -117,7 +118,7 @@ function resolveParamDef(param: PropertyDef, asset: Asset): PropertyDef {
|
||||
/** Previews straight into the node's params; the command records the value from before the preview. */
|
||||
function paramHandlers(host: InspectorHost, assetId: string, nodeId: string, key: string): FieldHandlers {
|
||||
return previewingHandlers<GraphNode>(host, {
|
||||
target: () => currentNode(host.state.project, assetId, nodeId),
|
||||
target: () => currentNode(host.state.project, assetId, nodeId)?.node,
|
||||
read: (node) => node.params[key],
|
||||
write: (node, value) => (node.params[key] = value),
|
||||
remove: (node) => delete node.params[key],
|
||||
@@ -125,7 +126,14 @@ function paramHandlers(host: InspectorHost, assetId: string, nodeId: string, key
|
||||
});
|
||||
}
|
||||
|
||||
function currentNode(project: Project, assetId: string, nodeId: string): GraphNode | undefined {
|
||||
/** The node together with the graph it sits in, because its ports are derived from both. */
|
||||
function currentNode(
|
||||
project: Project,
|
||||
assetId: string,
|
||||
nodeId: string,
|
||||
): { node: GraphNode; graph: Graph } | undefined {
|
||||
const asset = findAsset(project, assetId);
|
||||
return asset?.type === 'behavior' ? asset.graph.nodes.find((n) => n.id === nodeId) : undefined;
|
||||
if (asset?.type !== 'behavior') return undefined;
|
||||
const node = asset.graph.nodes.find((n) => n.id === nodeId);
|
||||
return node && { node, graph: asset.graph };
|
||||
}
|
||||
Reference in new issue
Block a user