Make a call node say why it does nothing, and easier to place

A behavior node that cannot run looked exactly like a healthy one: no
start node, no behavior picked, a deleted behavior or a self-call were
all silently skipped. The call still had to be forgiving at runtime -- a
dangling reference must not abort a game -- so the fix is diagnosis, not
an exception: describeProblem() names the one reason, the node draws it
in red under its title, and the inspector repeats it where you pick the
behavior. A missing end node is deliberately not a problem: a call is
always synchronous, and the end node only decides which values come back.

Two ways in: a behavior dragged from the asset panel lands in the graph
under the pointer, and double-clicking a call node opens the graph it
runs. Both reuse machinery that already existed -- the asset panel
already starts the drag, and list views already report double clicks.

The e2e module needed its assets at the head of the asset list: after 18
modules the list is long enough that a newly appended row scrolls out of
view, and the press then lands on the panel behind it, starting no drag
at all. That looked exactly like a rejected drop and cost two full runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Opus 5 committed 2026-09-09 20:40:56 +02:00
1 parent 1d8e6ebea0
commit c6f907b4e0
12 files changed
+427 -24

No files matched your search

+4 -1
View File
@@ -1,5 +1,6 @@
import { RemoveNodeCommand, SetNodeParamCommand } from '../../core/commands/graphCommands';
import {
callProblem,
edgesInto,
type GraphNode,
inlineInputDef,
@@ -32,7 +33,7 @@ export function nodeSignature(project: Project, assetId: string, nodeId: string)
.map((p) => p.key)
: [];
const declared = (node.ports ?? []).map((p) => `${p.key}:${p.kind}`).join(',');
return `${portSignature(node, lookup)}|${declared}|${connected.join(',')}|${asset.variables
return `${portSignature(node, lookup, assetId)}|${declared}|${connected.join(',')}|${asset.variables
.map((v) => v.key)
.join(',')}`;
}
@@ -48,6 +49,8 @@ export function buildNodeSection(host: InspectorHost, assetId: string, nodeId: s
addTitle(host, `${def.category.toUpperCase()} NODE`);
host.body.add(new Label(def.label, { style: 'bodyBold' }));
addNote(host, def.description);
const problem = callProblem(node, behaviorLookup(host.state.project), assetId);
if (problem) addNote(host, problem, 'warning');
if (isTerminalNode(node)) buildPortSection(host, assetId, node);
if (def.params.length > 0) addTitle(host, 'SETTINGS');