Let structs and lists nest to any depth (#239)
game-maker / gate (push) Successful in 3m52s
game-maker / suite (push) Successful in 10m24s

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:
adminandClaude Opus 5.5 committed 2026-09-26 22:33:20 +02:00
1 parent 483c34bb5c
commit 0c5ac32b59
29 files changed
+1046 -437

No files matched your search

+2 -8
View File
@@ -19,12 +19,7 @@ import { bagWithoutObjects, liveValue } from '../../src/core/graph/liveObjects';
import type { BehaviorAsset } from '../../src/core/model/Asset';
import { NO_OBJECT, type PropertyBag } from '../../src/core/model/Properties';
import { findScene } from '../../src/core/model/Project';
import {
isKindWith,
LIST_ITEM_KINDS,
STRUCT_FIELD_KINDS,
VARIABLE_KINDS,
} from '../../src/core/properties/kinds';
import { isKindWith, NESTED_KINDS, VARIABLE_KINDS } from '../../src/core/properties/kinds';
import { makeItem } from '../../src/core/properties/listItems';
import { serializeProject } from '../../src/core/store/serialize';
import {
@@ -123,8 +118,7 @@ const placedOf = (f: SceneFixture, behavior: BehaviorAsset) =>
describe('the object kind', () => {
it('can be a variable, a struct field and a list item', () => {
expect(VARIABLE_KINDS).toContain('object');
expect(STRUCT_FIELD_KINDS).toContain('object');
expect(LIST_ITEM_KINDS).toContain('object');
expect(NESTED_KINDS).toContain('object');
expect(isKindWith('tagged', 'object')).toBe(true);
});