Let structs and lists nest to any depth (#239)
Structs and lists were capped at two fixed levels (list of structs, struct with a list field). Anything deeper was dropped silently on load, in node settings and when written to a variable. The cap came from an early design choice, not from a technical need, so it is lifted completely instead of adding a third fixed level. - One kind list NESTED_KINDS replaces ListItemKind and StructFieldKind. The flat special cases in listItems.ts are gone, every value goes through one recursive path. - A struct inside a struct is declared, like a top-level struct. Values coming in over an edge are brought into that shape. Struct items in lists still take their fields from the value. - New valuePath.ts addresses any nested spot by keys and indices. ListSlot.field is now ListSlot.path. - Field commands (add, rename, remove) take the path to the struct. They change declaration, own value and all overrides in one undo step, as before on the top level. - Inspector: "+ Field" offers Struct and List on every level. A nested struct is a header row with its own "+ Field", its fields are indented one step further. - List tab: a list item shows "n items" and opens one level deeper. The title shows the path, e.g. "Listy · nums · [3]". - Pack in Pack now writes a struct into a struct field instead of text. Objects are kept at any depth and stripped at every exit. - A list edit in a struct field without an existing object override now starts from the shown value instead of an empty struct. - Old projects load unchanged. Save, load and save again gives the same result. Tests: - ./check.sh: 172 files, 2498 tests, CHECK EXIT 0 - e2e 21 + 22: ALL PASSED, 90 checks (new 21d-iii, 22f-ii, 21d and 22f updated) - 20 new unit tests in nested-values.test.ts and nested-list-slot.test.ts, three tests that pinned the old cap turned around - 6 counter-probes, each fails 1 to 8 tests, originals restored and checked with cmp - Not covered: a nested struct inside a struct item in the list tab has no e2e click Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
483c34bb5c
commit
0c5ac32b59
29 files changed
+1046
-437
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import { itemLike } from '../../../properties/listItems';
|
||||
import type { PropertyValue } from '../../../model/Properties';
|
||||
import { itemKindOf, itemLike, makeItem } from '../../../properties/listItems';
|
||||
import type { NodeDef } from '../../NodeRegistry';
|
||||
import { asList, asNumber, asValueItem, isEntity } from '../../values';
|
||||
import { asList, asNumber, asValueItem, isEntity, type ValueList } from '../../values';
|
||||
import { any, int, list } from '../shared';
|
||||
|
||||
export const SetItem: NodeDef = {
|
||||
@@ -20,8 +21,21 @@ export const SetItem: NodeDef = {
|
||||
const value = asValueItem(inputs.value);
|
||||
const slot = items[i];
|
||||
// An object has no kind to keep and none to take on, so it is written as it is (#157).
|
||||
next[i] = isEntity(value) || isEntity(slot) ? value : itemLike(slot, value);
|
||||
if (isEntity(value) || isEntity(slot)) next[i] = value;
|
||||
else next[i] = nestedKind(slot) ? sameNested(slot, value) : itemLike(slot, value);
|
||||
return { outputs: { result: next } };
|
||||
},
|
||||
params: [],
|
||||
};
|
||||
|
||||
/** Whether a slot holds a list or a struct, which may carry objects `itemLike` would not keep. */
|
||||
const nestedKind = (slot: ValueList[number] | undefined): boolean => {
|
||||
const kind = itemKindOf(slot as PropertyValue);
|
||||
return kind === 'list' || kind === 'struct';
|
||||
};
|
||||
|
||||
/** A list or struct slot takes a value of its own kind as it is, anything else as its empty one. */
|
||||
function sameNested(slot: ValueList[number] | undefined, value: ValueList[number]): ValueList[number] {
|
||||
const kind = itemKindOf(slot as PropertyValue);
|
||||
return itemKindOf(value as PropertyValue) === kind ? value : makeItem(kind, undefined);
|
||||
}
|
||||
Reference in new issue
Block a user