@volter/project 0.5.210 → 0.5.212

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 (52) hide show
  1. package/NOTICE +2 -2
  2. package/README.md +8 -15
  3. package/package.json +5 -26
  4. package/schemas/volter-project.schema.json +33 -145
  5. package/schemas/volter-settings.schema.json +11 -49
  6. package/src/adapter/adapter-module.ts +204 -465
  7. package/src/adapter/adapter-surface.ts +4 -32
  8. package/src/adapter/authoring-seam-contract.ts +7 -24
  9. package/src/adapter/authoring.ts +126 -265
  10. package/src/adapter/editor-looks.ts +96 -127
  11. package/src/adapter/finders/registry.ts +6 -21
  12. package/src/adapter/host-context.ts +17 -72
  13. package/src/adapter/index.ts +4 -77
  14. package/src/adapter/manifest-interpreter.ts +7 -50
  15. package/src/adapter/physics-adapter-2d.ts +4 -21
  16. package/src/adapter/root-adapter.ts +37 -111
  17. package/src/adapter/root-seam-contract.ts +3 -6
  18. package/src/adapter/root-stacking.ts +4 -46
  19. package/src/adapter/seam-evidence.ts +0 -16
  20. package/src/adapter/system-adapter.ts +24 -656
  21. package/src/adapter/system-seam-contract.ts +1 -91
  22. package/src/adapter/transform.ts +4 -3
  23. package/src/adapter/workspace-arrangement.ts +29 -49
  24. package/src/manifest/editor-port.ts +10 -36
  25. package/src/manifest/filename.ts +3 -42
  26. package/src/manifest/load-file.ts +9 -15
  27. package/src/manifest/load.ts +44 -179
  28. package/src/manifest/locate.ts +6 -29
  29. package/src/manifest/schema.ts +155 -427
  30. package/src/semver.ts +16 -0
  31. package/src/settings/keys.ts +9 -11
  32. package/src/settings/schema.ts +55 -44
  33. package/schemas/engine-api.json +0 -57
  34. package/schemas/engine-api.md +0 -22
  35. package/src/adapter/binding.ts +0 -445
  36. package/src/adapter/debug-registry-ref.ts +0 -19
  37. package/src/adapter/frame-capture.ts +0 -80
  38. package/src/adapter/ingest/contract-system-slots.ts +0 -28
  39. package/src/adapter/ingest/game-contract-seams.ts +0 -86
  40. package/src/adapter/ingest/game-contract.ts +0 -506
  41. package/src/adapter/ingest/mount-readiness.ts +0 -140
  42. package/src/adapter/loop-gate-report.ts +0 -53
  43. package/src/adapter/native-entry-surface.ts +0 -100
  44. package/src/adapter/render-audio.ts +0 -20
  45. package/src/adapter/render-memory.ts +0 -34
  46. package/src/adapter/renderer-config.ts +0 -84
  47. package/src/adapter/system-slot.ts +0 -301
  48. package/src/core/system-phase.ts +0 -52
  49. package/src/inspection.ts +0 -281
  50. package/src/manifest/configuration-kinds.ts +0 -241
  51. package/src/manifest/kind-modules.ts +0 -58
  52. package/src/manifest/runtime-environment.ts +0 -48
@@ -1,25 +1,23 @@
1
1
  /**
2
2
  * THE LOOK POINTS — a workspace layout, a keymap and a style bundle as DATA a
3
- * package contributes (WORKBENCH.md §Contribution points: "Layouts, keymaps
3
+ * package contributes (keymaps
4
4
  * and styles are DATA points: they need no code to load, which is what makes
5
- * a 'styled build' a manifest choice rather than a code change").
5
+ * a styled build a manifest choice).
6
6
  *
7
7
  * A contribution module declares its point and exports ONE object:
8
8
  *
9
- * `model.layout.ts` export const point = 'workspace.layout'; export const layout: WorkspaceLayoutContribution
10
- * `blender.keymap.ts` export const point = 'workspace.keymap'; export const keymap: KeymapContribution
11
- * `blender.style.ts` export const point = 'workspace.style'; export const style: StyleContribution
9
+ * `<name>.layout.ts` export const point = 'workspace.layout'; export const layout: WorkspaceLayoutContribution
10
+ * `<name>.keymap.ts` export const point = 'workspace.keymap'; export const keymap: KeymapContribution
11
+ * `<name>.style.ts` export const point = 'workspace.style'; export const style: StyleContribution
12
12
  *
13
- * The editor registers what a project's declared packages contribute beside
14
- * its own built-ins — Look, Animate and Design as workspaces, the volter keymap,
15
- * the Classic/Glass/Maya/Substance bundles — and never asks which package is
16
- * open: a build without the Blender look has no Model workspace.
13
+ * The editor registers the layouts, keymaps and styles declared by product and project
14
+ * packages. A composition offers only what its installed packages contribute.
17
15
  */
18
16
  import type { WorkspaceArrangement } from './workspace-arrangement';
19
17
 
20
18
  /**
21
19
  * Every keyboard action the editor's chrome binds. A keymap binds these and
22
- * nothing else; a game never reads this table (play-mode input isolation is a
20
+ * nothing else; a project never reads this table (play-mode input isolation is a
23
21
  * different mechanism).
24
22
  */
25
23
  export type EditorKeyActionId =
@@ -52,10 +50,8 @@ export type EditorKeyActionId =
52
50
  | 'view.focusMode'
53
51
  | 'workspace.cycleNext'
54
52
  | 'workspace.cyclePrevious'
55
- /** Arm the tool that draws NO transform gizmo — Blender's Select Box, whose
56
- * 3D View keymap puts it on `W` (`blender_default.py:1950-1955`,
57
- * `op_tool_cycle("builtin.select_box", …)`). This editor's own keymap binds
58
- * it to nothing: W/E/R/T are its four transform tools. */
53
+ /** Arm the tool that draws NO transform gizmo, a box-select tool. This editor's
54
+ * own keymap binds it to nothing: W/E/R/T are its four transform tools. */
59
55
  | 'transform.select'
60
56
  | 'transform.combined'
61
57
  | 'transform.translate'
@@ -76,7 +72,7 @@ export type EditorKeyActionId =
76
72
  | 'view.back'
77
73
  | 'view.left'
78
74
  /** Step the view: orbit 15° about the world's up or the view's horizon, turn to the opposite
79
- * side, or roll 15° (Blender's `view3d.view_orbit` and `view3d.view_roll` on the numpad). */
75
+ * side, or roll 15°. */
80
76
  | 'view.orbitLeft'
81
77
  | 'view.orbitRight'
82
78
  | 'view.orbitUp'
@@ -84,14 +80,13 @@ export type EditorKeyActionId =
84
80
  | 'view.opposite'
85
81
  | 'view.rollLeft'
86
82
  | 'view.rollRight'
87
- /** Frame everything in the view, selected or not (Blender's `view3d.view_all`, Home). */
83
+ /** Frame everything in the view, selected or not. */
88
84
  | 'view.all'
89
- /** Step the view nearer or farther by the keymap's zoom step (`view3d.zoom`, numpad +/-). */
85
+ /** Step the view nearer or farther by the keymap's zoom step. */
90
86
  | 'view.zoomIn'
91
87
  | 'view.zoomOut'
92
88
  | 'view.perspective'
93
- /** Switch the view between perspective and orthographic, keeping where it looks from
94
- * (Blender's `view3d.view_persportho`, numpad 5). */
89
+ /** Switch the view between perspective and orthographic, keeping where it looks from. */
95
90
  | 'view.projection'
96
91
  | 'view.camera';
97
92
 
@@ -105,51 +100,51 @@ export interface KeyChord {
105
100
  readonly key: string;
106
101
  readonly code?: string;
107
102
  readonly mod?: boolean;
108
- /** The Control key itself on every platform, macOS included (Blender's Ctrl), where `mod`
109
- * would be ⌘. */
103
+ /** The Control key itself on every platform, macOS included, where `mod` would be ⌘. */
110
104
  readonly ctrl?: boolean;
111
105
  readonly shift?: boolean;
112
106
  readonly alt?: boolean;
113
107
  }
114
108
 
115
- /** The chords a keymap binds for the actions it names. An action a keymap
116
- * omits keeps the editor's own binding; an EMPTY list unbinds it. */
117
- export type KeymapBindings = Partial<Record<EditorKeyActionId, readonly KeyChord[]>>;
118
-
109
+ /**
110
+ * A KEYMAP, named. Its chords are not here: they are its package's
111
+ * `contributes.keybindings`, gated on `volter.keymap == '<id>'`, over the
112
+ * editor's own (the built-in `volter-editor` extension). This names the keymap
113
+ * for the `keymap` setting and states what a keybinding cannot: the viewport's
114
+ * mouse.
115
+ */
119
116
  export interface KeymapContribution {
120
117
  /** The `keymap` settings value that selects it. */
121
118
  readonly id: string;
122
119
  readonly title: string;
123
120
  readonly description: string;
124
- readonly bindings: KeymapBindings;
125
121
  /** Which mouse button orbits the viewport. Absent: the editor's own. */
126
122
  readonly navigation?: KeymapNavigation;
127
123
  }
128
124
 
129
125
  /**
130
126
  * THE VIEWPORT'S MOUSE, as a keymap states it: the button that orbits. The editor's own orbits
131
- * with the right button and pans with the middle; a keymap that orbits with the middle (Blender's
132
- * `view3d.rotate` on MIDDLEMOUSE) pans with Shift and the same button, as that keymap does.
127
+ * with the right button and pans with the middle; a keymap that orbits with the middle pans with
128
+ * Shift and the same button.
133
129
  */
134
130
  export interface KeymapNavigation {
135
131
  readonly orbit: 'middle' | 'right';
136
132
  /**
137
- * The orbit as a TURNTABLE that keeps the view's roll and may pass over the top (Blender's
138
- * `view3d.rotate` in Turntable mode): a sideways drag spins the view about the world's up, a
133
+ * The orbit as a TURNTABLE that keeps the view's roll and may pass over the top: a sideways
134
+ * drag spins the view about the world's up, a
139
135
  * vertical one pitches it about the horizon, each at this angle per CSS pixel. Absent: the
140
136
  * editor's own orbit, which holds the view level and stops at the poles.
141
137
  */
142
138
  readonly turntable?: { readonly degreesPerPixel: number };
143
139
  /**
144
- * An orthographic view still down an axis turns perspective when a rotate starts, as Blender's
145
- * Auto Perspective does (`ED_view3d_persp_ensure`); the axis views themselves are orthographic.
140
+ * An orthographic view still down an axis turns perspective when a rotate starts; the axis
141
+ * views themselves are orthographic.
146
142
  */
147
143
  readonly autoPerspective?: boolean;
148
144
  /**
149
- * ZOOM: `step` is the distance factor one zoom key moves by (Blender's `view_zoom_apply_step`,
150
- * 1.2); `drag: 'dolly'` makes a zoom drag Blender's Dolly style (`viewzoom_scale_value`: the
151
- * distance scales by `2 · (len / len₀ − 1) + 1`, `len` the pointer's height below the region's
152
- * top plus 5). Absent: the editor's own.
145
+ * ZOOM: `step` is the distance factor one zoom key moves by; `drag: 'dolly'` makes a zoom drag
146
+ * a dolly (the distance scales by `2 · (len / len₀ − 1) + 1`, `len` the pointer's height below
147
+ * the region's top plus 5). Absent: the editor's own.
153
148
  */
154
149
  readonly zoom?: { readonly step: number; readonly drag?: 'dolly' };
155
150
  }
@@ -164,12 +159,11 @@ export interface WorkspaceLayoutContribution {
164
159
  * `'mounts'` (at least one root that plays) or a document kind the
165
160
  * project's table lists. Omitted: every project. */
166
161
  readonly requires?: 'mounts' | { readonly documentKind: string };
167
- /** Which chrome regions the workspace shows; absent means every region. */
162
+ /** Chrome region overrides; omitted regions use their defaults, including hidden workspace tabs. */
168
163
  readonly regions?: WorkspaceLayoutRegions;
169
164
  /** The workspace's EDITOR AREAS beside the centre document — a second
170
- * editor group holding a `workspace.document` contribution, the shape
171
- * Blender's own screens have (`WorkspaceAreaContribution`). Absent: the
172
- * centre document alone. */
165
+ * editor group holding a `workspace.document` contribution
166
+ * (`WorkspaceAreaContribution`). Absent: the centre document alone. */
173
167
  readonly areas?: WorkspaceArrangement['areas'];
174
168
  }
175
169
 
@@ -220,8 +214,8 @@ export interface DensityContribution {
220
214
  readonly '2xl'?: number;
221
215
  readonly heading?: number;
222
216
  };
223
- /** Glyph sizes, independent of the type scale: Blender's chrome glyphs are
224
- * 14 px whatever the text around them measures. */
217
+ /** Glyph sizes, independent of the type scale, so a look's chrome glyphs keep
218
+ * their size whatever the text around them measures. */
225
219
  readonly icon?: {
226
220
  readonly xs?: number;
227
221
  readonly sm?: number;
@@ -236,8 +230,8 @@ export interface DensityContribution {
236
230
  readonly localToolbar?: number;
237
231
  readonly treeRow?: number;
238
232
  /** How far one level of a tree steps right. Absent keeps the editor's own
239
- * 14; Blender's Outliner steps by its ROW HEIGHT — a square grid, 20 —
240
- * with the chevron owning one cell and the type glyph the next. */
233
+ * 14; a look that sets it to `treeRow` steps by the row's height — a
234
+ * square grid, with the chevron owning one cell and the type glyph the next. */
241
235
  readonly treeIndent?: number;
242
236
  readonly statusBar?: number;
243
237
  /** The tool rail: a tool's box, its width, the gap between groups. */
@@ -245,15 +239,26 @@ export interface DensityContribution {
245
239
  readonly toolWidth?: number;
246
240
  readonly toolGap?: number;
247
241
  /** How wide the seam between two docked AREAS is cut, in px. Absent
248
- * keeps the editor's own hairline; Blender's groove measures 3.5. The
249
- * colour is the palette's `color.boundary.area`. */
242
+ * keeps the editor's own hairline. The colour is the palette's
243
+ * `color.boundary.area`. */
250
244
  readonly areaSeam?: number;
251
245
  /** THE EMBOSS beside that groove, as a percentage of WHITE mixed into the
252
246
  * area's OWN fill — so it is one number and every area computes its own
253
- * colour from it. Absent (or 0) paints nothing, which is what every skin
254
- * but Blender does; Blender's measures 8.2. See `theme.ts`'s
255
- * `areaEmbossValue` for the five fills it was solved against. */
247
+ * colour from it. Absent (or 0) paints nothing, the editor's own. See
248
+ * `theme.ts`'s `areaEmbossValue` for how each fill computes it. */
256
249
  readonly areaEmboss?: number;
250
+ /** The viewport navigation cluster's button column, in px: every control
251
+ * height inside the cluster. Absent keeps the editor's own 28. */
252
+ readonly navigationControl?: number;
253
+ /** The height of a tree row's disclosure caret, in px. Absent keeps the
254
+ * editor's own 18. */
255
+ readonly treeCaret?: number;
256
+ /** The corner of the plate a tree draws behind the active row's type glyph
257
+ * and the edited datablock's glyph, in px. Absent keeps the editor's own 5. */
258
+ readonly treeGlyphPlateRadius?: number;
259
+ /** The opacity a tree composites its type and datablock glyphs at, as a
260
+ * fraction from 0 to 1. Absent keeps the editor's own 0.8. */
261
+ readonly treeGlyphOpacity?: number;
257
262
  };
258
263
  }
259
264
 
@@ -261,55 +266,40 @@ export interface DensityContribution {
261
266
  * WHAT A LOOK STATES ABOUT THE 3D STAGE, as opposed to about the chrome around it: how the
262
267
  * stage's furniture is drawn (sizes, widths, the selection box's form). Absent members keep
263
268
  * the editor's own. What the stage DOES — its boot tool, its box-select rule, its world's up
264
- * axis — is its presentation's (`@volter/sdk/kit/viewport-presentation`), never the
269
+ * axis — is its presentation's (`@volter/sdk/viewport-presentation`), never the
265
270
  * look's (ARCHITECTURE.md rule 7).
266
271
  */
267
272
  export interface StageContribution {
268
273
  /**
269
- * THE TRANSFORM GIZMO'S SCREEN SIZE, in px per gizmo unit — Blender's
270
- * `U.gizmo_size` (`DNA_userdef_types.h:1095`, default 75; Preferences ▸
271
- * Viewport ▸ Gizmos ▸ Size, `rna_userdef.cc:5415-5419`).
274
+ * THE TRANSFORM GIZMO'S SCREEN SIZE, in px per gizmo unit.
272
275
  *
273
- * A gizmo is a CONSTANT SCREEN SIZE, never a fraction of the object or of
274
- * the viewport: `wm_gizmo.cc:450-474` computes
275
- * `scale_final = scale_basis · UI_SCALE_FAC · U.gizmo_size ·
276
- * ED_view3d_pixel_size_no_ui_scale(rv3d, origin)`, and that last term is
277
- * world units per device pixel while `UI_SCALE_FAC` is device pixels per
278
- * UI pixel — so one gizmo unit is exactly `U.gizmo_size` CSS px at any
276
+ * Set, the gizmo is a CONSTANT SCREEN SIZE, never a fraction of the object
277
+ * or of the viewport: one gizmo unit is exactly this many CSS px at any
279
278
  * display scale.
280
279
  *
281
280
  * Absent keeps the editor's own gizmo, which is a fraction of the
282
- * VIEWPORT (three's `TransformControls` at `size` 1). What the number
283
- * means on our side — three's handles are authored on a unit half
284
- * Blender's — is `editor-viewport.ts`'s to know, and it says so there.
281
+ * VIEWPORT (the stage gizmo at `size` 1). How the number maps
282
+ * onto the stage gizmo's handles is `editor-viewport.ts`'s to know, and it
283
+ * says so there.
285
284
  */
286
285
  readonly gizmoSize?: number;
287
286
  /**
288
- * THE GIZMO'S HANDLES AT REST AND HIGHLIGHTED. `gizmoOpacity` is a resting handle's opacity
289
- * (a highlighted one is opaque): Blender's 0.6 (`transform_gizmo_3d.cc`,
290
- * `gizmo_get_axis_color`), Godot's 0.9 (`editors/3d/manipulator_gizmo_opacity`), Unity's
291
- * 0.93 (`Handles.cs`, the axis colours' alpha). A handle with no fixed highlight colour
292
- * (`color.gizmo.hover`) highlights in its own axis colour with its saturation multiplied by
293
- * `gizmoHighlightSaturation` and its value set to `gizmoHighlightValue`, each kept when
294
- * absent: Blender keeps both, Godot's are 0.25 and 1 (`node_3d_editor_plugin.cpp`,
295
- * `col.from_hsv(col.get_h(), col.get_s() * 0.25, 1.0, 1)`).
287
+ * Resting handle opacity; highlighted handles are opaque. When no fixed hover
288
+ * color is supplied, highlight uses the axis hue with the optional saturation
289
+ * multiplier and value override. Omitted values retain the axis color.
296
290
  */
297
291
  readonly gizmoOpacity?: number;
298
292
  /**
299
- * THE MOVE ARROWS' SHAPE: `gizmoArrowLength` is how far an arrow's tip stands from the
300
- * centre, in rotation-ring radii (three's own 1.2; Godot's 1.6, measured on
301
- * `tuto_3d5.png` as a 142 px tip against a 90 px ring), and `gizmoArrowHead` scales its
302
- * head (three's own 1; Godot's about 1.5). Which handles the tool offers is the view's
303
- * (`interaction.transformHandles`), never the look's.
293
+ * Move-arrow length in rotation-ring radii and arrow-head scale. The view’s
294
+ * interaction.transformHandles chooses which handles are available.
304
295
  */
305
296
  readonly gizmoArrowLength?: number;
306
297
  readonly gizmoArrowHead?: number;
307
- /** THE ROTATION RINGS' THICKNESS, as a multiple of three's own (Godot's rings are about
308
- * twice as thick, `tuto_3d5.png`). */
298
+ /** THE ROTATION RINGS' THICKNESS, as a multiple of the stage gizmo's own (Godot's rings
299
+ * are about twice as thick, `tuto_3d5.png`). */
309
300
  readonly gizmoRingWidth?: number;
310
301
  /**
311
- * THE NAVIGATION GIZMO'S FORM AND CORNER: lettered `balls` (the editor's own, Blender's and
312
- * Godot's), `cones` round a centre cube (Unity's scene gizmo) or a lettered axis `triad`
302
+ * THE NAVIGATION GIZMO'S FORM AND CORNER: lettered `balls` (the editor's own and Godot's), `cones` round a centre cube (Unity's scene gizmo) or a lettered axis `triad`
313
303
  * (Unreal's); `top-right` (the editor's own) or `bottom-left` (Unreal's). Whether a click on
314
304
  * it turns the view is the view's (`overlays.navigation`).
315
305
  */
@@ -324,17 +314,13 @@ export interface StageContribution {
324
314
  * THE FLOOR GRID'S LINES, in device pixels: the minor lines' width, the major lines' width,
325
315
  * and how far the major lines' colour is carried past the minor's from the backdrop (1 draws
326
316
  * both levels alike). A look that states none keeps the editor's own floor, one hairline
327
- * level — the grid the kit drew before its floor was fitted to Blender. Blender's are 1.5,
328
- * 2.25 and 39/21, measured across one scanline of `modeling-object-none.png`: the 1 m line
329
- * 4 px at half rise plateauing at 83, the 10 m line 6 px plateauing at 101
330
- * (`docs/VIEWPORT-STAGE.md`: widths are the look's, like colours).
317
+ * level (`docs/VIEWPORT-STAGE.md`: widths are the look's, like colours).
331
318
  */
332
319
  readonly gridLineWidth?: number;
333
320
  readonly gridMajorWidth?: number;
334
321
  readonly gridMajorContrast?: number;
335
- /** THE AXIS LINES' WIDTH in device pixels (the editor's own and Blender's are 2, measured
336
- * on `modeling-object-none.png`). Which axis lines show is the view's
337
- * (`overlays.axes`), never the look's. */
322
+ /** THE AXIS LINES' WIDTH in device pixels (the editor's own is 2). Which axis lines show is
323
+ * the view's (`overlays.axes`), never the look's. */
338
324
  readonly axisLineWidth?: number;
339
325
  /**
340
326
  * HOW THE SELECTION BOX MARK IS DRAWN (the view's `overlays.selection.box`): `corners`, the
@@ -347,9 +333,9 @@ export interface StageContribution {
347
333
  readonly selectionBoxFrame?: 'world' | 'object';
348
334
  /**
349
335
  * THE SELECTION OUTLINE'S FORM: `soft`, the editor's own blurred halo, or `crisp`, a hard
350
- * line `outlineWidth` device pixels wide (Blender's, Unity's and Unreal's are crisp); and
351
- * whether the parts of the selection other objects hide are drawn too (`outlineHidden`,
352
- * the editor's own; Unity's and Blender's are not).
336
+ * line `outlineWidth` device pixels wide (Unity's and Unreal's are crisp); and whether the
337
+ * parts of the selection other objects hide are drawn too (`outlineHidden`, the editor's
338
+ * own; Unity's are not).
353
339
  */
354
340
  readonly outlineStyle?: 'soft' | 'crisp';
355
341
  readonly outlineWidth?: number;
@@ -361,10 +347,8 @@ export interface StageContribution {
361
347
  /**
362
348
  * THE BOX-SELECT OUTLINE'S DASH, in device pixels: each dash this long in the palette's
363
349
  * `color.viewport.marqueeLine`, each gap as long in its `marqueeGap`. Absent draws the
364
- * editor's own solid line. Blender's is 4: `wm_gesture_draw_rect` (`wm_gesture.cc`) draws the
365
- * box with a `dash_width` of 8 framebuffer pixels at a `udash_factor` of 0.5, the first half
366
- * of each period in its `color` and the second in its `color2`. What a box drag selects is
367
- * the view's (`interaction.boxSelect`), never the look's.
350
+ * editor's own solid line. What a box drag selects is the view's (`interaction.boxSelect`),
351
+ * never the look's.
368
352
  */
369
353
  readonly marqueeDash?: number;
370
354
  /** THE STAGE'S OWN CHROME: which overlay controls the viewport carries and where. See
@@ -377,22 +361,22 @@ export interface StageContribution {
377
361
  /**
378
362
  * THE VIEWPORT'S OWN CHROME, as each target's viewport arranges it (read from the reference
379
363
  * frames, `docs/VIEWPORT-STAGE.md`): a look places the stage's controls, it never adds one.
380
- * Each member is independently optional; absent keeps the editor's own arrangement (Blender's:
381
- * the view text top-left, the tool shelf, the display controls in the top-right corner).
364
+ * Each member is independently optional; absent keeps the editor's own arrangement (the view
365
+ * text top-left, the tool shelf, the display controls in the top-right corner).
382
366
  *
383
367
  * - `bar`: a row across the stage's top. `strip` is a flush panel band (Godot's 3D toolbar,
384
368
  * Unity's Scene view toolbar); `pills` is a row of rounded pills floating over the view
385
369
  * (Unreal's level viewport toolbar). Absent, there is no bar.
386
- * - `viewName`: where the view's name is. `text` is Blender's lines at the top-left; `menu`
370
+ * - `viewName`: where the view's name is. `text` is lines of text at the top-left; `menu`
387
371
  * a pill at the top-left that opens the view menu (Godot's "⋮ Perspective"); `gizmo` a label
388
372
  * under the navigation gizmo that toggles the projection (Unity's "Persp"); `bar` the first
389
373
  * pill of the bar, opening the view menu (Unreal's "Perspective").
390
- * - `tools`: the transform tools on the `shelf` (Blender's, Unity's Tools overlay) or at the
374
+ * - `tools`: the transform tools on the `shelf` (the editor's own, Unity's Tools overlay) or at the
391
375
  * bar's `bar-start` (Godot) or `bar-end` (Unreal).
392
376
  * - `display`: the display controls (shading, grid, helpers, lights) in the top-right
393
377
  * `corner` or at the bar's `bar-start` (Unity, Unreal) or `bar-end` (Godot).
394
378
  * - `transformControls`: the stage's transform controls (orientation, pivot, snap and its
395
- * values) in the document's `header` (the editor's own, Blender's) or on the `bar` after the
379
+ * values) in the document's `header` (the editor's own) or on the `bar` after the
396
380
  * tools (Unreal's row: world/local, the snap values; Godot's: local space, snap).
397
381
  *
398
382
  * Whether the zoom and pan cluster and the camera readout are drawn at all is the view's
@@ -416,7 +400,7 @@ export interface StageWordsContribution {
416
400
  readonly shading?: Readonly<Partial<Record<StageShadingModeId, string>>>;
417
401
  readonly helpers?: string;
418
402
  }
419
- /** The stage's shading modes, by id (`@volter/editor-threejs` `viewport-shading`). */
403
+ /** The stage's shading modes, by id (the 3D integration's `viewport-shading`). */
420
404
  export type StageShadingModeId =
421
405
  | 'solid'
422
406
  | 'clay'
@@ -433,11 +417,9 @@ export type StageShadingModeId =
433
417
  * as `var(--volter-category-<tone>, currentColor)` so a palette that names no
434
418
  * `color.category` group paints the glyph exactly as a monochrome one.
435
419
  *
436
- * The vocabulary is Blender's Properties-tab rail read as data: that rail
437
- * groups by HUE, and at 14 px the hue is what tells one tab from another
438
- * (`properties-object.png` — the shapes are near-indistinguishable). Each
439
- * member is a MEASURED ink; see `EditorTheme.color.category` for the values
440
- * and their source coordinates.
420
+ * The vocabulary is a properties rail's grouping read as data: a rail that
421
+ * groups by HUE, where at 14 px the hue is what tells one tab from another.
422
+ * A style's palette states each member's ink (`EditorTheme.color.category`).
441
423
  */
442
424
  export type IconCategoryTone =
443
425
  | 'object'
@@ -446,21 +428,11 @@ export type IconCategoryTone =
446
428
  | 'tool'
447
429
  | 'operator'
448
430
  | 'data'
449
- // Blender's SCENE group and its COLLECTION, added 2026-09-19 (WORK.md
450
- // §Blender in the tab is Blender, "Inspection parity", I2 decision 3). They
451
- // are not a reading of the rail: every Blender icon declares its group in
452
- // `UI_icons.hh` through a `DEF_ICON_<GROUP>` macro, and
453
- // `interface_icons.cc:126-132` maps those onto the theme members
454
- // `interface/resources.cc:1059-1078` reads — `DEF_ICON_SCENE` →
455
- // `TH_ICON_SCENE` → `.tui.icon_scene` #cccccc
456
- // (`userdef_default_theme.c:272`), `DEF_ICON_COLLECTION` →
457
- // `.tui.icon_collection` #ffffff (`:273`). The Render, Output, View Layer
458
- // and Scene tabs are `DEF_ICON_SCENE`; the Collection tab is
459
- // `DEF_ICON_COLLECTION(GROUP)` (`UI_icons.hh:248`).
431
+ // The scene-level tabs' group and the collection group.
460
432
  | 'scene'
461
433
  | 'collection'
462
- // The SELECTION TOOLS' marquee, which Blender bakes into their tool icons' geometry
463
- // (`ops.generic.select_box`) rather than reading from a theme member.
434
+ // The SELECTION TOOLS' marquee, drawn into their tool icons' geometry rather than read
435
+ // from a theme member.
464
436
  | 'select';
465
437
 
466
438
  /**
@@ -470,15 +442,13 @@ export type IconCategoryTone =
470
442
  * A set may be partial — a glyph it lacks paints the editor's own.
471
443
  *
472
444
  * `tone` and `tonedPath` are the COLOUR channel, and they are two shapes of
473
- * one idea because Blender draws two shapes of it:
445
+ * one idea because a set may draw two shapes of it:
474
446
  *
475
447
  * - `tone` alone tints the WHOLE glyph — the Properties rail's category
476
448
  * tabs and the outliner's type glyphs, where the mark IS the category.
477
449
  * - `tone` with `tonedPath` splits the glyph in two: `path` stays neutral
478
450
  * (`currentColor`) and `tonedPath` takes the category ink. That is
479
- * exactly how an operator mark reads in `modeling-edit-none.png` — the
480
- * cube stays white and only the OPERATED element is tinted — and a
481
- * single per-glyph tone cannot express it.
451
+ * how a mark can keep its base neutral while tinting the operated element.
482
452
  *
483
453
  * Either way an explicit `tone` prop at the SITE wins, so a de-emphasized
484
454
  * (`dim`/`muted`) glyph stays de-emphasized.
@@ -502,9 +472,9 @@ export interface IconSetContribution {
502
472
  export interface StyleContribution {
503
473
  readonly id: string;
504
474
  readonly title: string;
505
- /** A palette id the editor knows — a built-in, or the `palette` document
506
- * this contribution carries. */
507
- readonly paletteId: string;
475
+ /** The colour theme this style was drawn with (`contributes.themes`), which choosing the
476
+ * style applies (docs/adr/looks-one-model.md). */
477
+ readonly colorTheme: string;
508
478
  /** A material id the editor knows — `classic`, `glass`, or the `material`
509
479
  * this contribution carries. */
510
480
  readonly materialId: string;
@@ -513,7 +483,6 @@ export interface StyleContribution {
513
483
  * editor knows. Absent: the editor's own glyphs. */
514
484
  readonly iconSetId?: string;
515
485
  readonly icons?: IconSetContribution;
516
- /** A v3 palette DOCUMENT (the same shape a person imports), registered into
517
- * the editor's theme library with this bundle. */
518
- readonly palette?: unknown;
486
+ /** The style's type families; absent, the editor's own. */
487
+ readonly typography?: { readonly sans: string; readonly mono: string };
519
488
  }
@@ -1,13 +1,7 @@
1
1
  /**
2
- * THE FINDER REGISTRY — the open set of finders an adapter may SELECT by
3
- * name (ARCHITECTURE-CORE §The editor protocol, "Documents, not scenes").
4
- *
5
- * A finder is a pure function from the project's gathered source to document
6
- * entries, plus the Zod schema of its own parameters. The engine registers
7
- * its two here (`./index.ts`); a contribution registers more through the
8
- * same door. `adapter-module.ts` validates only the selection ENVELOPE; the
9
- * parameters are checked against the finder's schema when the loader runs
10
- * it — an unregistered name or a bad parameter refuses loudly, naming both.
2
+ * Finders selected by name, registered by integrations. Each registration supplies a pure
3
+ * function over gathered project files and a parameter schema. Adapter definitions validate
4
+ * the selection envelope; runRegisteredFinder validates its parameters against this registry.
11
5
  */
12
6
 
13
7
  import type { ZodType } from 'zod';
@@ -27,15 +21,7 @@ export interface FinderRegistration<
27
21
  * same rule as every other adapter table. */
28
22
  readonly schema: ZodType<S>;
29
23
  readonly run: (selection: S, input: I) => FinderResult;
30
- /**
31
- * WHAT OF THE MATCHED PROJECT IT READS. `sources` (the default) is the text of every file
32
- * under the selection's `include` globs (`input.sources`) and their paths; `files` is the
33
- * paths alone (`input.files`). A finder over BINARY documents says `files`, so the host never
34
- * fetches a file it could not hand over as text: `@volter/editor-blender`'s
35
- * `modelsFromBlendFiles` lists `.blend` files, and reading each one through the text-source
36
- * route asked a multi-megabyte binary of a route capped at 2 MiB of text — a
37
- * `413 Payload Too Large` per model per resolve, for bytes nothing would have read.
38
- */
24
+ /** sources reads matching file text and paths; files reads paths alone, including binary assets. */
39
25
  readonly reads?: 'sources' | 'files';
40
26
  }
41
27
 
@@ -54,9 +40,8 @@ export function registerFinder<S extends FinderSelectionEnvelope, I>(
54
40
  }
55
41
 
56
42
  /**
57
- * What a CONTRIBUTION exports to register a finder (ARCHITECTURE-CORE §The
58
- * project model): a plain object a project module `editor/contributions/<name>.finder.ts`
59
- * exports as `finder`, handed by the HOST to {@link registerContributedFinder}.
43
+ * What a CONTRIBUTION exports to register a finder: a plain object a package's finder module
44
+ * (`contributes.volter.finders`) exports as `finder`, handed by the HOST to {@link registerContributedFinder}.
60
45
  * A selection may carry `include` globs; the host then supplies the matching
61
46
  * project sources on `input.sources`, which is how a finder reads the
62
47
  * project without a file system.
@@ -4,7 +4,7 @@
4
4
  */
5
5
  import type { AdapterSurface } from './adapter-surface';
6
6
 
7
- /** The canvas/container the game renders into, plus its size. */
7
+ /** The canvas/container the project renders into, plus its size. */
8
8
  export interface HostSurface {
9
9
  readonly canvas: HTMLCanvasElement;
10
10
  readonly width: number;
@@ -12,36 +12,15 @@ export interface HostSurface {
12
12
  }
13
13
 
14
14
  /**
15
- * The surface-INDEPENDENT half of every host context — see this file's header
16
- * for ownership of product-specific extensions. Generic host code (a
17
- * harness, a tier report, anything that must not care what it is mounting) can
18
- * type against this; a real adapter always receives one of the per-surface
19
- * contexts below.
15
+ * What a `stage`-surface root is handed. This is the ONLY context carrying
16
+ * the stage's own objects — the split's entire point (see the header): a canvas
17
+ * or React root is never handed a GPU renderer it cannot use. The objects are
18
+ * opaque here; a runtime extends the context with its own types.
20
19
  */
21
- export interface HostContextBase {
22
- /**
23
- * No GPU/DOM/audio available (Node conformance tests). A first-party adapter
24
- * skips postprocessing/render/audio/input-map loading but still builds the
25
- * scene + Rapier + components, so its scene/authoring/physics can be exercised
26
- * headlessly. Browser hosts leave this false/undefined → full behavior.
27
- */
28
- readonly headless?: boolean;
29
- }
30
-
31
- /**
32
- * What a `three`-surface root is handed. This is the ONLY context carrying
33
- * three's own objects — the split's entire point (see the header): a Pixi or
34
- * React root is never handed a `WebGLRenderer` it cannot use. The objects are
35
- * opaque here; `@volter/editor-threejs` names the Three-typed context, and a
36
- * runtime names its own.
37
- */
38
- export interface ThreeHostContext<Three = unknown, Renderer = unknown, Assets = unknown>
39
- extends HostContextBase {
40
- /** The ONE shared three instance — identity matters for capture (see ingest). */
41
- readonly three: Three;
42
- /** Canvas/container + size; a self-driven game may take the surface over. */
20
+ export interface StageHostContext<Renderer = unknown, Assets = unknown> {
21
+ /** Canvas/container + size; a self-driven project may take the surface over. */
43
22
  readonly surface: HostSurface;
44
- /** Host renderer — host-driven games render through it. */
23
+ /** Host renderer — host-driven projects render through it. */
45
24
  readonly renderer: Renderer;
46
25
  /** Shared GLTF/texture cache. */
47
26
  readonly assets: Assets;
@@ -49,51 +28,17 @@ export interface ThreeHostContext<Three = unknown, Renderer = unknown, Assets =
49
28
 
50
29
  /**
51
30
  * What a `canvas`-surface root is handed: a raw canvas + its
52
- * logical size, plus the
53
- * compositing hints the multi-root host computes once for every stacked
54
- * surface. No `three`, no `renderer`, no `assets` — a Pixi root builds its own
55
- * `PIXI.Application` and its own asset cache from this.
31
+ * logical size. No stage library, no `renderer`, no `assets` — a canvas root builds its own
32
+ * library's application and its own asset cache from this.
56
33
  */
57
- export interface CanvasHostContext extends HostContextBase {
34
+ export interface CanvasHostContext {
58
35
  readonly canvas: HTMLCanvasElement;
59
36
  readonly width: number;
60
37
  readonly height: number;
61
- /** DPR override (T6.1 slice 1, D5 §3 — "one DPR
62
- * everywhere"): the roots-path host computes ONE dpr for every stacked
63
- * surface and passes it here; omitted -> the canvas adapter's own
64
- * default (`window.devicePixelRatio`, uncapped). */
65
- readonly dpr?: number;
66
- /** This root is stacked ABOVE another root (D5 §1): clear with alpha 0
67
- * instead of an opaque background so the layer below shows through. */
68
- readonly transparent?: boolean;
69
- /** Capture tier cost (D5 §4) — the renderer's `preserveDrawingBuffer`. */
70
- readonly preserveDrawingBuffer?: boolean;
71
38
  }
72
39
 
73
- /**
74
- * What a `dom`-surface root is handed (the shape `runtime/create-runtime.ts`
75
- * used to spell `ReactRootHost`).
76
- *
77
- * `container` is the absolutely-positioned, z-ordered DOM-root layer `<div>`
78
- * the host already created and stacked (same box/z-order rules as a canvas per
79
- * D5 §1) — the adapter's `mount` renders its react tree INTO
80
- * this exact element via `createRoot`; it must never create its own root
81
- * element (the DOM analog of three's "the root's runtime renders into the
82
- * surface it is handed" rule).
83
- *
84
- * `game` (inherited, and deliberately still optional): a
85
- * `default-react` sibling mounted BESIDE an ingest root (`packages/editor/src/
86
- * ingest-siblings.ts`) has no native `Game` to hand it — there is no
87
- * first-party loop for the sibling to join, only the ingested
88
- * root's own foreign runtime — so its host carries no `game` at all rather than
89
- * fabricating an empty one (anti-shim rule); the sibling mounts its entry
90
- * component bare, as every `dom` root renders. `mountOneReactRoot`
91
- * (`runtime/create-runtime.ts`, the NATIVE multi-root runtime path) still
92
- * ALWAYS supplies a real `game` — this optionality is reached only by the
93
- * composite sibling's own hand-built host, never by weakening the native path's
94
- * guarantee.
95
- */
96
- export interface DomHostContext extends HostContextBase {
40
+ /** A DOM root mounts into the host-owned, absolutely positioned layer. */
41
+ export interface DomHostContext {
97
42
  readonly container: HTMLElement;
98
43
  }
99
44
 
@@ -102,11 +47,11 @@ export interface DomHostContext extends HostContextBase {
102
47
  * handed — the mirror image of `MountedRootFor<K>` (`root-adapter.ts`), and
103
48
  * what makes `RootAdapter<K>.mount`'s PARAMETER K-typed. A hypothetical 4th
104
49
  * surface resolves to `never` here, so it cannot be mounted until it declares
105
- * its own context (the same fail-to-compile discipline `assertNever` gives the
106
- * dispatch sites).
50
+ * its own context (the same fail-to-compile discipline an exhaustive switch
51
+ * gives a dispatch site).
107
52
  */
108
- export type HostContextFor<K extends AdapterSurface> = K extends 'three'
109
- ? ThreeHostContext
53
+ export type HostContextFor<K extends AdapterSurface> = K extends 'stage'
54
+ ? StageHostContext
110
55
  : K extends 'canvas'
111
56
  ? CanvasHostContext
112
57
  : K extends 'dom'