Describe every value kind in one table instead of spreading it over the code

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>
This commit is contained in:
rootandClaude Opus 5 committed 2026-09-12 14:47:17 +02:00
1 parent 8a7185948d
commit 3ed8b68ecf
39 files changed
+699 -565

No files matched your search

-14
View File
@@ -1,14 +0,0 @@
import type { NodeDef } from '../../NodeRegistry';
import { asBoolean } from '../../values';
import { bool } from '../shared';
export const BooleanLiteral: NodeDef = {
type: 'Boolean',
label: 'Boolean',
category: 'data',
description: 'A constant true/false.',
inputs: [],
outputs: [bool('value')],
params: [{ key: 'value', label: 'Value', kind: 'boolean', default: false }],
run: (_ctx, _inputs, params) => ({ outputs: { value: asBoolean(params.value) } }),
};
+3 -4
View File
@@ -1,5 +1,4 @@
import { sameValue } from '../../../model/Properties';
import { itemValue } from '../../../properties/listItems';
import { sameValue, untag } from '../../../model/Properties';
import type { NodeDef } from '../../NodeRegistry';
import { asList, asListItem } from '../../values';
import { any, list, num } from '../shared';
@@ -15,9 +14,9 @@ export const IndexOf: NodeDef = {
// Compared by content, not by identity: two equal structs are never the same object. A color item
// is compared as its number, or searching a list of colors with one would never find it.
run: (_ctx, inputs) => {
const wanted = itemValue(asListItem(inputs.value));
const wanted = untag(asListItem(inputs.value));
return {
outputs: { index: asList(inputs.list).findIndex((item) => sameValue(itemValue(item), wanted)) },
outputs: { index: asList(inputs.list).findIndex((item) => sameValue(untag(item), wanted)) },
};
},
};
+2 -2
View File
@@ -1,4 +1,4 @@
import { itemValue } from '../../../properties/listItems';
import { untag } from '../../../model/Properties';
import type { NodeDef } from '../../NodeRegistry';
import { asList, asNumber } from '../../values';
import { any, int, list } from '../shared';
@@ -14,7 +14,7 @@ export const ItemAt: NodeDef = {
run: (_ctx, inputs) => {
const items = asList(inputs.list);
const i = Math.floor(asNumber(inputs.index));
return { outputs: { item: itemValue(items[i]) ?? 0 } };
return { outputs: { item: untag(items[i]) ?? 0 } };
},
params: [],
};
-14
View File
@@ -1,14 +0,0 @@
import type { NodeDef } from '../../NodeRegistry';
import { asList } from '../../values';
import { list } from '../shared';
export const ListLiteral: NodeDef = {
type: 'List',
label: 'List',
category: 'data',
description: 'A constant list, edited item by item in the List tab – each item with its own kind.',
inputs: [],
outputs: [list('value')],
params: [{ key: 'value', label: 'Value', kind: 'list', default: [] }],
run: (_ctx, _inputs, params) => ({ outputs: { value: asList(params.value) } }),
};
-14
View File
@@ -1,14 +0,0 @@
import type { NodeDef } from '../../NodeRegistry';
import { asNumber } from '../../values';
import { num } from '../shared';
export const NumberLiteral: NodeDef = {
type: 'Number',
label: 'Number',
category: 'data',
description: 'A constant number.',
inputs: [],
outputs: [num('value')],
params: [{ key: 'value', label: 'Value', kind: 'number', default: 0, step: 1 }],
run: (_ctx, _inputs, params) => ({ outputs: { value: asNumber(params.value) } }),
};
-14
View File
@@ -1,14 +0,0 @@
import type { NodeDef } from '../../NodeRegistry';
import { asString } from '../../values';
import { str } from '../shared';
export const StringLiteral: NodeDef = {
type: 'String',
label: 'Text',
category: 'data',
description: 'A constant text.',
inputs: [],
outputs: [str('value')],
params: [{ key: 'value', label: 'Value', kind: 'text', default: '' }],
run: (_ctx, _inputs, params) => ({ outputs: { value: asString(params.value) } }),
};
-15
View File
@@ -1,15 +0,0 @@
import type { NodeDef } from '../../NodeRegistry';
import { asString } from '../../values';
import { str } from '../shared';
/** The picture is its id on the edge: a whole texture, one frame of a sheet or an animation. */
export const TextureLiteral: NodeDef = {
type: 'Texture',
label: 'Texture',
category: 'data',
description: 'A constant picture: a texture, one of its frames or an animation, as a sprite wears it.',
inputs: [],
outputs: [str('value')],
params: [{ key: 'value', label: 'Value', kind: 'texture', default: '' }],
run: (_ctx, _inputs, params) => ({ outputs: { value: asString(params.value) } }),
};
+37
View File
@@ -0,0 +1,37 @@
import { KIND, kindLabel, type VariableKind } from '../../../properties/kinds';
import { coerceValue, flatDef, type PropertyDef } from '../../../properties/PropertySchema';
import type { NodeDef } from '../../NodeRegistry';
/** A node handing out one constant of a kind: its one setting, edited as that kind, is its output. */
function literal(type: string, kind: Exclude<VariableKind, 'struct'>, description: string): NodeDef {
const value: PropertyDef =
kind === 'list'
? { key: 'value', label: 'Value', kind, default: [] }
: kind === 'number'
? { ...flatDef('value', kind, { label: 'Value' }), step: 1 }
: flatDef('value', kind, { label: 'Value' });
return {
type,
label: kindLabel(kind),
category: 'data',
description,
inputs: [],
outputs: [{ key: 'value', label: 'value', type: KIND[kind].travels }],
params: [value],
run: (_ctx, _inputs, params) => ({ outputs: { value: coerceValue(value, params.value ?? value.default) } }),
};
}
export const NumberLiteral = literal('Number', 'number', 'A constant number.');
export const BooleanLiteral = literal('Boolean', 'boolean', 'A constant true/false.');
export const StringLiteral = literal('String', 'text', 'A constant text.');
export const TextureLiteral = literal(
'Texture',
'texture',
'A constant picture: a texture, one of its frames or an animation, as a sprite wears it.',
);
export const ListLiteral = literal(
'List',
'list',
'A constant list, edited item by item in the List tab – each item with its own kind.',
);