@pix3/cli 1.6.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +788 -754
  3. package/dist/index.js +208 -203
  4. package/dist/runtime-types.json +1 -1
  5. package/dist/smoke/prebuilt/{chunk-J2SJVMDO.mjs → chunk-7MZGPAUK.mjs} +1 -1
  6. package/dist/smoke/prebuilt/chunk-7WOFGLW3.mjs +239 -0
  7. package/dist/smoke/prebuilt/runtime.mjs +1 -1
  8. package/dist/smoke/prebuilt/smoke-worker.mjs +1 -1
  9. package/dist/smoke/prebuilt/tree-defaults.mjs +1 -1
  10. package/dist/validate/prebuilt/bundle-main.js +13 -13
  11. package/dist/validate/prebuilt/chunk-HKG6YI6Y.js +239 -0
  12. package/dist/validate/prebuilt/runtime.js +1 -1
  13. package/kit/files/.claude/skills/pix3-nodes/SKILL.md +210 -210
  14. package/kit/files/.claude/skills/pix3-nodes/reference.md +32 -24
  15. package/kit/files/.claude/skills/pix3-scene-format/SKILL.md +238 -235
  16. package/kit/files/.claude/skills/pix3-scene-format/reference.md +28 -28
  17. package/kit/files/.claude/skills/pix3-scripts/SKILL.md +285 -281
  18. package/kit/files/.claude/skills/pix3-scripts/reference.md +69 -52
  19. package/kit/files/.claude/skills/pix3-verify/SKILL.md +298 -256
  20. package/kit/files/.claude/skills/pix3-verify/reference.md +21 -21
  21. package/kit/files/AGENTS.md +203 -203
  22. package/kit/files/CLAUDE.md +1 -1
  23. package/kit/kit.json +2 -2
  24. package/package.json +54 -54
  25. package/templates/empty-2d/files/README.md +20 -20
  26. package/templates/empty-2d/files/scenes/main.pix3scene +68 -68
  27. package/templates/empty-2d/template.yaml +7 -7
  28. package/templates/empty-3d/files/README.md +20 -20
  29. package/templates/empty-3d/files/scenes/main.pix3scene +108 -108
  30. package/templates/empty-3d/template.yaml +7 -7
  31. package/templates/idea-blank/files/README.md +22 -22
  32. package/templates/idea-blank/files/scenes/main.pix3scene +37 -37
  33. package/templates/idea-blank/template.yaml +10 -10
  34. package/templates/minigame-2d/files/README.md +44 -44
  35. package/templates/minigame-2d/files/design/tests/reachability.json +1 -1
  36. package/templates/minigame-2d/files/design/tests/routines/mute-music.json +44 -44
  37. package/templates/minigame-2d/files/scenes/main.pix3scene +84 -84
  38. package/templates/minigame-2d/files/scenes/menu.pix3scene +118 -118
  39. package/templates/minigame-2d/files/scenes/ui/settings-window.pix3scene +138 -138
  40. package/templates/minigame-2d/files/scripts/GameFlow.ts +133 -133
  41. package/templates/minigame-2d/files/scripts/MenuFlow.ts +217 -217
  42. package/templates/minigame-2d/files/scripts/SettingsWindow.ts +198 -198
  43. package/templates/minigame-2d/template.yaml +10 -10
  44. package/templates/playable-2d/files/README.md +30 -30
  45. package/templates/playable-2d/files/design/recipe.md +83 -83
  46. package/templates/playable-2d/files/design/tests/reachability.json +4 -4
  47. package/templates/playable-2d/files/design/tests/routines/intro-to-cta.json +32 -32
  48. package/templates/playable-2d/files/scenes/main.pix3scene +111 -111
  49. package/templates/playable-2d/files/scenes/ui/end-screen.pix3scene +91 -91
  50. package/templates/playable-2d/files/scenes/ui/intro.pix3scene +65 -65
  51. package/templates/playable-2d/files/scripts/GameFlow.ts +256 -256
  52. package/templates/playable-2d/template.yaml +9 -9
  53. package/templates/playable-3d/files/README.md +32 -32
  54. package/templates/playable-3d/files/design/recipe.md +127 -127
  55. package/templates/playable-3d/files/design/tests/reachability.json +4 -4
  56. package/templates/playable-3d/files/design/tests/routines/intro-to-cta.json +32 -32
  57. package/templates/playable-3d/files/scenes/main.pix3scene +230 -230
  58. package/templates/playable-3d/files/scenes/ui/end-screen.pix3scene +91 -91
  59. package/templates/playable-3d/files/scenes/ui/intro.pix3scene +65 -65
  60. package/templates/playable-3d/files/scripts/GameFlow.ts +309 -309
  61. package/templates/playable-3d/files/scripts/GameRules.ts +290 -290
  62. package/templates/playable-3d/files/scripts/ScoreHud.ts +113 -113
  63. package/templates/playable-3d/template.yaml +9 -9
  64. package/templates/recipe-arena-2d/files/README.md +48 -48
  65. package/templates/recipe-arena-2d/files/design/recipe.md +127 -127
  66. package/templates/recipe-arena-2d/files/scenes/main.pix3scene +292 -292
  67. package/templates/recipe-arena-2d/files/scenes/menu.pix3scene +100 -100
  68. package/templates/recipe-arena-2d/files/scenes/prefabs/hazard.pix3scene +42 -42
  69. package/templates/recipe-arena-2d/files/scenes/prefabs/pickup.pix3scene +45 -45
  70. package/templates/recipe-arena-2d/files/scenes/ui/result.pix3scene +85 -85
  71. package/templates/recipe-arena-2d/files/scripts/GameRules.ts +368 -368
  72. package/templates/recipe-arena-2d/files/scripts/MenuFlow.ts +106 -106
  73. package/templates/recipe-arena-2d/files/scripts/PlayerController.ts +151 -151
  74. package/templates/recipe-arena-2d/files/scripts/ScoreHud.ts +94 -94
  75. package/templates/recipe-arena-2d/files/scripts/Spawner.ts +214 -214
  76. package/templates/recipe-arena-2d/files/scripts/TouchRules.ts +174 -174
  77. package/templates/recipe-arena-2d/template.yaml +10 -10
  78. package/templates/recipe-blank-2d/files/README.md +61 -61
  79. package/templates/recipe-blank-2d/files/design/recipe.md +148 -148
  80. package/templates/recipe-blank-2d/files/design/tests/reachability.json +4 -4
  81. package/templates/recipe-blank-2d/files/design/tests/routines/restart.json +27 -27
  82. package/templates/recipe-blank-2d/files/scenes/main.pix3scene +224 -224
  83. package/templates/recipe-blank-2d/files/scenes/ui/result.pix3scene +85 -85
  84. package/templates/recipe-blank-2d/files/scripts/GameRules.ts +350 -350
  85. package/templates/recipe-blank-2d/files/scripts/ScoreHud.ts +110 -110
  86. package/templates/recipe-blank-2d/template.yaml +10 -10
  87. package/templates/recipe-bouncer-2d/files/README.md +81 -81
  88. package/templates/recipe-bouncer-2d/files/design/recipe.md +146 -146
  89. package/templates/recipe-bouncer-2d/files/scenes/main.pix3scene +615 -615
  90. package/templates/recipe-bouncer-2d/files/scenes/menu.pix3scene +191 -191
  91. package/templates/recipe-bouncer-2d/files/scenes/ui/result.pix3scene +87 -87
  92. package/templates/recipe-bouncer-2d/files/scripts/BallBody.ts +298 -298
  93. package/templates/recipe-bouncer-2d/files/scripts/GameRules.ts +377 -377
  94. package/templates/recipe-bouncer-2d/files/scripts/MenuFlow.ts +109 -109
  95. package/templates/recipe-bouncer-2d/files/scripts/PaddleController.ts +112 -112
  96. package/templates/recipe-bouncer-2d/files/scripts/ScoreHud.ts +94 -94
  97. package/templates/recipe-bouncer-2d/files/scripts/TouchRules.ts +263 -263
  98. package/templates/recipe-bouncer-2d/files/scripts/ball-collision.ts +307 -307
  99. package/templates/recipe-bouncer-2d/template.yaml +10 -10
  100. package/templates/recipe-grid-3d/files/README.md +22 -22
  101. package/templates/recipe-grid-3d/files/design/recipe.md +110 -110
  102. package/templates/recipe-grid-3d/files/scenes/main.pix3scene +169 -169
  103. package/templates/recipe-grid-3d/files/scenes/menu.pix3scene +100 -100
  104. package/templates/recipe-grid-3d/files/scenes/ui/result.pix3scene +105 -105
  105. package/templates/recipe-grid-3d/files/scripts/GridBoard.ts +319 -319
  106. package/templates/recipe-grid-3d/files/scripts/GridRules.ts +278 -278
  107. package/templates/recipe-grid-3d/files/scripts/MenuFlow.ts +106 -106
  108. package/templates/recipe-grid-3d/files/scripts/ScoreHud.ts +94 -94
  109. package/templates/recipe-grid-3d/template.yaml +10 -10
  110. package/templates/recipe-tapper-2d/files/README.md +39 -39
  111. package/templates/recipe-tapper-2d/files/design/recipe.md +132 -132
  112. package/templates/recipe-tapper-2d/files/design/tests/reachability.json +4 -4
  113. package/templates/recipe-tapper-2d/files/design/tests/routines/terminal-retry.json +23 -23
  114. package/templates/recipe-tapper-2d/files/scenes/main.pix3scene +269 -269
  115. package/templates/recipe-tapper-2d/files/scenes/menu.pix3scene +100 -100
  116. package/templates/recipe-tapper-2d/files/scenes/prefabs/hazard.pix3scene +42 -42
  117. package/templates/recipe-tapper-2d/files/scenes/prefabs/target.pix3scene +45 -45
  118. package/templates/recipe-tapper-2d/files/scenes/ui/result.pix3scene +85 -85
  119. package/templates/recipe-tapper-2d/files/scripts/GameRules.ts +368 -368
  120. package/templates/recipe-tapper-2d/files/scripts/MenuFlow.ts +106 -106
  121. package/templates/recipe-tapper-2d/files/scripts/ScoreHud.ts +94 -94
  122. package/templates/recipe-tapper-2d/files/scripts/Spawner.ts +214 -214
  123. package/templates/recipe-tapper-2d/files/scripts/TouchRules.ts +192 -192
  124. package/templates/recipe-tapper-2d/template.yaml +10 -10
  125. package/dist/smoke/prebuilt/chunk-OJXQXIIQ.mjs +0 -239
  126. package/dist/validate/prebuilt/chunk-BS2OZM7U.js +0 -239
@@ -1,235 +1,238 @@
1
- ---
2
- name: pix3-scene-format
3
- description: The .pix3scene YAML format — file header, node shape (id/type/name/properties/children/components), component shape, 2D transform and layout blocks, res:// references, textures, prefab instances and overrides, scenes/ui overlays and the visible/initiallyVisible split, plus how to translate design/recipe.md tool names into file edits. Use BEFORE creating or editing any .pix3scene file (scene, prefab or overlay) in this project.
4
- ---
5
-
6
- <!-- Pix3 agent kit 1.6.0 -->
7
- # `.pix3scene` format
8
-
9
- Scenes, prefabs and overlays are the same file type: YAML with a `.pix3scene` extension.
10
- "Prefab" only means "a scene file that other scenes instance". There is no `.pix3prefab`.
11
- The engine's own statement of the format (principles, skinned UI controls, Spine nodes) is in
12
- `reference.md` beside this file; where the two differ, this page describes what the loader and
13
- `pix3 validate` actually do.
14
-
15
- ## Before you write
16
-
17
- - Re-read the file you are about to change — the human may have edited it in the editor.
18
- - Change only the lines you mean to change. A whole-file rewrite from memory drops what the
19
- human (or the editor) wrote since you last read it.
20
- - If your tools support it, write to a temporary file and rename it over the target. Not
21
- required for correctness — the editor waits for the file to stop changing — but cleaner.
22
- - Then `pix3 check` (see `pix3-verify`).
23
-
24
- ## File
25
-
26
- ```yaml
27
- version: 1.0.0 # the only format version; copy it
28
- metadata: # optional, free-form
29
- author: Pix3 Recipe
30
- description: Tapper gameplay — falling objects
31
- root: # list of root nodes; at least one
32
- - id: game-root
33
- type: Group2D
34
- ...
35
- ```
36
-
37
- `#` lines are comments and may appear anywhere. Top-level keys are exactly `version`,
38
- `description`, `metadata`, `root`.
39
-
40
- ## Node
41
-
42
- ```yaml
43
- - id: spawner-targets # REQUIRED. Unique across the scene incl. instanced prefabs.
44
- type: Group2D # REQUIRED for a plain node. Exact class name (see pix3-nodes).
45
- name: Spawner Targets # display name; free to change
46
- groups: [spawners] # optional runtime groups
47
- properties: # node properties — names from pix3-nodes
48
- width: 900
49
- height: 60
50
- transform:
51
- position: [0, 690]
52
- scale: [1, 1]
53
- rotation: 0
54
- components: # optional; script components
55
- - id: target-spawner
56
- type: user:Spawner
57
- enabled: true
58
- config:
59
- prefab: res://scenes/prefabs/target.pix3scene
60
- intervalSec: 0.85
61
- children: [] # optional; child nodes, same shape, recursive
62
- ```
63
-
64
- Rules:
65
-
66
- - **`id`**: short, readable, kebab-case, unique (`combo-label`, `spawner-bonus`). Two nodes
67
- with the same id fail the load. Never rename an id the recipe lists as stable — scripts
68
- find nodes by id; rename `name` instead.
69
- - **`type`**: must be a real node type. An unknown or misspelled type does **not** fail the
70
- load — the node becomes an inert placeholder that draws and does nothing.
71
- - **`properties`**: unknown keys are kept silently and do nothing; a value of the wrong type
72
- (a string where a number belongs) is silently replaced by the default. Check names and
73
- types against `pix3-nodes`.
74
- - **`children`**: tree order is 2D paint order — a later sibling, or a deeper node, draws on
75
- top. `zIndex` overrides it.
76
-
77
- ### 2D transform
78
-
79
- ```yaml
80
- transform:
81
- position: [x, y] # design pixels; the node's CENTRE, relative to its parent
82
- scale: [1, 1]
83
- rotation: 0 # DEGREES in YAML; positive = counter-clockwise (Y is up)
84
- ```
85
-
86
- 2D space: origin at the centre of the screen/parent, **X right, Y up**. The recipes use a
87
- 1080 x 1920 portrait design (top edge y = 960, bottom y = -960); other projects read
88
- `viewportBaseSize` / `projectType` in `pix3project.yaml` — a `3d` project may have loose
89
- top-level 2D nodes (a HUD) and no 2D root at all, so position new HUD relative to the HUD
90
- nodes that are already there. Scripts see the rotation in
91
- radians (`node.rotation.z`). `{ x: 0, y: 690 }` is also accepted for a vector, and so is a flat
92
- `position:` beside `transform` (read-compat), but write the `transform:` block the editor saves.
93
-
94
- 3D nodes (`Node3D`, meshes, cameras, lights) use
95
- `transform: { position: [x, y, z], rotationEuler: [dx, dy, dz], scale: [1, 1, 1] }`,
96
- rotation in degrees.
97
-
98
- ### Anchor layout (2D)
99
-
100
- ```yaml
101
- layout:
102
- enabled: true
103
- horizontalAlign: left # left | center | right | stretch
104
- verticalAlign: top # top | center | bottom | stretch
105
- ```
106
-
107
- The node keeps its authored distance to that edge of its parent when the parent resizes
108
- (a different screen aspect). `stretch` keeps both margins and resizes the node. Position is
109
- still authored in `transform.position`. HUD widgets anchor to screen edges; full-screen
110
- backgrounds and roots use `stretch` on both axes.
111
-
112
- **A new HUD node must not overlap the HUD that is already there.** Before you place it, read
113
- the `position`, `width`/`height` and anchoring of its siblings and pick a free spot (a combo
114
- label placed "top-right" once landed under the game's Shop button). With the live channel,
115
- confirm it with a `viewport_screenshot` while the game runs; without it, ask the human to look.
116
-
117
- Flow (stack children in a row/column) is a separate block:
118
- `flow: { enabled: true, direction: vertical, gap: 16, paddingX: 0, paddingY: 0, align: start, autoSize: false }`.
119
-
120
- The flat names `layoutEnabled`, `horizontalAlign`, `flowEnabled`, `flowGap` … are the
121
- inspector's names for the same values: on a plain node they belong in these blocks (flat they
122
- are ignored — `pix3 validate` says so); on an **instance** node they are the only form (below).
123
-
124
- ### Values
125
-
126
- - **Colour**: a quoted hex string, `color: "#141a2e"`. Always quote — an unquoted `#` starts
127
- a YAML comment and the value becomes empty.
128
- - **Asset path**: `res://` + path from the project root (the folder with `pix3project.yaml`),
129
- whatever the layout: `res://sprites/ph-target.png` in a recipe, `res://src/assets/textures/x.png`
130
- in a project that keeps assets under `src/`. Follow the folders the project already has; a
131
- new project from `pix3 new` uses one folder per type at the root (`sprites/`, `audio/`,
132
- `fonts/`, `models/`, `spine/`, `scripts/`, `scenes/`).
133
- - **Texture slot**: `texture: { type: 'texture', url: 'res://sprites/x.png' }` (same shape for
134
- `textureNormal`, `textureFill`, …). The saver always writes this form; a bare
135
- `texturePath: 'res://…'` string is only read for compatibility.
136
- - **Strings** with `:` `#` `{` or leading spaces: quote them (`label: "SCORE 0"`).
137
-
138
- ### Shader effects (`Sprite2D`, `AnimatedSprite2D`, `Button2D` only)
139
-
140
- ```yaml
141
- effects:
142
- - type: core:tint
143
- params: { color: "#7ee787", amount: 1 }
144
- ```
145
-
146
- `core:tint` multiplies a near-white placeholder PNG to the colour — this is how recipe
147
- placeholders get their palette. Also `core:adjust`, `core:grayscale`, `core:flash`,
148
- `core:uv-scroll`, `core:dissolve`. Other 2D nodes do not read `effects` (a `ColorRect2D` takes
149
- its colour from `color`). On a `GeometryMesh` the stack lives at `material.effects`.
150
-
151
- ## Component
152
-
153
- ```yaml
154
- - id: combo # readable id; keep it unique in the scene
155
- type: user:Combo # user:<ExportedClassName> (scripts/*.ts) or core:<Name>
156
- enabled: true
157
- config: # keys = names in the component's getPropertySchema()
158
- windowSec: 1.2
159
- ```
160
-
161
- - `config` is **merged over** the defaults the script's constructor sets, so write only the
162
- keys you want to differ.
163
- - A `user:X` whose class does not exist (or has no `static getPropertySchema()`) is kept as
164
- "pending" with a console warning and never runs.
165
- - Built-ins (`core:*`) and every config key they take, with defaults, are listed in
166
- `.claude/skills/pix3-scripts/reference.md` ("`core:` components").
167
-
168
- ## Prefab instance
169
-
170
- ```yaml
171
- - id: result-overlay
172
- name: Result Overlay
173
- instance: res://scenes/ui/result.pix3scene
174
- properties:
175
- visible: false
176
- transform: { position: [0, 0] }
177
- ```
178
-
179
- - `instance:` **replaces** `type:`. The instanced file must contain **exactly one** root node.
180
- - Instance `properties` are applied to the prefab's root through its **schema names**:
181
- `visible`, `opacity`, `width`, `layoutEnabled`, `horizontalAlign`, … plus a `transform` block
182
- (`position`, `rotation`, `scale`). The `layout:` / `flow:` blocks of a plain node are not read
183
- here — write `layoutEnabled: true` + `horizontalAlign: right` instead.
184
- - Do **not** add `components` or `children` to an instance node — they are ignored (the
185
- content comes from the prefab). To change what is inside, edit the prefab file itself, or
186
- override one inner node's properties:
187
-
188
- ```yaml
189
- overrides:
190
- byLocalId:
191
- retry-button: # an id below the prefab root; nested: parent-id/child-id
192
- properties: { label: "AGAIN" } # schema names, as on the instance itself
193
- ```
194
-
195
- - Nodes inside an instance keep their authored ids when those are unique, so scripts can find
196
- them by id (`retry-button` inside `scenes/ui/result.pix3scene` is found as `retry-button`).
197
- - A prefab carries fixed defaults only (e.g. its `core:Hitbox2D` group). Tunables live on
198
- plain nodes in the host scene.
199
- - Where prefabs live: `scenes/prefabs/` (the recipes) or a top-level `prefabs/` — the export
200
- treats both (and `scenes/ui/`) as prefabs, never as a boot scene.
201
- - Spawned at runtime with `await this.scene.instantiate('res://scenes/prefabs/x.pix3scene', { parent })`.
202
-
203
- ## Full-screen UI lives in `scenes/ui/`
204
-
205
- A result card, pause menu or modal is its own file in `scenes/ui/`, instanced into the host
206
- scene with `properties: { visible: false }`. Two different flags, both needed:
207
-
208
- - `visible: false` on the **instance** — hides it in the **editor**, so `main.pix3scene`
209
- opens on the game, not on a GAME OVER card. Never remove it.
210
- - `initiallyVisible: false|true` on the **overlay file's root node** — what **play mode**
211
- applies at start. A script reveals the overlay by setting `node.visible = true`.
212
-
213
- Never put a full-screen dimmer/panel inline in `main.pix3scene`.
214
-
215
- ## Recipe tool names → file edits
216
-
217
- `design/recipe.md` was written for the in-editor agent. Without its tools:
218
-
219
- | recipe.md says | you do |
220
- | --- | --- |
221
- | tunable with `component: "user:X"` | edit `config.<property>` of the component with `type: user:X` on the node with that `id` |
222
- | tunable without `component` | edit `properties.<property>` on the node with that `id` (in the `transform` / `layout` / `flow` block where it belongs) |
223
- | `set_property` | edit `properties` in the YAML |
224
- | `set_component_property` | edit the component's `config` |
225
- | `create_node` / `add_component` | add the node / component entry to the YAML |
226
- | `fs_write` (+ `overwrite: true`) | write the file |
227
- | `play_start`, `game_input`, `game_observe`, `game_run` | the same tools over the live channel when it is connected (`pix3-verify`); otherwise `pix3 check`, then `pix3 smoke <the scene you changed>`, then ask the human to press Play and tell them what to look for |
228
-
229
- Values out of a tunable's `min`/`max` load as written (`pix3 validate` warns); the script's
230
- schema `setValue` may clamp them.
231
-
232
- ## Known gaps
233
-
234
- - `metadata.description` is free text; nothing reads it but humans.
235
- - Instance overrides of **components** inside a prefab are not supported from the host scene.
1
+ ---
2
+ name: pix3-scene-format
3
+ description: The .pix3scene YAML format — file header, node shape (id/type/name/properties/children/components), component shape, 2D transform and layout blocks, res:// references, textures, prefab instances and overrides, scenes/ui overlays and the visible/initiallyVisible split, plus how to translate design/recipe.md tool names into file edits. Use BEFORE creating or editing any .pix3scene file (scene, prefab or overlay) in this project.
4
+ ---
5
+
6
+ <!-- Pix3 agent kit 1.6.2 -->
7
+ # `.pix3scene` format
8
+
9
+ Scenes, prefabs and overlays are the same file type: YAML with a `.pix3scene` extension.
10
+ "Prefab" only means "a scene file that other scenes instance". There is no `.pix3prefab`.
11
+ The engine's own statement of the format (principles, skinned UI controls, Spine nodes) is in
12
+ `reference.md` beside this file; where the two differ, this page describes what the loader and
13
+ `pix3 validate` actually do.
14
+
15
+ ## Before you write
16
+
17
+ - Re-read the file you are about to change — the human may have edited it in the editor.
18
+ - Change only the lines you mean to change. A whole-file rewrite from memory drops what the
19
+ human (or the editor) wrote since you last read it.
20
+ - If your tools support it, write to a temporary file and rename it over the target. Not
21
+ required for correctness — the editor waits for the file to stop changing — but cleaner.
22
+ - Then `pix3 check` (see `pix3-verify`).
23
+
24
+ ## File
25
+
26
+ ```yaml
27
+ version: 1.0.0 # the only format version; copy it
28
+ metadata: # optional, free-form
29
+ author: Pix3 Recipe
30
+ description: Tapper gameplay — falling objects
31
+ root: # list of root nodes; at least one
32
+ - id: game-root
33
+ type: Group2D
34
+ ...
35
+ ```
36
+
37
+ `#` lines are comments and may appear anywhere. Top-level keys are exactly `version`,
38
+ `description`, `metadata`, `root`.
39
+
40
+ ## Node
41
+
42
+ ```yaml
43
+ - id: spawner-targets # REQUIRED. Unique across the scene incl. instanced prefabs.
44
+ type: Group2D # REQUIRED for a plain node. Exact class name (see pix3-nodes).
45
+ name: Spawner Targets # display name; free to change
46
+ groups: [spawners] # optional runtime groups
47
+ properties: # node properties — names from pix3-nodes
48
+ width: 900
49
+ height: 60
50
+ transform:
51
+ position: [0, 690]
52
+ scale: [1, 1]
53
+ rotation: 0
54
+ components: # optional; script components
55
+ - id: target-spawner
56
+ type: user:Spawner
57
+ enabled: true
58
+ config:
59
+ prefab: res://scenes/prefabs/target.pix3scene
60
+ intervalSec: 0.85
61
+ children: [] # optional; child nodes, same shape, recursive
62
+ ```
63
+
64
+ Rules:
65
+
66
+ - **`id`**: short, readable, kebab-case, unique (`combo-label`, `spawner-bonus`). Two nodes
67
+ with the same id fail the load. Never rename an id the recipe lists as stable — scripts
68
+ find nodes by id; rename `name` instead.
69
+ - **`type`**: must be a real node type. An unknown or misspelled type does **not** fail the
70
+ load — the node becomes an inert placeholder that draws and does nothing.
71
+ - **`properties`**: unknown keys are kept silently and do nothing; a value of the wrong type
72
+ (a string where a number belongs) is silently replaced by the default. Check names and
73
+ types against `pix3-nodes`.
74
+ - **`children`**: tree order is 2D paint order — a later sibling, or a deeper node, draws on
75
+ top. `zIndex` overrides it.
76
+
77
+ ### 2D transform
78
+
79
+ ```yaml
80
+ transform:
81
+ position: [x, y] # design pixels; the node's CENTRE, relative to its parent
82
+ scale: [1, 1]
83
+ rotation: 0 # DEGREES in YAML; positive = counter-clockwise (Y is up)
84
+ ```
85
+
86
+ 2D space: origin at the centre of the screen/parent, **X right, Y up**. The recipes use a
87
+ 1080 x 1920 portrait design (top edge y = 960, bottom y = -960); other projects read
88
+ `viewportBaseSize` / `projectType` in `pix3project.yaml` — a `3d` project may have loose
89
+ top-level 2D nodes (a HUD) and no 2D root at all, so position new HUD relative to the HUD
90
+ nodes that are already there. Scripts see the rotation in
91
+ radians (`node.rotation.z`). `{ x: 0, y: 690 }` is also accepted for a vector, and so is a flat
92
+ `position:` beside `transform` (read-compat), but write the `transform:` block the editor saves.
93
+
94
+ 3D nodes (`Node3D`, meshes, cameras, lights) use
95
+ `transform: { position: [x, y, z], rotationEuler: [dx, dy, dz], scale: [1, 1, 1] }`,
96
+ rotation in degrees.
97
+
98
+ ### Anchor layout (2D)
99
+
100
+ ```yaml
101
+ layout:
102
+ enabled: true
103
+ horizontalAlign: left # left | center | right | stretch
104
+ verticalAlign: top # top | center | bottom | stretch
105
+ ```
106
+
107
+ The node keeps its authored distance to that edge of its parent when the parent resizes
108
+ (a different screen aspect). `stretch` keeps both margins and resizes the node. Position is
109
+ still authored in `transform.position`. HUD widgets anchor to screen edges; full-screen
110
+ backgrounds and roots use `stretch` on both axes.
111
+
112
+ **A new HUD node must not overlap the HUD that is already there.** Before you place it, read
113
+ the `position`, `width`/`height` and anchoring of its siblings and pick a free spot (a combo
114
+ label placed "top-right" once landed under the game's Shop button). With the live channel,
115
+ confirm it with a `viewport_screenshot` while the game runs; without it, ask the human to look.
116
+
117
+ Flow (stack children in a row/column) is a separate block:
118
+ `flow: { enabled: true, direction: vertical, gap: 16, paddingX: 0, paddingY: 0, align: start, autoSize: false }`.
119
+
120
+ The flat names `layoutEnabled`, `horizontalAlign`, `flowEnabled`, `flowGap` … are the
121
+ inspector's names for the same values: on a plain node they belong in these blocks (flat they
122
+ are ignored — `pix3 validate` says so); on an **instance** node they are the only form (below).
123
+
124
+ ### Values
125
+
126
+ - **Colour**: a quoted hex string, `color: "#141a2e"`. Always quote — an unquoted `#` starts
127
+ a YAML comment and the value becomes empty.
128
+ - **Asset path**: `res://` + path from the project root (the folder with `pix3project.yaml`),
129
+ whatever the layout: `res://sprites/ph-target.png` in a recipe, `res://src/assets/textures/x.png`
130
+ in a project that keeps assets under `src/`. Follow the folders the project already has; a
131
+ new project from `pix3 new` uses one folder per type at the root (`sprites/`, `audio/`,
132
+ `fonts/`, `models/`, `spine/`, `scripts/`, `scenes/`).
133
+ - **Texture slot**: `texture: { type: 'texture', url: 'res://sprites/x.png' }` (same shape for
134
+ `textureNormal`, `textureFill`, …). The saver always writes this form; a bare
135
+ `texturePath: 'res://…'` string is only read for compatibility.
136
+ - **Strings** with `:` `#` `{` or leading spaces: quote them (`label: "SCORE 0"`).
137
+
138
+ ### Shader effects (`Sprite2D`, `AnimatedSprite2D`, `Button2D` only)
139
+
140
+ ```yaml
141
+ effects:
142
+ - type: core:tint
143
+ params: { color: "#7ee787", amount: 1 }
144
+ ```
145
+
146
+ `core:tint` multiplies a near-white placeholder PNG to the colour — this is how recipe
147
+ placeholders get their palette. Also `core:adjust`, `core:grayscale`, `core:flash`,
148
+ `core:uv-scroll`, `core:dissolve`. Other 2D nodes do not read `effects` (a `ColorRect2D` takes
149
+ its colour from `color`). On a `GeometryMesh` the stack lives at `material.effects`.
150
+
151
+ ## Component
152
+
153
+ ```yaml
154
+ - id: combo # readable id; keep it unique in the scene
155
+ type: user:Combo # user:<ExportedClassName> (scripts/*.ts) or core:<Name>
156
+ enabled: true
157
+ config: # keys = names in the component's getPropertySchema()
158
+ windowSec: 1.2
159
+ ```
160
+
161
+ - `config` is **merged over** the defaults the script's constructor sets, so write only the
162
+ keys you want to differ.
163
+ - A `user:X` whose class does not exist (or has no `static getPropertySchema()`) is kept as
164
+ "pending" with a console warning and never runs.
165
+ - Built-ins (`core:*`) and every config key they take, with defaults, are listed in
166
+ `.claude/skills/pix3-scripts/reference.md` ("`core:` components").
167
+
168
+ ## Prefab instance
169
+
170
+ ```yaml
171
+ - id: result-overlay
172
+ name: Result Overlay
173
+ instance: res://scenes/ui/result.pix3scene
174
+ properties:
175
+ visible: false
176
+ transform: { position: [0, 0] }
177
+ ```
178
+
179
+ - `instance:` **replaces** `type:`. The instanced file must contain **exactly one** root node.
180
+ - The instance node's `name` **replaces the prefab root's name** in the loaded scene (its `id`
181
+ stays the instance's own id). `findByName('Result Overlay')` finds the instance above, not
182
+ the name written in `result.pix3scene`; omit `name` to keep the prefab's.
183
+ - Instance `properties` are applied to the prefab's root through its **schema names**:
184
+ `visible`, `opacity`, `width`, `layoutEnabled`, `horizontalAlign`, … plus a `transform` block
185
+ (`position`, `rotation`, `scale`). The `layout:` / `flow:` blocks of a plain node are not read
186
+ here — write `layoutEnabled: true` + `horizontalAlign: right` instead.
187
+ - Do **not** add `components` or `children` to an instance node — they are ignored (the
188
+ content comes from the prefab). To change what is inside, edit the prefab file itself, or
189
+ override one inner node's properties:
190
+
191
+ ```yaml
192
+ overrides:
193
+ byLocalId:
194
+ retry-button: # an id below the prefab root; nested: parent-id/child-id
195
+ properties: { label: "AGAIN" } # schema names, as on the instance itself
196
+ ```
197
+
198
+ - Nodes inside an instance keep their authored ids when those are unique, so scripts can find
199
+ them by id (`retry-button` inside `scenes/ui/result.pix3scene` is found as `retry-button`).
200
+ - A prefab carries fixed defaults only (e.g. its `core:Hitbox2D` group). Tunables live on
201
+ plain nodes in the host scene.
202
+ - Where prefabs live: `scenes/prefabs/` (the recipes) or a top-level `prefabs/` — the export
203
+ treats both (and `scenes/ui/`) as prefabs, never as a boot scene.
204
+ - Spawned at runtime with `await this.scene.instantiate('res://scenes/prefabs/x.pix3scene', { parent })`.
205
+
206
+ ## Full-screen UI lives in `scenes/ui/`
207
+
208
+ A result card, pause menu or modal is its own file in `scenes/ui/`, instanced into the host
209
+ scene with `properties: { visible: false }`. Two different flags, both needed:
210
+
211
+ - `visible: false` on the **instance** — hides it in the **editor**, so `main.pix3scene`
212
+ opens on the game, not on a GAME OVER card. Never remove it.
213
+ - `initiallyVisible: false|true` on the **overlay file's root node** — what **play mode**
214
+ applies at start. A script reveals the overlay by setting `node.visible = true`.
215
+
216
+ Never put a full-screen dimmer/panel inline in `main.pix3scene`.
217
+
218
+ ## Recipe tool names → file edits
219
+
220
+ `design/recipe.md` was written for the in-editor agent. Without its tools:
221
+
222
+ | recipe.md says | you do |
223
+ | --- | --- |
224
+ | tunable with `component: "user:X"` | edit `config.<property>` of the component with `type: user:X` on the node with that `id` |
225
+ | tunable without `component` | edit `properties.<property>` on the node with that `id` (in the `transform` / `layout` / `flow` block where it belongs) |
226
+ | `set_property` | edit `properties` in the YAML |
227
+ | `set_component_property` | edit the component's `config` |
228
+ | `create_node` / `add_component` | add the node / component entry to the YAML |
229
+ | `fs_write` (+ `overwrite: true`) | write the file |
230
+ | `play_start`, `game_input`, `game_observe`, `game_run` | the same tools over the live channel when it is connected (`pix3-verify`); otherwise `pix3 check`, then `pix3 smoke <the scene you changed>`, then ask the human to press Play and tell them what to look for |
231
+
232
+ Values out of a tunable's `min`/`max` load as written (`pix3 validate` warns); the script's
233
+ schema `setValue` may clamp them.
234
+
235
+ ## Known gaps
236
+
237
+ - `metadata.description` is free text; nothing reads it but humans.
238
+ - Instance overrides of **components** inside a prefab are not supported from the host scene.
@@ -1,14 +1,14 @@
1
- <!-- Pix3 agent kit 1.6.0 — generated from docs/pix3-specification.md ("Scene File Format"). Do not edit: pix3 kit --update regenerates it. -->
2
- # `.pix3scene` — the engine specification
3
-
4
- Excerpts of the engine's specification, as shipped with this CLI. `SKILL.md` beside this file
5
- is the practical version and wins where they differ — in particular node ids may be readable
6
- kebab-case, and there are no scene-format migrations today (`version: 1.0.0` is the only one).
7
- The "Flat Asset Layout" principle is what `pix3 new` creates; an existing project keeps its own
8
- layout (`res://` is always relative to the project root). Source paths quoted here (`src/…`, `packages/…`) are files of the Pix3 engine repository, not of this project.
9
-
10
- ## Key principles
11
-
1
+ <!-- Pix3 agent kit 1.6.2 — generated from docs/pix3-specification.md ("Scene File Format"). Do not edit: pix3 kit --update regenerates it. -->
2
+ # `.pix3scene` — the engine specification
3
+
4
+ Excerpts of the engine's specification, as shipped with this CLI. `SKILL.md` beside this file
5
+ is the practical version and wins where they differ — in particular node ids may be readable
6
+ kebab-case, and there are no scene-format migrations today (`version: 1.0.0` is the only one).
7
+ The "Flat Asset Layout" principle is what `pix3 new` creates; an existing project keeps its own
8
+ layout (`res://` is always relative to the project root). Source paths quoted here (`src/…`, `packages/…`) are files of the Pix3 engine repository, not of this project.
9
+
10
+ ## Key principles
11
+
12
12
  - Declarative: The file describes the composition and structure of the scene, not the process of its creation.
13
13
  - Asset Referencing: Assets (models, textures) are not embedded in the file but are referenced via relative paths with a res:// prefix (path from the project root).
14
14
  - Flat Asset Layout: a game project keeps one folder per asset type **at its root** — `scenes/`, `sprites/` (images/textures), `models/`, `audio/`, `fonts/`, `spine/` (skeleton + atlas + pages together), `scripts/`, `locales/` — with free subdivision inside (`sprites/ui/…`). There is no `assets/` wrapper folder; the project templates ship this layout and `defaultAssetFolder()` (`src/core/asset-categories.ts`) is the canonical mapping used when the editor creates an asset from a bare file name.
@@ -18,10 +18,10 @@ layout (`res://` is always relative to the project root). Source paths quoted he
18
18
  - Unique Identification: Every node must have an id field. The value is a short, cryptographically secure unique identifier (similar to Nano ID) to provide a balance between file readability and the absolute reliability of references.
19
19
  - Versioned Schema: Each file includes a `version` field; migrations are maintained in the SceneManager and run automatically on load.
20
20
  - Conflict Resolution: Instance overrides always win over parent definitions. Duplicate IDs trigger validation errors during import.
21
- - Forgiving Vocabulary: a `type:` the loader does not know does **not** fail the load — the node is built as a bare `NodeBase` placeholder that does nothing, so a scene written by a newer version (or with a typo) stays openable and editable. Because such a node is otherwise indistinguishable from a working one, it is reported on three surfaces: the renderability lint (`inert-nodes` in `scene_tree` / `game_observe` / play start), the loader diagnostic (`describeUnknownNodeType`, which suggests the nearest known name), and the Scene Tree row, which carries a warn badge and states the reason in its tooltip. `isInertNode` (`packages/pix3-runtime/src/core/renderability-lint.ts`) is the one predicate behind all three.
22
-
23
- ## Skinned 2D UI controls
24
-
21
+ - Forgiving Vocabulary: a `type:` the loader does not know does **not** fail the load — the node is built as a bare `NodeBase` placeholder that does nothing, so a scene written by a newer version (or with a typo) stays openable and editable. Because such a node is otherwise indistinguishable from a working one, it is reported on three surfaces: the renderability lint (`inert-nodes` in `scene_tree` / `game_observe` / play start), the loader diagnostic (`describeUnknownNodeType`, which suggests the nearest known name), and the Scene Tree row, which carries a warn badge and states the reason in its tooltip. `isInertNode` (`packages/pix3-runtime/src/core/renderability-lint.ts`) is the one predicate behind all three.
22
+
23
+ ## Skinned 2D UI controls
24
+
25
25
  `Button2D`, `Checkbox2D`, `Slider2D` and `Bar2D` are colour-driven by default and
26
26
  skinned by pointing their texture slots at `res://` sprites. A slot that is set
27
27
  replaces the corresponding flat colour (the material tint goes white); a slot that
@@ -92,10 +92,10 @@ in **§6.23 UI Kit Assets**.
92
92
  textureBox: { type: 'texture', url: 'res://sprites/ui/check_off.png' }
93
93
  textureBoxChecked: { type: 'texture', url: 'res://sprites/ui/check_on.png' }
94
94
  textureMark: { type: 'texture', url: 'res://sprites/ui/check_tick.png' }
95
- ```
96
-
97
- ## Spine skeleton nodes
98
-
95
+ ```
96
+
97
+ ## Spine skeleton nodes
98
+
99
99
  A `SpineSkeleton2D` references its Spine export by two `res://` paths — the
100
100
  skeleton (`.json` or `.skel`) and the atlas (`.atlas`). The atlas' page images are
101
101
  NOT listed: they are named inside the atlas text and resolved relative to it (the
@@ -124,10 +124,10 @@ optional single-page override.
124
124
 
125
125
  Rendering requires the optional `@esotericsoftware/spine-threejs` (`~4.3`) module,
126
126
  registered by the host through `setSpineModuleLoader`. A scene with a Spine node
127
- still loads without it — the node keeps its authored properties and warns.
128
-
129
- ## Groups
130
-
127
+ still loads without it — the node keeps its authored properties and warns.
128
+
129
+ ## Groups
130
+
131
131
  Groups provide runtime categorization for nodes (for example, `enemies`, `ui`, `interactables`).
132
132
 
133
133
  #### 6.19.1 Node API
@@ -155,10 +155,10 @@ root:
155
155
  type: Node3D
156
156
  name: Player
157
157
  groups: [actors, player]
158
- ```
159
-
160
- ## Prefabs — instance creation
161
-
158
+ ```
159
+
160
+ ## Prefabs — instance creation
161
+
162
162
  Creating a prefab instance uses the `instance:` YAML key:
163
163
 
164
164
  ```yaml
@@ -173,4 +173,4 @@ root:
173
173
  position: { x: 0, y: 0, z: 0 }
174
174
  ```
175
175
 
176
- The `properties` block allows overriding base prefab values. Overrides are tracked separately from the base values.
176
+ The `properties` block allows overriding base prefab values. Overrides are tracked separately from the base values.