@volter/project 0.5.211 → 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
package/src/semver.ts ADDED
@@ -0,0 +1,16 @@
1
+ // Compares two exact versions (`1.2.3`, with an optional prerelease or build suffix, which is not ordered),
2
+ // such as a project's pinned engine and the engine an editor runs. Anything else compares as `null`.
3
+ const EXACT_SEMVER = /^(\d+)\.(\d+)\.(\d+)(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
4
+
5
+ export function compareSemver(a: string, b: string): -1 | 0 | 1 | null {
6
+ const aMatch = EXACT_SEMVER.exec(a);
7
+ const bMatch = EXACT_SEMVER.exec(b);
8
+ if (!aMatch || !bMatch) return null;
9
+ for (let index = 1; index <= 3; index++) {
10
+ const aPart = Number(aMatch[index]);
11
+ const bPart = Number(bMatch[index]);
12
+ if (aPart < bPart) return -1;
13
+ if (aPart > bPart) return 1;
14
+ }
15
+ return 0;
16
+ }
@@ -4,22 +4,21 @@
4
4
  *
5
5
  * ## Why it exists, and why it is DERIVED
6
6
  *
7
- * Under the Code-OSS frame (ARCHITECTURE-CORE §The core is Code-OSS, U7) the
7
+ * Under the Code-OSS frame the
8
8
  * settings layers are the configuration service's, and a configuration service
9
- * speaks FLAT DOTTED KEYS — `volter.appearance.palette` — while
9
+ * speaks FLAT DOTTED KEYS — `volter.appearance.regions.header` — while
10
10
  * `schema.ts`'s document is a nested object. Something has to be the one place
11
11
  * those two spellings meet, and the same table is what the fork's
12
12
  * `contributes.configuration` is generated from, so VS Code's Settings editor
13
13
  * shows every volter setting with the description its `.describe()` call already
14
14
  * carries.
15
15
  *
16
- * It is DERIVED from {@link EditorSettingsSchema} rather than written, for the
17
- * reason `PROVIDER_GAP` in `adapter-reach.ts` is a `Record` over a pinned key
18
- * union: a hand-kept second list of the same seventeen keys drifts the first
16
+ * It is DERIVED from {@link EditorSettingsSchema} rather than written: a
17
+ * hand-kept second list of the same keys drifts the first
19
18
  * time somebody adds a field, and a settings key with no declaration is
20
19
  * invisible in the one settings UI rather than loud. The walk is over the JSON
21
20
  * Schema Zod itself produces (`z.toJSONSchema`) — the SAME derivation
22
- * `scripts/generate-schema.ts` commits as
21
+ * `packages/project/scripts/generate-schema.mjs` commits as
23
22
  * `packages/project/schemas/volter-settings.schema.json`, which is what the
24
23
  * fork's generator reads. One derivation, three consumers: this table, the
25
24
  * committed schema a person's editor autocompletes against, and the frame's
@@ -40,13 +39,12 @@ export const SETTINGS_KEY_PREFIX = 'volter';
40
39
 
41
40
  /** What a key is, and what the Settings editor shows for it. */
42
41
  export interface SettingsKeyDescriptor {
43
- /** The dotted name, prefix included: `volter.appearance.palette`. */
42
+ /** The dotted name, prefix included: `volter.appearance.regions.header`. */
44
43
  readonly key: string;
45
- /** The path into the settings document: `['appearance', 'palette']`. */
44
+ /** The path into the settings document: `['appearance', 'regions', 'header']`. */
46
45
  readonly path: readonly string[];
47
- /** JSON Schema `type`, when the field has exactly one. A nullable field
48
- * (`devicePreview.touch`) has none, and the frame declares no type for it
49
- * rather than picking one of the two. */
46
+ /** JSON Schema `type`, when the field has exactly one. A nullable field has
47
+ * none, and the frame declares no type for it rather than picking one. */
50
48
  readonly type?: string | undefined;
51
49
  /** The closed set of values, when the field is an enum. */
52
50
  readonly values?: readonly string[] | undefined;
@@ -1,13 +1,13 @@
1
1
  /**
2
- * THE SETTINGS DOCUMENT — the Zod truth for the two shared settings layers
3
- * (ARCHITECTURE-CORE §Editor chrome, "Settings have four layers with named
4
- * homes"):
2
+ * THE SETTINGS DOCUMENT — the Zod truth for the two shared settings layers:
5
3
  *
6
- * ~/.volter/settings.json the USER layer — one person, every project
7
- * <project>/.volter/settings.json the PROJECT layer — one project, committed,
8
- * every person who opens it
4
+ * ~/.volter/settings.json the USER layer — one person, every project
5
+ * <project>/.vscode/settings.json the PROJECT layer — one project, committed,
6
+ * every person who opens it: its `volter.*`
7
+ * keys, in Code-OSS's own workspace settings
8
+ * (`editor-core/server/project-settings-file.ts`)
9
9
  *
10
- * Both files carry the same shape; the project layer overrides the user layer
10
+ * Both layers carry the same shape; the project layer overrides the user layer
11
11
  * key by key (`mergeEditorSettings`). Neither layer holds authored content —
12
12
  * an unreadable file is reported by name and falls back to defaults, never
13
13
  * migrated. The generated JSON Schema (`npm run generate-schema` →
@@ -16,9 +16,8 @@
16
16
  *
17
17
  * Every field here has a runtime reader in the editor's settings store
18
18
  * consumers (`theme-preference.ts`, `workspace-regions.ts`,
19
- * `keymap-presets.ts`, `workspace-presets.ts`, `device-preview.ts`); a field
20
- * with no reader does not get declared (CLAUDE.md, "Do not author schema
21
- * fields that have no runtime reader").
19
+ * `keymap-presets.ts`, `workspace-presets.ts`); a field
20
+ * with no runtime reader has no setting to govern.
22
21
  */
23
22
  import { z } from 'zod';
24
23
 
@@ -27,29 +26,26 @@ const RegionVisibility = z.enum(['shown', 'hidden']);
27
26
  export const ChromeRegionsSettingsSchema = z
28
27
  .object({
29
28
  workspaceTabs: RegionVisibility.optional().describe(
30
- "The workspace tab strip in the top bar (Blender's); hidden unless a style shows it.",
29
+ 'The workspace tab strip in the top bar; hidden unless a style shows it.',
31
30
  ),
32
31
  header: RegionVisibility.optional().describe('The top header bar.'),
33
32
  shelf: RegionVisibility.optional().describe('The utility shelf.'),
34
33
  inspector: z
35
34
  .enum(['column', 'properties', 'card'])
36
35
  .optional()
37
- .describe('Inspector presentation: the narrow docked column or a properties panel.'),
36
+ .describe('Inspector presentation: a narrow docked column, properties panel or card.'),
38
37
  drawer: RegionVisibility.optional().describe('The bottom drawer.'),
39
- telemetry: RegionVisibility.optional().describe(
40
- "The top bar's runtime telemetry cluster: the frame-rate readout, its sparkline and the audio meter. The Profiler utility is the same information's other door.",
41
- ),
42
38
  hierarchyTypeSuffix: RegionVisibility.optional().describe(
43
- "The `·TypeName` a component-instance row prints after its name in the hierarchy. Hidden leaves the row its name alone, the way Blender's Outliner does; the type stays reachable through the row's own tooltip and the inspector.",
39
+ "The `·TypeName` a component-instance row prints after its name in the hierarchy. Hidden leaves the row its name alone; the type stays reachable through the row's own tooltip and the inspector.",
44
40
  ),
45
41
  hierarchyInstanceRule: RegionVisibility.optional().describe(
46
- "The dotted rule a component-instance row draws under its name in the hierarchy. Hidden leaves the name unmarked, the way Blender's Outliner does; the fact stays reachable through the row's own tooltip, its source actions and the inspector.",
42
+ "The dotted rule a component-instance row draws under its name in the hierarchy. Hidden leaves the name unmarked; the fact stays reachable through the row's own tooltip, its source actions and the inspector.",
47
43
  ),
48
44
  hierarchyRestrictions: z
49
45
  .enum(['select+viewport', 'viewport+render'])
50
46
  .optional()
51
47
  .describe(
52
- "Which restriction columns a hierarchy row carries at its right edge: this editor's selection lock beside the eye, or Blender's default filter — the eye in its own column with the render column reserved and blank, three.js having one visibility flag for viewport and render alike.",
48
+ "Which restriction columns a hierarchy row carries at its right edge: the selection lock beside the eye, or the eye in its own column with the render column reserved and blank, the viewport having one visibility flag for viewport and render alike.",
53
49
  ),
54
50
  })
55
51
  .strict()
@@ -61,26 +57,20 @@ export const EditorSettingsSchema = z
61
57
  .string()
62
58
  .optional()
63
59
  .describe('JSON Schema pointer for editor autocomplete; ignored by the editor.'),
60
+ style: z
61
+ .string()
62
+ .optional()
63
+ .describe(
64
+ 'Editor style id (`classic`, `glass`, or one a package carries): its material, icon set and type. Choosing one also applies the colour theme it was drawn with.',
65
+ ),
64
66
  appearance: z
65
67
  .object({
66
- palette: z
67
- .string()
68
- .optional()
69
- .describe('Editor palette id — a built-in or a custom theme document.'),
70
- material: z
71
- .string()
72
- .optional()
73
- .describe('Editor material id (`classic`, `glass`, or one a style bundle carries).'),
74
- icons: z
75
- .string()
76
- .optional()
77
- .describe('Editor icon set id (`default`, or one a style bundle carries).'),
78
68
  regions: ChromeRegionsSettingsSchema.optional(),
79
69
  })
80
70
  .strict()
81
71
  .optional()
82
72
  .describe('How the editor chrome looks.'),
83
- keymap: z.string().optional().describe('Active keymap preset id (`volter`, `blender`, …).'),
73
+ keymap: z.string().optional().describe('Active keymap preset id (`volter`, or a contributed preset).'),
84
74
  play: z
85
75
  .object({
86
76
  keepPanelsVisible: z
@@ -93,24 +83,11 @@ export const EditorSettingsSchema = z
93
83
  .strict()
94
84
  .optional()
95
85
  .describe('How the editor presents Play mode.'),
96
- devicePreview: z
97
- .object({
98
- preset: z.string().optional().describe('Device preview frame preset id.'),
99
- touch: z
100
- .boolean()
101
- .nullable()
102
- .optional()
103
- .describe('Touch emulation override; null follows the preset.'),
104
- })
105
- .strict()
106
- .optional()
107
- .describe('The device frame the Game document previews in.'),
108
86
  })
109
87
  .strict()
110
88
  .describe('Editor settings — the user layer and the project layer share this shape.');
111
89
 
112
90
  export type EditorSettings = z.infer<typeof EditorSettingsSchema>;
113
- export type ChromeRegionsSettings = z.infer<typeof ChromeRegionsSettingsSchema>;
114
91
 
115
92
  /** Which layer a file is; the file paths are the server's. */
116
93
  export type SettingsLayer = 'user' | 'project';
@@ -149,3 +126,37 @@ export function parseEditorSettings(
149
126
  ),
150
127
  };
151
128
  }
129
+
130
+ /** A settings FILE's text, read: `null` (no file) is the empty document; text that is not JSON or
131
+ * not a valid document carries its issues and no settings. */
132
+ export function parseEditorSettingsText(
133
+ text: string | null,
134
+ ): { settings: EditorSettings; issues: [] } | { settings: null; issues: string[] } {
135
+ if (text === null) return { settings: {}, issues: [] };
136
+ let raw: unknown;
137
+ try {
138
+ raw = JSON.parse(text);
139
+ } catch (cause) {
140
+ return { settings: null, issues: [`not JSON: ${(cause as Error).message}`] };
141
+ }
142
+ return parseEditorSettings(raw);
143
+ }
144
+
145
+ /**
146
+ * The refusal of a write over a settings file that is invalid on disk, or `null` when the write
147
+ * may go ahead. Every door that writes a settings layer answers with this one (HTTP 409): an
148
+ * invalid file is the person's to fix, never the editor's to replace, because writing the
149
+ * session's document over it would discard everything else the file holds.
150
+ */
151
+ export function settingsWriteRefusal(
152
+ layer: 'user' | 'project',
153
+ path: string,
154
+ onDiskIssues: readonly string[],
155
+ ): { error: string; issues: readonly string[]; path: string } | null {
156
+ if (onDiskIssues.length === 0) return null;
157
+ return {
158
+ error: `${path} is invalid (${onDiskIssues.join('; ')}); fix it before the editor writes ${layer} settings again.`,
159
+ issues: onDiskIssues,
160
+ path,
161
+ };
162
+ }
@@ -1,57 +0,0 @@
1
- {
2
- "$comment": "AUTO-GENERATED by scripts/generate-engine-manifest.ts — do not edit manually. Run 'npm run generate-engine-manifest'.",
3
- "phases": [
4
- {
5
- "name": "input",
6
- "description": "Sample input devices (keyboard, mouse, gamepad) at the top of the frame."
7
- },
8
- {
9
- "name": "prePhysics",
10
- "description": "Apply forces/intents to rigid bodies before the physics step."
11
- },
12
- {
13
- "name": "physics",
14
- "description": "Step the Rapier physics world (fixed timestep)."
15
- },
16
- {
17
- "name": "postPhysics",
18
- "description": "Read physics results back; sync transforms from bodies to Object3D."
19
- },
20
- {
21
- "name": "gameLogic",
22
- "description": "General gameplay behavior."
23
- },
24
- {
25
- "name": "animation",
26
- "description": "Advance animation graphs / mixers."
27
- },
28
- {
29
- "name": "preRender",
30
- "description": "Final per-frame fixups (cameras, HUD state) before drawing."
31
- },
32
- {
33
- "name": "render",
34
- "description": "Draw the frame."
35
- }
36
- ],
37
- "phaseOrder": [
38
- "input",
39
- "prePhysics",
40
- "physics",
41
- "postPhysics",
42
- "gameLogic",
43
- "animation",
44
- "preRender",
45
- "render"
46
- ],
47
- "phaseConstants": {
48
- "INPUT": "input",
49
- "PRE_PHYSICS": "prePhysics",
50
- "PHYSICS": "physics",
51
- "POST_PHYSICS": "postPhysics",
52
- "GAME_LOGIC": "gameLogic",
53
- "ANIMATION": "animation",
54
- "PRE_RENDER": "preRender",
55
- "RENDER": "render"
56
- }
57
- }
@@ -1,22 +0,0 @@
1
- <!-- AUTO-GENERATED by scripts/generate-engine-manifest.ts — do not edit manually. -->
2
- <!-- Run 'npm run generate-engine-manifest' to regenerate. -->
3
-
4
- # Engine API Manifest
5
-
6
- The schema-as-source-of-truth artifact for the engine's imperative surface — generated from the actual code so docs can never drift (see ROADMAP P0.3 / P2.9).
7
-
8
- ## System Phases
9
-
10
- Each frame, systems run in this exact order (from `PHASE_ORDER` in `packages/project/src/core/system-phase.ts`):
11
-
12
- | # | Phase | Constant | Description |
13
- | - | ----- | -------- | ----------- |
14
- | 1 | `input` | `SystemPhase.INPUT` | Sample input devices (keyboard, mouse, gamepad) at the top of the frame. |
15
- | 2 | `prePhysics` | `SystemPhase.PRE_PHYSICS` | Apply forces/intents to rigid bodies before the physics step. |
16
- | 3 | `physics` | `SystemPhase.PHYSICS` | Step the Rapier physics world (fixed timestep). |
17
- | 4 | `postPhysics` | `SystemPhase.POST_PHYSICS` | Read physics results back; sync transforms from bodies to Object3D. |
18
- | 5 | `gameLogic` | `SystemPhase.GAME_LOGIC` | General gameplay behavior. |
19
- | 6 | `animation` | `SystemPhase.ANIMATION` | Advance animation graphs / mixers. |
20
- | 7 | `preRender` | `SystemPhase.PRE_RENDER` | Final per-frame fixups (cameras, HUD state) before drawing. |
21
- | 8 | `render` | `SystemPhase.RENDER` | Draw the frame. |
22
-