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.
49 lines
1.8 KiB
TypeScript
49 lines
1.8 KiB
TypeScript
import {
|
|
allNodeDefs,
|
|
BEHAVIOR_PARAM,
|
|
createEmptyGraph,
|
|
isTerminalType,
|
|
type NodeDef,
|
|
resolveDef,
|
|
RUN_BEHAVIOR,
|
|
terminalNode,
|
|
} from '../../core/graph';
|
|
import type { BehaviorAsset } from '../../core/model/Asset';
|
|
import { behaviorLookup } from '../../core/model/behaviorPrograms';
|
|
import type { PropertyBag } from '../../core/model/Properties';
|
|
import type { Project } from '../../core/model/Project';
|
|
|
|
/**
|
|
* One row of the palette. `def` is the definition the node would actually have once placed, so the
|
|
* drop filter and the port it lands on are right for a behavior entry too.
|
|
*/
|
|
export interface PaletteEntry {
|
|
id: string;
|
|
hint: string;
|
|
def: NodeDef;
|
|
params: PropertyBag;
|
|
}
|
|
|
|
/**
|
|
* The node types offerable in `asset`'s graph. Every other behavior appears under its own name
|
|
* instead of behind a generic "Run behavior"; the two terminals drop out once the graph has them.
|
|
*/
|
|
export function paletteEntries(project: Project, asset: BehaviorAsset): PaletteEntry[] {
|
|
const entries: PaletteEntry[] = [];
|
|
for (const def of allNodeDefs()) {
|
|
if (def.type === RUN_BEHAVIOR) continue;
|
|
if (isTerminalType(def.type) && terminalNode(asset.graph, def.type)) continue;
|
|
entries.push({ id: def.type, hint: def.category, def, params: {} });
|
|
}
|
|
const lookup = behaviorLookup(project);
|
|
for (const other of project.assets) {
|
|
// 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 };
|
|
// 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;
|
|
}
|