Let a list hold structs, and read or write variables in bundles

A list item may now be a struct. The depth is bounded in the declaration
rather than by a comment: FieldKind (number|text|boolean|color) says what a
struct field holds, ListItemKind adds struct on top and says what a list
holds, so list -> struct -> primitive is the deepest storable shape and
sameValue stays finite. A struct item renders inline in the List tab, which
is why it needs no path in the slot address.

Four paths silently assumed a list item was a primitive: sameValue compared
items with ===, textOf would have printed [object Object], asListItem turned
a struct into text on Set item and Fill, and Index of used indexOf, which is
reference equality and could never have found a struct.

Tetris now runs on bundles. shapes is 112 {x, y} structs instead of 224 loose
numbers, piece and the candidate are structs, so Next move hands back a whole
candidate and each strand packs only the field it changes, since mergeStruct
leaves the rest standing. Declared behavior ports drop 49 -> 35, the master's
variables 18 -> 13, its graph 84/94 -> 69/76 nodes/edges.

Get variables and Set variables declare their ports from a list of variable
names, the way Pack and Expand already do, so one node can read or write
several at once. Renaming a variable rewires them, because there the port key
is the variable name.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
rootandClaude Opus 5 committed 2026-09-10 14:01:56 +02:00
1 parent 9d62dfea7b
commit f60358c951
32 files changed
+1083 -253

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
beim expand und pack node habe ich mir eigentlich auch vorgestellt, dass man sich eine struct variable auswählt und automatisch die felder erzeugt werden.
wir können das so lassen wie es ist (eigene felder definieren), aber eine helfer auswahl von structs das die felder erstellt wäre gut.
+16 -10
View File
@@ -4,6 +4,13 @@ Drei Punkte, die beim Bau von List-Tab und Struct-Variablen bewusst liegen gebli
standen am Ende von `done/nodes-mit-dynamischer-port-zahl.md` und sind hierher gezogen, damit sie
nicht mit der erledigten Notiz verschwinden.
**Punkt 3 ist inzwischen gebaut** (siehe unten). Offen sind noch 1 und 2.
Fehlender Nebenpunkt, der dabei aufgefallen ist: eine Listen-Variable kann ihre **Item-Art im
Editor gar nicht wählen** — `+ Variable ▸ List` legt immer `items: 'number'` an, und für
`items: 'struct'` gibt es keinen Feld-Editor. Eine Liste aus Structs entsteht deshalb heute nur
aus einer Projektdatei (Seeder); bearbeiten lässt sie sich danach normal.
## 1. Der unverbundene Eingang zeigt das falsche Feld
`inlineInputDef` (`NodeRegistry.ts:131`) kennt nur `boolean`, `string`/`any` und *alles andere* —
@@ -21,15 +28,14 @@ Ein deklariertes Struct-Feld ist heute ein Primitiv (Zahl, Text, Ja/Nein, Farbe)
einen **Pfad in der Slot-Adresse** (`ListSlot` in `state.ts` hat bewusst keinen). Die Naht ist die
eine Funktion `resolveList` — eine Signaturänderung, kein Umbau.
## 3. Liste aus Structs
## 3. Liste aus Structs — ERLEDIGT
Der teuerste der drei. `ListValue` ist heute `readonly (number | string | boolean)[]` und
`FieldValue` hält fest, dass kein Feld selbst ein Struct ist (`Properties.ts:5-8`); `sameValue`
verlässt sich darauf ("recurses exactly one level deep", `Properties.ts:50`), und
`LIST_ITEM_KINDS` macht eine Liste homogen.
Gebaut. Zwei Annahmen dieser Notiz waren dabei **falsch**, und beide haben es billiger gemacht:
Structs in Listen machen `ListValue` und `StructValue` **gegenseitig rekursiv**: beliebige
Schachtelungstiefe, echter Deep-Compare, und die Items brauchen eine gemeinsame deklarierte
Feldform — die im Typmodell keine Heimat hat, solange sie nur an *einer* Property hängt.
Setzt Punkt 2 voraus (Pfad in der Adresse).
- Es wird **nichts gegenseitig rekursiv**. Die Tiefe steckt in der *Deklaration*, nicht in einer
Konvention: `FieldKind` (Primitive) sagt, was ein Struct-**Feld** hält, `ListItemKind`
(= `FieldKind` + `struct`) sagt, was eine **Liste** hält. Damit ist `list → struct → primitiv`
die tiefste gespeicherte Form — kein beliebiges Schachteln, `sameValue` bleibt endlich.
- Es setzt **Punkt 2 nicht voraus**. Ein Struct-Item braucht keinen Pfad in der Adresse, weil es
**inline** gerendert wird — dieselbe Begründung, aus der ein Struct im Inspector ein Block ist
und keine eigene Navigation bekommt. `structFieldHandlers` trägt die Feld-Adresse.
@@ -1 +1,2 @@
Tetris Game mit Structs neu bauen
...und mit neuen `Get variables` / `Set variables` node