@volter/editor-game 0.5.68 → 0.5.70

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 (169) hide show
  1. package/NOTICE +7 -7
  2. package/contributions/autoplay.service.ts +1 -1
  3. package/contributions/build-profiles.document.tsx +1 -1
  4. package/contributions/build-progress.status.tsx +2 -2
  5. package/contributions/build.header.tsx +1 -1
  6. package/contributions/connection.status.tsx +1 -1
  7. package/contributions/coverage.service.ts +2 -2
  8. package/contributions/game-eval.command.ts +6 -6
  9. package/contributions/gameplay.command.ts +2 -2
  10. package/contributions/generations.status.tsx +1 -1
  11. package/contributions/ingest.service.ts +2 -2
  12. package/contributions/play.command.ts +6 -6
  13. package/contributions/react/react-inspector.service.ts +2 -2
  14. package/package.json +8 -8
  15. package/serving/index.ts +1 -1
  16. package/src/asset-budget/AssetBudgetPanel.tsx +1 -1
  17. package/src/asset-budget/asset-budget-model.ts +14 -14
  18. package/src/asset-budget/optimize-apply.ts +8 -8
  19. package/src/bridge/call.ts +3 -3
  20. package/src/bridge/dispatch.ts +12 -12
  21. package/src/bridge/screenshot.ts +6 -6
  22. package/src/build/BuildProfilesPanel.tsx +8 -8
  23. package/src/canvas/canvas-board/CanvasBoardDocument.tsx +2 -2
  24. package/src/canvas/canvas-board/canvas-component-board.ts +1 -1
  25. package/src/canvas/canvas-design-mount.ts +1 -1
  26. package/src/coverage/live-project-verbs.ts +3 -3
  27. package/src/coverage/native-system-coverage.ts +3 -3
  28. package/src/coverage/root-coverage.ts +2 -2
  29. package/src/coverage/session-coverage.ts +2 -2
  30. package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +2 -2
  31. package/src/game-document/DevicePresetPicker.tsx +6 -6
  32. package/src/game-document/GameCaptureFrameButton.tsx +1 -1
  33. package/src/game-document/GameDocument.tsx +2 -2
  34. package/src/game-document/GamePanel.tsx +9 -9
  35. package/src/game-document/InstanceInspectorPicker.tsx +1 -1
  36. package/src/game-document/device-preview.ts +4 -4
  37. package/src/game-document/physics-debug.ts +2 -2
  38. package/src/generation/GenerationActivity.tsx +22 -22
  39. package/src/generation/GenerationGallery.css +79 -79
  40. package/src/generation/generation-documents.tsx +25 -25
  41. package/src/generation/generation-presentation.ts +1 -1
  42. package/src/host/adapter-reach.ts +1 -1
  43. package/src/host/adapter-runtime-bindings.ts +5 -5
  44. package/src/host/after-paint.ts +3 -3
  45. package/src/host/api/configurations.ts +1 -1
  46. package/src/host/authoring/canvas-runtime-recognition.ts +2 -2
  47. package/src/host/authoring/contract-hierarchy-authoring.ts +6 -6
  48. package/src/host/authoring/contract-scenes-stories.ts +5 -5
  49. package/src/host/authoring/ingest-data-writer.ts +1 -1
  50. package/src/host/authoring/ingest-source-persistence.ts +2 -2
  51. package/src/host/authoring/mount-isolated-pixi-screen.ts +1 -1
  52. package/src/host/authoring/pixi-authoring-adapter.ts +60 -4
  53. package/src/host/authoring/pixi-creatable-kinds.ts +18 -0
  54. package/src/host/authoring/pixi-live-write-target.ts +6 -5
  55. package/src/host/authoring/pixi-source-write-target.ts +5 -2
  56. package/src/host/authoring/pixi-structure-history.ts +3 -3
  57. package/src/host/binding-resolver.ts +6 -6
  58. package/src/host/browser-transpile.ts +11 -11
  59. package/src/host/canvas-preview-frames.ts +1 -1
  60. package/src/host/components/CameraAuthoringOverlay.tsx +14 -14
  61. package/src/host/components/HeaderTelemetry.tsx +11 -11
  62. package/src/host/components/PixiIsolationSceneContent.tsx +2 -2
  63. package/src/host/components/ResolutionPicker.tsx +4 -4
  64. package/src/host/components/scene-document.tsx +3 -3
  65. package/src/host/components/world-root-stage-binding.tsx +1 -1
  66. package/src/host/components/world-root-stage.ts +1 -1
  67. package/src/host/coverage/authoring-read-probe.ts +1 -1
  68. package/src/host/coverage/capability-coverage.ts +22 -22
  69. package/src/host/coverage/game-contract-seam-evidence.ts +8 -8
  70. package/src/host/coverage/project-verb-coverage.ts +1 -1
  71. package/src/host/coverage/system-adapter-coverage.ts +19 -6
  72. package/src/host/game-location-guard.ts +4 -4
  73. package/src/host/game-module-access.ts +6 -6
  74. package/src/host/game-realm-page.ts +3 -3
  75. package/src/host/game-realm-reclaim.ts +1 -1
  76. package/src/host/gameplay-recording.ts +5 -5
  77. package/src/host/gated-globals.ts +13 -13
  78. package/src/host/ingest/registry.ts +14 -14
  79. package/src/host/play-control-hook.ts +1 -1
  80. package/src/host/playwright-shim.ts +3 -3
  81. package/src/host/projection/pixi.ts +2 -2
  82. package/src/host/react-mount-runtime.ts +1 -1
  83. package/src/host/roots/canvas-root.tsx +2 -2
  84. package/src/host/roots/r3f-root.tsx +1 -1
  85. package/src/host/scene-view-drawability.ts +1 -1
  86. package/src/host/sdk/tools.ts +1 -1
  87. package/src/host/staged-projects.ts +1 -1
  88. package/src/host/stories/story-pixi-preview.ts +1 -1
  89. package/src/host/stories/story-three-preview.ts +1 -1
  90. package/src/host/three-ingest-runtime.ts +1 -1
  91. package/src/ingest/active-ingest.ts +1 -1
  92. package/src/ingest/authoring/ingest-dom-surface-authoring.ts +4 -4
  93. package/src/ingest/authoring/ingest-root-adapter.ts +11 -11
  94. package/src/ingest/capture-wait-report.ts +4 -4
  95. package/src/ingest/discovery-public-ingest.ts +4 -4
  96. package/src/ingest/dom-stub-mark.ts +1 -1
  97. package/src/ingest/entry-load.ts +1 -1
  98. package/src/ingest/game-contract-realm.ts +3 -3
  99. package/src/ingest/game-pointer-lock.ts +1 -1
  100. package/src/ingest/ingest-canvas-scene-document.tsx +2 -2
  101. package/src/ingest/ingest-evidence-hook.ts +6 -6
  102. package/src/ingest/ingest-frame-snapshot.ts +2 -2
  103. package/src/ingest/ingest-play-commands.ts +1 -1
  104. package/src/ingest/ingest-play-control.ts +11 -11
  105. package/src/ingest/ingest-render-debug.ts +5 -5
  106. package/src/ingest/ingest-siblings.ts +3 -3
  107. package/src/ingest/module-mode.ts +2 -2
  108. package/src/ingest/mount-canvas-ingest-root.ts +10 -10
  109. package/src/ingest/mount-coverage.ts +3 -3
  110. package/src/ingest/mount-dom-ingest-root.ts +7 -7
  111. package/src/ingest/mount-ingest-root.ts +11 -11
  112. package/src/ingest/mount-three-ingest-root.ts +5 -5
  113. package/src/ingest/resolve-three.ts +2 -2
  114. package/src/ingest/served-bundle.ts +6 -6
  115. package/src/ingest/served-html-boot.ts +8 -8
  116. package/src/ingest/surface-dom.ts +9 -9
  117. package/src/ingest/surface-three.ts +1 -1
  118. package/src/ingest/types.ts +1 -1
  119. package/src/ingest/unmount-ingest-root.ts +3 -3
  120. package/src/network/NetworkInspectorPanel.tsx +2 -2
  121. package/src/play/play-boot-stall.ts +1 -1
  122. package/src/play/play-mode.ts +31 -30
  123. package/src/play/play-recording.ts +12 -12
  124. package/src/play-bar/PlayBar.tsx +8 -8
  125. package/src/play-bar/PlayerCountPicker.tsx +3 -3
  126. package/src/profiler/FrameDebuggerPanel.tsx +2 -2
  127. package/src/profiler/PerformancePanel.tsx +4 -4
  128. package/src/react/design-time-react-mount.ts +5 -5
  129. package/src/react/dom-authoring-adapter.ts +3 -3
  130. package/src/react/react-inspector-section.tsx +2 -2
  131. package/src/react/react-world-authoring-adapter.ts +20 -20
  132. package/src/react/story-documents/story-documents.tsx +4 -4
  133. package/src/react/ui-component-board.ts +1 -1
  134. package/src/runtime/adapter/audio-meter.ts +1 -1
  135. package/src/runtime/adapter/ingest/contract-debug-adapter.ts +4 -4
  136. package/src/runtime/adapter/ingest/contract-system-adapters.ts +9 -9
  137. package/src/runtime/adapter/ingest/merge-debug-adapters.ts +2 -2
  138. package/src/runtime/adapter/ingest/observation-debug-adapter.ts +1 -1
  139. package/src/runtime/adapter/native-debug-module.ts +5 -5
  140. package/src/runtime/audio/pose-guard.ts +1 -1
  141. package/src/runtime/core/game-scoped-slot.ts +1 -1
  142. package/src/runtime/create-runtime.ts +15 -15
  143. package/src/runtime/debug-bridge.ts +25 -25
  144. package/src/runtime/debug-registry.ts +17 -17
  145. package/src/runtime/dev/instruments.ts +1 -1
  146. package/src/runtime/dev/performance-profiler.ts +2 -2
  147. package/src/runtime/dev/register-render-vitals.ts +2 -2
  148. package/src/runtime/dev/static-batch-advisor.ts +3 -3
  149. package/src/runtime/dev-build.ts +3 -3
  150. package/src/runtime/game.ts +5 -5
  151. package/src/runtime/input-router.ts +1 -1
  152. package/src/runtime/mount-manifest.ts +10 -10
  153. package/src/runtime/pixi/authoring.ts +1 -1
  154. package/src/runtime/pixi/ingest.ts +1 -1
  155. package/src/runtime/presentation.ts +1 -1
  156. package/src/runtime/render-control.ts +21 -21
  157. package/src/runtime/render-seed.ts +3 -3
  158. package/src/runtime/run-ticks-settled.ts +1 -1
  159. package/src/services/game-audio.ts +3 -3
  160. package/src/services/game-network.ts +4 -4
  161. package/src/three/authoring/model-asset-inspector-section.css +15 -15
  162. package/src/three/authoring/model-asset-inspector-section.tsx +7 -7
  163. package/src/three/authoring/r3f-design-session.ts +10 -10
  164. package/src/three/authoring/r3f-source-authoring-adapter.ts +5 -5
  165. package/src/three/authoring/three-authoring-adapter.ts +5 -4
  166. package/src/three/authoring/typed-three-inspector.ts +1 -1
  167. package/src/three/story-documents/three-story-documents.tsx +3 -3
  168. package/src/three/three-board/board-scene.ts +9 -9
  169. package/src/three/three-board/three-component-board.ts +1 -1
@@ -247,7 +247,7 @@ export interface PixiAuthoringOptions {
247
247
  * `pickable` at all — a stage with no mapped surface cannot answer where a
248
248
  * client point landed, and guessing one would fabricate a hit.
249
249
  *
250
- * When the element carries `data-vgai-stage-width`/`-height`, those are the
250
+ * When the element carries `data-volter-stage-width`/`-height`, those are the
251
251
  * stage's LOGICAL size and the mapping divides the measured rect by them —
252
252
  * which is what lets a design surface presented through the shared pan/zoom
253
253
  * camera (a CSS scale) pick correctly. Without them the mapping is 1:1,
@@ -413,6 +413,11 @@ function defaultLabelFor(kind: string): string {
413
413
  * `pixi` is THIS SURFACE's namespace ({@link PixiAuthoringOptions.pixi}): a
414
414
  * node built from another graph's classes is a foreign object in the world's
415
415
  * own display list, and the world's renderer is the one that has to draw it. */
416
+ /** A ParticleContainer, from whichever copy of Pixi made it: it adds particles, never children. */
417
+ function isParticleContainer(object: Container | null | undefined): boolean {
418
+ return !!object && typeof (object as { addParticle?: unknown }).addParticle === 'function';
419
+ }
420
+
416
421
  function createDisplayObject(pixi: CanvasPixiNamespace, kind: string): Container | null {
417
422
  switch (kind) {
418
423
  case 'container':
@@ -439,6 +444,18 @@ function createDisplayObject(pixi: CanvasPixiNamespace, kind: string): Container
439
444
  return new pixi.BitmapText({ text: 'BitmapText', style: { fill: 0xffffff, fontSize: 24 } });
440
445
  case 'htmlText':
441
446
  return new pixi.HTMLText({ text: 'HTMLText', style: { fill: 0xffffff, fontSize: 24 } });
447
+ case 'meshPlane': {
448
+ const plane = new pixi.MeshPlane({ texture: pixi.Texture.WHITE });
449
+ plane.setSize(100, 100);
450
+ return plane;
451
+ }
452
+ case 'perspectiveMesh': {
453
+ const mesh = new pixi.PerspectiveMesh({ texture: pixi.Texture.WHITE });
454
+ mesh.setSize(100, 100);
455
+ return mesh;
456
+ }
457
+ case 'particleContainer':
458
+ return new pixi.ParticleContainer();
442
459
  default:
443
460
  return null;
444
461
  }
@@ -589,7 +606,18 @@ function transformForBoxPatch(
589
606
  scale: [...session.transform.scale],
590
607
  };
591
608
  const rotate = patch['rotate'];
592
- if (rotate !== undefined) next.rotation = rotateTransformZ(session.transform, localTurn(session, rotate));
609
+ if (rotate !== undefined) {
610
+ let turn = localTurn(session, rotate);
611
+ const step = patch['rotationStep'];
612
+ if (step !== undefined && step > 0) {
613
+ // Configure Snap's absolute step lands the node's own rotation on the step from the offset.
614
+ const [, , z, w] = session.transform.rotation;
615
+ const start = (2 * Math.atan2(z, w) * 180) / Math.PI;
616
+ const offset = patch['rotationOffset'] ?? 0;
617
+ turn = Math.round((start + turn - offset) / step) * step + offset - start;
618
+ }
619
+ next.rotation = rotateTransformZ(session.transform, turn);
620
+ }
593
621
  const width = patch['width'];
594
622
  if (width !== undefined && session.rect.width > 0) {
595
623
  next.scale[0] = session.transform.scale[0] * Math.max(0.0001, width / session.rect.width);
@@ -817,10 +845,38 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
817
845
  const structure = this.target.structure ?? this.liveStructure;
818
846
  // A placed creation reaches the structure in the new node's PARENT's own space: the target
819
847
  // writes `x`/`y` as authored, and only this adapter knows the frame `rects` answer in.
848
+ // A ParticleContainer holds particles its code adds, never display objects: Pixi's `addChild`
849
+ // on one throws, so nothing is created or moved into it.
850
+ const holdsNoChildren = (parentId: string | null | undefined): boolean =>
851
+ !!parentId && isParticleContainer(this.projector.object(parentId));
852
+ const noChildren = 'a ParticleContainer holds particles added with addParticle, not display objects.';
820
853
  this.structure = {
821
854
  ...structure,
822
855
  create: (kind, parentId, at) =>
823
- structure.create(kind, parentId, at ? this.pointInParent(parentId ?? null, at) : undefined),
856
+ holdsNoChildren(parentId)
857
+ ? { id: '', ack: this.refuseStructure(noChildren) }
858
+ : structure.create(kind, parentId, at ? this.pointInParent(parentId ?? null, at) : undefined),
859
+ ...(structure.reparent
860
+ ? {
861
+ reparent: (id: string, newParentId: string | null) =>
862
+ holdsNoChildren(newParentId) ? this.refuseStructure(noChildren) : structure.reparent!(id, newParentId),
863
+ }
864
+ : {}),
865
+ ...(structure.paste
866
+ ? {
867
+ paste: (parentId: string | null) =>
868
+ holdsNoChildren(parentId) ? this.refuseStructure(noChildren) : structure.paste!(parentId),
869
+ }
870
+ : {}),
871
+ ...(structure.canPaste
872
+ ? { canPaste: (parentId: string | null) => !holdsNoChildren(parentId) && structure.canPaste!(parentId) }
873
+ : {}),
874
+ ...(structure.creatableKinds
875
+ ? {
876
+ creatableKinds: (parentId: string | null) =>
877
+ holdsNoChildren(parentId) ? [] : structure.creatableKinds!(parentId),
878
+ }
879
+ : {}),
824
880
  };
825
881
  this.assetDrop =
826
882
  this.target.assetDrop ??
@@ -1039,7 +1095,7 @@ export class PixiAuthoringAdapter implements AuthoringAdapter {
1039
1095
  private containerFor(parentId: string | null): Container | null {
1040
1096
  if (!parentId) return this.root;
1041
1097
  const object = this.projector.object(parentId);
1042
- if (!object || typeof object.addChild !== 'function') return null;
1098
+ if (!object || typeof object.addChild !== 'function' || isParticleContainer(object)) return null;
1043
1099
  return object;
1044
1100
  }
1045
1101
 
@@ -59,4 +59,22 @@ export const PIXI_CREATABLE_KINDS: readonly CreatableKind[] = [
59
59
  extends: 'container',
60
60
  description: 'Draws vector shapes (rectangles, circles, paths) from its draw callback.',
61
61
  },
62
+ {
63
+ kind: 'meshPlane',
64
+ label: 'MeshPlane',
65
+ extends: 'container',
66
+ description: 'A textured grid of vertices that can be bent and deformed.',
67
+ },
68
+ {
69
+ kind: 'perspectiveMesh',
70
+ label: 'PerspectiveMesh',
71
+ extends: 'meshPlane',
72
+ description: 'A texture drawn in perspective between four corner points.',
73
+ },
74
+ {
75
+ kind: 'particleContainer',
76
+ label: 'ParticleContainer',
77
+ extends: 'container',
78
+ description: 'Draws many lightweight particles in one batch, added from code with addParticle.',
79
+ },
62
80
  ];
@@ -30,6 +30,7 @@
30
30
  * moved, and the persistence parameter is what this unit added.
31
31
  */
32
32
 
33
+ import { physicsOwnerOf } from '@volter/editor-project/adapter';
33
34
  import type {
34
35
  AuthoringAdapter2D,
35
36
  Overlay2D,
@@ -554,7 +555,7 @@ export function createLiveCanvasWriteTarget(
554
555
  * `literalsFor` is what a rendering one needs. A revert or an apply is a
555
556
  * deliberate act with somewhere to await, so it must not silently do nothing
556
557
  * merely because nothing happened to have described this node first (an agent
557
- * driving the provider through `vgai eval` never does).
558
+ * driving the provider through `volter-game-editor eval` never does).
558
559
  */
559
560
  const literalsNow = async (
560
561
  id: string,
@@ -807,7 +808,7 @@ export function createLiveCanvasWriteTarget(
807
808
  beginTransformEdit(id: string): void {
808
809
  historyResource?.assertCanMutate();
809
810
  const display = a2d.displayObject(id);
810
- if (display && physics.ownerOf(display) === 'physics') physics.freeze(display);
811
+ if (display && physicsOwnerOf(physics, display) === 'physics') physics.freeze(display);
811
812
  editStartState = captureHistoryState();
812
813
  editBaseline = persist ? (a2d.getTransform(id) ?? undefined) : undefined;
813
814
  editMovedOrigin = false;
@@ -816,7 +817,7 @@ export function createLiveCanvasWriteTarget(
816
817
  writeTransform(id: string, next: Transform2DValue): void {
817
818
  historyResource?.assertCanMutate();
818
819
  const display = a2d.displayObject(id);
819
- if (display && physics.ownerOf(display) === 'physics') {
820
+ if (display && physicsOwnerOf(physics, display) === 'physics') {
820
821
  physics.commit(display, next.position, next.rotation);
821
822
  }
822
823
  a2d.setTransform(id, next);
@@ -829,7 +830,7 @@ export function createLiveCanvasWriteTarget(
829
830
  // source write is attempted.
830
831
  endTransformEdit(id: string): void | Promise<WriteAck> {
831
832
  const display = a2d.displayObject(id);
832
- if (display && physics.ownerOf(display) === 'physics') physics.unfreeze(display);
833
+ if (display && physicsOwnerOf(physics, display) === 'physics') physics.unfreeze(display);
833
834
  const label = `Transform ${labelOf(id)}`;
834
835
  const before = editStartState;
835
836
  const baseline = editBaseline;
@@ -907,7 +908,7 @@ export function createLiveCanvasWriteTarget(
907
908
  * channel a refused transform does: they asked for source writes, so each
908
909
  * one that did not land matters individually. Unarmed, the first structural
909
910
  * edit of the session raises the warning that reaches the product's own
910
- * doors (`vgai status`, the session journal, one ack to clear) — a whole
911
+ * doors (`volter-game-editor status`, the session journal, one ack to clear) — a whole
911
912
  * class of this author's edits will not be saved, which is exactly what an
912
913
  * unresolved warning is for — and every later op logs, because repeating an
913
914
  * ack-requiring entry per created node would flood the set with the lane
@@ -119,12 +119,15 @@ const CREATE_SNIPPETS: Readonly<Record<string, string>> = {
119
119
  nineSliceSprite: '<pixiNineSliceSprite label="NineSliceSprite" texture={Texture.WHITE} width={100} height={100} />',
120
120
  bitmapText: '<pixiBitmapText label="BitmapText" text="BitmapText" />',
121
121
  htmlText: '<pixiHTMLText label="HTMLText" text="HTMLText" />',
122
+ meshPlane: '<pixiMeshPlane label="MeshPlane" texture={Texture.WHITE} width={100} height={100} />',
123
+ perspectiveMesh: '<pixiPerspectiveMesh label="PerspectiveMesh" texture={Texture.WHITE} width={100} height={100} />',
124
+ particleContainer: '<pixiParticleContainer label="ParticleContainer" />',
122
125
  };
123
126
  /** A kind whose snippet names Pixi's `Texture`, which the file must then import. */
124
- const TEXTURE_SNIPPET_KINDS = new Set(['animatedSprite', 'tilingSprite', 'nineSliceSprite']);
127
+ const TEXTURE_SNIPPET_KINDS = new Set(['animatedSprite', 'tilingSprite', 'nineSliceSprite', 'meshPlane', 'perspectiveMesh']);
125
128
 
126
129
  function isHiddenProp(name: string): boolean {
127
- return HIDDEN_PROPS.has(name) || /^on[A-Z]/.test(name) || name.startsWith('__vgai');
130
+ return HIDDEN_PROPS.has(name) || /^on[A-Z]/.test(name) || name.startsWith('__volter');
128
131
  }
129
132
 
130
133
  /** Present a source identifier the way native engine inspectors present
@@ -42,7 +42,7 @@ const ROOT_UID = '#root';
42
42
  * `AuthoringAdapter2D` uses for `__authId`, for the same reason (there is no
43
43
  * side table that survives the object outliving one walk). */
44
44
  interface StructureStamped {
45
- __vgaiStructUid?: string;
45
+ __volterStructUid?: string;
46
46
  }
47
47
 
48
48
  /** A node's own local pose, as plain numbers. Recorded because `reparent`
@@ -185,10 +185,10 @@ export class CanvasStructureHistory {
185
185
  private uidOf(object: Container): string {
186
186
  if (object === this.root) return ROOT_UID;
187
187
  const stamped = object as Container & StructureStamped;
188
- const existing = stamped.__vgaiStructUid;
188
+ const existing = stamped.__volterStructUid;
189
189
  if (existing && this.byUid.get(existing) === object) return existing;
190
190
  const uid = existing ?? `s${crypto.randomUUID()}`;
191
- stamped.__vgaiStructUid = uid;
191
+ stamped.__volterStructUid = uid;
192
192
  this.byUid.set(uid, object);
193
193
  return uid;
194
194
  }
@@ -4,7 +4,7 @@
4
4
  *
5
5
  * A game is a native program plus a declaration of itself; the editor is a
6
6
  * universal client of that declaration. This module is where the declaration
7
- * is READ: the manifest root, the project's parsed `vgai.adapter.ts`, and the
7
+ * is READ: the manifest root, the project's parsed `volter.adapter.ts`, and the
8
8
  * entry module's own static surface, resolved against ONE realm
9
9
  * (`realm-services.ts` — dev or packaged).
10
10
  *
@@ -85,7 +85,7 @@ export interface ResolveRootBindingOptions {
85
85
  * Resolve one manifest root against one realm.
86
86
  *
87
87
  * `adapterDef` is an INPUT, never something this function goes and fetches:
88
- * `vgai.adapter.ts` loading stays in `project-adapter.ts`, and the binding is
88
+ * `volter.adapter.ts` loading stays in `project-adapter.ts`, and the binding is
89
89
  * the hand-off's shape rather than a second loader.
90
90
  */
91
91
  export async function resolveRootBinding(
@@ -252,7 +252,7 @@ function rejectCapturedIngestAsRootAdapter(root: ResolvedAdapterRoot): never {
252
252
  export interface ResolveAllRootEntriesOptions {
253
253
  /**
254
254
  * Remount the named region's entrypoint at this swap-slot key. Only that
255
- * world's entry URL carries the override; siblings share `?vgai-mount=`
255
+ * world's entry URL carries the override; siblings share `?volter-mount=`
256
256
  * alone so the module graph stays one instance.
257
257
  */
258
258
  readonly selectionOverride?:
@@ -276,7 +276,7 @@ interface ResolvedComposition {
276
276
  * resolvers would give each root its own generation, which is the defect.
277
277
  *
278
278
  * The COMPOSITION level is likewise where the project's two ambient inputs are
279
- * fetched — the realm's services and the parsed `vgai.adapter.ts` — because
279
+ * fetched — the realm's services and the parsed `volter.adapter.ts` — because
280
280
  * both are properties of the open project rather than of any one root, and
281
281
  * asking once per composition is what keeps every root of a game bound to the
282
282
  * same declaration. `resolveRootBinding` itself still fetches neither: it is
@@ -302,7 +302,7 @@ async function resolveComposition(
302
302
  out.push({ world, resolved });
303
303
  }
304
304
  // Stringified because that is the form the id travels in everywhere else:
305
- // the entry url's `?vgai-mount=` value, and therefore the key
305
+ // the entry url's `?volter-mount=` value, and therefore the key
306
306
  // `gated-globals.ts` reads back off a module url.
307
307
  return { roots: out, mountId: String(epoch) };
308
308
  }
@@ -351,7 +351,7 @@ export async function resolveAllRoots(
351
351
  *
352
352
  * It also returns this composition's `mountId` — the identity the whole
353
353
  * isolation layer is keyed by. It is minted here (one epoch per composition),
354
- * rides every project module url as `?vgai-mount=`, and is what
354
+ * rides every project module url as `?volter-mount=`, and is what
355
355
  * `gated-globals.ts` resolves a realm and an input gate under.
356
356
  *
357
357
  * It does NOT return the entry modules' harvested `debug`/`systems` bindings.
@@ -174,7 +174,7 @@ export function registerRuntimeModules(modules: Record<string, ModuleNamespace>)
174
174
  }
175
175
  // Exposed on globalThis so shim modules (which run in their own module scope)
176
176
  // can reach the live namespaces by specifier.
177
- (globalThis as Record<string, unknown>)['__vgaiModules'] = Object.fromEntries(runtimeModules);
177
+ (globalThis as Record<string, unknown>)['__volterModules'] = Object.fromEntries(runtimeModules);
178
178
  }
179
179
 
180
180
  /**
@@ -213,7 +213,7 @@ function unregisteredRuntimeModules(specifiers: readonly string[]): Error {
213
213
  'import to a dependency it ships; a package outside that list needs a dev server. Open ' +
214
214
  `the project locally with ${commandLine('edit')} (which resolves the project’s own node_modules), ` +
215
215
  'or add the package to a capability so it joins the list ' +
216
- '(packages/editor/src/served-bundle-runtime-modules.ts, gated by ' +
216
+ '(packages/editor-game/src/host/served-bundle-runtime-modules.ts, gated by ' +
217
217
  '`npm run validate-browser-runtime-modules`).',
218
218
  );
219
219
  }
@@ -259,7 +259,7 @@ function loadRuntimeModule(spec: string): Promise<void> {
259
259
 
260
260
  /**
261
261
  * The call-time half of a literal dynamic import in project code. The rewrite
262
- * turns `import('tone')` into `globalThis.__vgaiImportRuntimeModule('tone')`,
262
+ * turns `import('tone')` into `globalThis.__volterImportRuntimeModule('tone')`,
263
263
  * so the load — and the wall, if the browser editor does not ship it — happens
264
264
  * exactly when the project's own code executes the import, never earlier.
265
265
  */
@@ -267,7 +267,7 @@ export async function importRuntimeModule(spec: string): Promise<ModuleNamespace
267
267
  if (!runtimeModules.has(spec)) await loadRuntimeModule(spec);
268
268
  return runtimeModules.get(spec) as ModuleNamespace;
269
269
  }
270
- (globalThis as Record<string, unknown>)['__vgaiImportRuntimeModule'] = importRuntimeModule;
270
+ (globalThis as Record<string, unknown>)['__volterImportRuntimeModule'] = importRuntimeModule;
271
271
 
272
272
  /**
273
273
  * Bring every static bare import live, then rewrite by the lexer's own byte
@@ -304,7 +304,7 @@ export async function resolveAndRewriteBareImports(code: string): Promise<string
304
304
  out = `${out.slice(0, record.s)}${shimUrlFor(record.n)}${out.slice(record.e)}`;
305
305
  } else if (record.d >= 0) {
306
306
  // Literal dynamic import: swap the whole `import(...)` expression.
307
- out = `${out.slice(0, record.ss)}globalThis.__vgaiImportRuntimeModule(${JSON.stringify(
307
+ out = `${out.slice(0, record.ss)}globalThis.__volterImportRuntimeModule(${JSON.stringify(
308
308
  record.n,
309
309
  )})${out.slice(record.se)}`;
310
310
  }
@@ -379,7 +379,7 @@ function shimUrlFor(spec: string): string {
379
379
  // `registerRuntimeModules` user) — the same wall, said the same way.
380
380
  if (!ns) throw unregisteredRuntimeModules([spec]);
381
381
 
382
- const lines = [`const __m = globalThis.__vgaiModules[${JSON.stringify(spec)}];`];
382
+ const lines = [`const __m = globalThis.__volterModules[${JSON.stringify(spec)}];`];
383
383
  for (const key of Object.keys(ns)) {
384
384
  if (key === 'default' || !IDENT_RE.test(key) || RESERVED_WORDS.has(key)) continue;
385
385
  lines.push(`export const ${key} = __m[${JSON.stringify(key)}];`);
@@ -432,7 +432,7 @@ export async function transpileToModuleUrl(
432
432
 
433
433
  /**
434
434
  * Prepend the game-globals lexical shadow to project source — the browser-side
435
- * analogue of the dev server's `vgai-game-globals` Vite plugin
435
+ * analogue of the dev server's `volter-game-globals` Vite plugin
436
436
  * (`server/dev.ts` + `shouldShadowGameGlobals`). The server shadows per-module
437
437
  * because Vite transforms EVERY module indiscriminately and must be told which
438
438
  * ones are game code; the browser has no such firehose — {@link importFromSource}
@@ -541,7 +541,7 @@ export function loaderFor(path: string): esbuild.Loader {
541
541
  function resolveServedModulePath(importer: string, specifier: string): string {
542
542
  if (specifier.startsWith('/')) return specifier;
543
543
  if (/^(?:https?:|blob:|data:)/.test(specifier)) return specifier;
544
- const origin = globalThis.location?.origin ?? 'https://vgai.invalid';
544
+ const origin = globalThis.location?.origin ?? 'https://volter.invalid';
545
545
  const resolved = new URL(specifier, new URL(importer, origin));
546
546
  return resolved.origin === origin
547
547
  ? `${resolved.pathname}${resolved.search}${resolved.hash}`
@@ -572,7 +572,7 @@ export async function bundleServedModule(
572
572
  define: PROJECT_MODULE_DEFINES,
573
573
  plugins: [
574
574
  {
575
- name: 'vgai-served-module',
575
+ name: 'volter-served-module',
576
576
  setup(build) {
577
577
  build.onResolve({ filter: /.*/ }, (args) => {
578
578
  if (
@@ -586,9 +586,9 @@ export async function bundleServedModule(
586
586
  args.kind === 'entry-point'
587
587
  ? args.path
588
588
  : resolveServedModulePath(args.importer, args.path);
589
- return { path, namespace: 'vgai-served' };
589
+ return { path, namespace: 'volter-served' };
590
590
  });
591
- build.onLoad({ filter: /.*/, namespace: 'vgai-served' }, async (args) => {
591
+ build.onLoad({ filter: /.*/, namespace: 'volter-served' }, async (args) => {
592
592
  const response = await fetchImpl(args.path);
593
593
  if (!response.ok) {
594
594
  throw new Error(
@@ -17,7 +17,7 @@
17
17
  * five callers never had a story in hand (`authoring/canvas-design-mount.ts`,
18
18
  * `authoring/pixi-still-presentation.ts`, `components/world-root-stage.ts`,
19
19
  * `live-canvas-frame.ts`, and the ingest lane's canvas mount); moving it out is
20
- * the part of the CSF estate's exit to `@vgai/game` (WORK.md §The open-source
20
+ * the part of the CSF estate's exit to `@volter/editor-game` (WORK.md §The open-source
21
21
  * launch, phase 1 unit 3) that does not wait on anything else.
22
22
  */
23
23
 
@@ -56,13 +56,13 @@ function CameraPreview({ previewRef }: CameraAuthoringOverlayProps) {
56
56
  : 16 / 9;
57
57
 
58
58
  return (
59
- <div className="vgai-camera-preview" data-testid="camera-preview">
60
- <Inline className="vgai-camera-preview-header" gap={4} align="center">
59
+ <div className="volter-camera-preview" data-testid="camera-preview">
60
+ <Inline className="volter-camera-preview-header" gap={4} align="center">
61
61
  <EditorIcon icon={faCamera} size="sm" />
62
62
  <Text
63
63
  variant="label"
64
64
  truncate
65
- className="vgai-camera-preview-title"
65
+ className="volter-camera-preview-title"
66
66
  title={`${preview.name} · ${preview.lens}`}
67
67
  >
68
68
  {preview.name}
@@ -77,7 +77,7 @@ function CameraPreview({ previewRef }: CameraAuthoringOverlayProps) {
77
77
  <EditorIcon icon={faThumbtack} size="sm" />
78
78
  </IconButton>
79
79
  </Tooltip>
80
- <div ref={menuRef} className="vgai-camera-preview-menu-anchor">
80
+ <div ref={menuRef} className="volter-camera-preview-menu-anchor">
81
81
  <Tooltip text="Camera actions">
82
82
  <IconButton
83
83
  aria-label="Camera actions"
@@ -89,7 +89,7 @@ function CameraPreview({ previewRef }: CameraAuthoringOverlayProps) {
89
89
  </IconButton>
90
90
  </Tooltip>
91
91
  {menuOpen ? (
92
- <EditorPopover className="vgai-camera-preview-menu">
92
+ <EditorPopover className="volter-camera-preview-menu">
93
93
  <Button
94
94
  variant="ghost"
95
95
  size="compact"
@@ -130,7 +130,7 @@ function CameraPreview({ previewRef }: CameraAuthoringOverlayProps) {
130
130
  </Inline>
131
131
  <div
132
132
  ref={previewRef}
133
- className="vgai-camera-preview-image"
133
+ className="volter-camera-preview-image"
134
134
  style={{ aspectRatio: String(Math.max(0.5, Math.min(3, aspect))) }}
135
135
  role="img"
136
136
  aria-label={`Live view through ${preview.name}`}
@@ -142,13 +142,13 @@ function CameraPreview({ previewRef }: CameraAuthoringOverlayProps) {
142
142
 
143
143
  function CompositionGuides() {
144
144
  return (
145
- <div className="vgai-camera-guides" aria-hidden="true">
146
- <div className="vgai-camera-guide vgai-camera-guide-v1" />
147
- <div className="vgai-camera-guide vgai-camera-guide-v2" />
148
- <div className="vgai-camera-guide vgai-camera-guide-h1" />
149
- <div className="vgai-camera-guide vgai-camera-guide-h2" />
150
- <div className="vgai-camera-safe-frame" />
151
- <div className="vgai-camera-center-mark" />
145
+ <div className="volter-camera-guides" aria-hidden="true">
146
+ <div className="volter-camera-guide volter-camera-guide-v1" />
147
+ <div className="volter-camera-guide volter-camera-guide-v2" />
148
+ <div className="volter-camera-guide volter-camera-guide-h1" />
149
+ <div className="volter-camera-guide volter-camera-guide-h2" />
150
+ <div className="volter-camera-safe-frame" />
151
+ <div className="volter-camera-center-mark" />
152
152
  </div>
153
153
  );
154
154
  }
@@ -163,7 +163,7 @@ function CameraViewBanner() {
163
163
  <EditorSurface
164
164
  variant="overlay"
165
165
  border
166
- className="vgai-camera-view-banner vgai-chrome-island vgai-glass-island"
166
+ className="volter-camera-view-banner volter-chrome-island volter-glass-island"
167
167
  data-testid="camera-view-banner"
168
168
  >
169
169
  <Inline gap={6} align="center">
@@ -172,13 +172,13 @@ function AudioTelemetry() {
172
172
  : 'Audio quiet';
173
173
 
174
174
  return (
175
- <div className="vgai-header-audio" data-muted={muted || undefined} data-available={!!adapter}>
175
+ <div className="volter-header-audio" data-muted={muted || undefined} data-available={!!adapter}>
176
176
  <Button
177
177
  ref={buttonRef}
178
178
  type="button"
179
179
  variant="ghost"
180
180
  size="compact"
181
- className="vgai-header-telemetry-button"
181
+ className="volter-header-telemetry-button"
182
182
  data-testid="header-audio-telemetry"
183
183
  aria-label={`${stateLabel}; open audio controls`}
184
184
  aria-haspopup="menu"
@@ -187,7 +187,7 @@ function AudioTelemetry() {
187
187
  onClick={() => setMenuOpen((open) => !open)}
188
188
  >
189
189
  <EditorIcon icon={muted ? faVolumeXmark : faVolumeHigh} size="sm" />
190
- <span className="vgai-audio-vu" aria-hidden="true">
190
+ <span className="volter-audio-vu" aria-hidden="true">
191
191
  {Array.from({ length: AUDIO_METER_SEGMENTS }, (_, index) => (
192
192
  <i key={index} data-filled={index < filled || undefined} />
193
193
  ))}
@@ -268,7 +268,7 @@ function PerformanceTelemetry() {
268
268
  type="button"
269
269
  variant="ghost"
270
270
  size="compact"
271
- className="vgai-header-telemetry-button vgai-header-performance"
271
+ className="volter-header-telemetry-button volter-header-performance"
272
272
  data-testid="header-performance-telemetry"
273
273
  data-performance-tone={tone}
274
274
  disabled={!source}
@@ -277,24 +277,24 @@ function PerformanceTelemetry() {
277
277
  onClick={() => profilerView.open('profiler')}
278
278
  >
279
279
  <svg
280
- className="vgai-performance-graph"
280
+ className="volter-performance-graph"
281
281
  aria-hidden="true"
282
282
  viewBox="0 0 72 18"
283
283
  width="72"
284
284
  height="18"
285
285
  >
286
286
  <line
287
- className="vgai-performance-budget"
287
+ className="volter-performance-budget"
288
288
  x1="0"
289
289
  x2="72"
290
290
  y1={18 - (FRAME_BUDGET_60_FPS_MS / 50) * 18}
291
291
  y2={18 - (FRAME_BUDGET_60_FPS_MS / 50) * 18}
292
292
  />
293
- {points && <polyline className="vgai-performance-sparkline" points={points} />}
293
+ {points && <polyline className="volter-performance-sparkline" points={points} />}
294
294
  </svg>
295
- <span className="vgai-performance-readout">
296
- <strong className="vgai-header-fps">{fps ?? '—'} FPS</strong>
297
- <span className="vgai-header-frame-ms">{frameMs?.toFixed(1) ?? '—'} ms</span>
295
+ <span className="volter-performance-readout">
296
+ <strong className="volter-header-fps">{fps ?? '—'} FPS</strong>
297
+ <span className="volter-header-frame-ms">{frameMs?.toFixed(1) ?? '—'} ms</span>
298
298
  </span>
299
299
  </Button>
300
300
  );
@@ -322,7 +322,7 @@ export function HeaderTelemetry() {
322
322
  if (activeChromeRegions().telemetry === 'hidden') return null;
323
323
  return (
324
324
  <Inline
325
- className="vgai-header-telemetry vgai-chrome-island vgai-glass-island"
325
+ className="volter-header-telemetry volter-chrome-island volter-glass-island"
326
326
  data-island-scale="compact"
327
327
  align="center"
328
328
  gap={1}
@@ -184,8 +184,8 @@ export function PixiIsolationSceneContent({
184
184
  ref={containerRef}
185
185
  data-testid="scene-document-pixi-isolation"
186
186
  data-document-id={documentId}
187
- data-vgai-backdrop-color={active ? CANVAS_SCENE_BACKGROUND : undefined}
188
- data-vgai-backdrop-policy={active ? 'dark-frost' : undefined}
187
+ data-volter-backdrop-color={active ? CANVAS_SCENE_BACKGROUND : undefined}
188
+ data-volter-backdrop-policy={active ? 'dark-frost' : undefined}
189
189
  onPointerDown={() => setActiveScope('viewport')}
190
190
  style={{
191
191
  position: 'absolute',
@@ -42,14 +42,14 @@ export function ResolutionPicker({
42
42
  const label = isFill ? 'Fill' : `${resolution.width}×${resolution.height}`;
43
43
 
44
44
  return (
45
- <div className="vgai-playbar-popover-anchor">
45
+ <div className="volter-playbar-popover-anchor">
46
46
  <Button
47
47
  ref={ref}
48
48
  variant="ghost"
49
49
  size="comfortable"
50
50
  aria-haspopup="menu"
51
51
  aria-expanded={open}
52
- aria-controls={open ? 'vgai-resolution-picker-menu' : undefined}
52
+ aria-controls={open ? 'volter-resolution-picker-menu' : undefined}
53
53
  onClick={() => setOpen((value) => !value)}
54
54
  title={title}
55
55
  >
@@ -63,9 +63,9 @@ export function ResolutionPicker({
63
63
  </Button>
64
64
  {open && (
65
65
  <AnchoredMenu
66
- id="vgai-resolution-picker-menu"
66
+ id="volter-resolution-picker-menu"
67
67
  anchorRef={ref}
68
- className="vgai-resolution-menu"
68
+ className="volter-resolution-menu"
69
69
  gap={4}
70
70
  onDismiss={() => setOpen(false)}
71
71
  >
@@ -3,7 +3,7 @@
3
3
  * `Scene` (the subject, not its entry file — see SCENE_DOCUMENT_TITLE),
4
4
  * closeable and discoverable in Content, content = the world root's stage.
5
5
  *
6
- * Transcribed from the Scene half of vgai's `components/CenterDocuments.tsx`.
6
+ * Transcribed from the Scene half of volter's `components/CenterDocuments.tsx`.
7
7
  * The kit keeps the other half (`@volter/editor-core/components/CenterDocuments`
8
8
  * `useCenterDocuments`): it binds the Game document's tab, reports the
9
9
  * live/stopped edge and closes every document on session teardown. What moved
@@ -195,7 +195,7 @@ export class ViewportErrorBoundary extends Component<
195
195
  editorConsole.error(`Viewport failed to start: ${error.message}`, 'editor');
196
196
  // Keep the component stack in the browser console for developer diagnosis.
197
197
  // biome-ignore lint/suspicious/noConsole: renderer startup failures need their React component stack in developer tools as well as the user-facing editor Console entry
198
- console.error('[vgai] Viewport failed to start', error, info.componentStack);
198
+ console.error('[volter] Viewport failed to start', error, info.componentStack);
199
199
 
200
200
  if (isTransientRendererStartupFailure(error) && this.autoRetryCount < MAX_AUTO_RETRIES) {
201
201
  this.clearPendingRetry();
@@ -251,7 +251,7 @@ export class ViewportErrorBoundary extends Component<
251
251
  </div>
252
252
  <Button
253
253
  type="button"
254
- className="vgai-btn"
254
+ className="volter-btn"
255
255
  data-testid="retry-viewport"
256
256
  onClick={this.handleManualRetry}
257
257
  >
@@ -3,7 +3,7 @@
3
3
  * (`@volter/editor-threejs/kit/components/world-root-binding`): the world's surface,
4
4
  * its stage, and the overlays the stage shows only over a world.
5
5
  *
6
- * `Overlays` is the world-root half of vgai's `stage-overlay-set.tsx` — every
6
+ * `Overlays` is the world-root half of volter's `stage-overlay-set.tsx` — every
7
7
  * part it rendered only when `worldRoot` held (the root selection layer, the
8
8
  * camera-authoring pin, the scene's
9
9
  * surface-state card). The rest of that overlay set is the kit's
@@ -454,7 +454,7 @@ export function installWorldRootStage(options: WorldRootStageOptions): WorldRoot
454
454
  // DEV/e2e hook: lets the per-game feature matrix assert the viewport camera
455
455
  // reframes (view presets / focus / orbit) on an ingested scene.
456
456
  if (import.meta.env.DEV) {
457
- (window as unknown as Record<string, unknown>)['__vgaiViewport'] = viewport;
457
+ (window as unknown as Record<string, unknown>)['__volterViewport'] = viewport;
458
458
  }
459
459
 
460
460
  // --- Composer rebuild ---
@@ -510,7 +510,7 @@ function probeDocumentProviders(adapter: AuthoringAdapter, id: string, record: R
510
510
  record(
511
511
  'editor.assetDrop.accepts',
512
512
  'operation',
513
- () => adapter.assetDrop?.accepts(id, '__vgai_read_probe__'),
513
+ () => adapter.assetDrop?.accepts(id, '__volter_read_probe__'),
514
514
  (value) => typeof value === 'boolean',
515
515
  );
516
516
  }