A kind like colour or texture used to be spelled out in five parallel lists, its port type, its empty value, its glyph, its label and its list tag - 22 literal places for texture alone. core/properties/kinds.ts now holds one row per kind and the rest is derived from it, so adding a kind is one row. The five literal nodes collapse into literals.ts, the six kind menus into one helper. Number, Text, Boolean is now the one order everywhere; the variable and port menus used to list Boolean before Text. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
33 lines
1.7 KiB
TypeScript
33 lines
1.7 KiB
TypeScript
import { type DeclaredPortKind, isKindWith, KIND, plainKindOf } from '../properties/kinds';
|
||
import { itemKindOf, structItemFields } from '../properties/listItems';
|
||
import type { PropertyDef, StructFieldDef } from '../properties/PropertySchema';
|
||
import type { DeclaredPort, GraphNode } from './Graph';
|
||
|
||
/** The port a variable or property travels on. A kind no port declares (an enum, a key) is what it travels as. */
|
||
export function portKindOf(def: PropertyDef): DeclaredPortKind {
|
||
if (isKindWith('port', def.kind)) return def.kind;
|
||
const plain = plainKindOf(KIND[def.kind].travels);
|
||
return plain !== undefined && isKindWith('port', plain) ? plain : 'text';
|
||
}
|
||
|
||
/**
|
||
* The bundle shape of a variable, if it has one: a struct variable declares it, a list takes it from
|
||
* the first struct item its default holds – items declare nothing, so the value is the only source.
|
||
* Both are what a Pack builds and an Expand takes apart, so both can seed a node's fields.
|
||
*/
|
||
export function structShapeOf(def: PropertyDef): readonly StructFieldDef[] | undefined {
|
||
if (def.kind === 'struct') return def.fields;
|
||
if (def.kind !== 'list') return undefined;
|
||
const item = def.default.find((entry) => itemKindOf(entry) === 'struct');
|
||
return item === undefined ? undefined : structItemFields(item);
|
||
}
|
||
|
||
/**
|
||
* The ports `fields` would add to `node`. A key the node already declares is left alone rather than
|
||
* retyped: it may carry an edge, and the manual declaration is the one the user typed.
|
||
*/
|
||
export function missingFieldPorts(node: GraphNode, fields: readonly StructFieldDef[]): DeclaredPort[] {
|
||
const taken = new Set((node.ports ?? []).map((p) => p.key));
|
||
return fields.filter((f) => !taken.has(f.key)).map((f) => ({ key: f.key, kind: f.kind }));
|
||
}
|