@volter/editor-game 0.5.66 → 0.5.67

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 (236) hide show
  1. package/contributions/audio-unlock.service.ts +2 -2
  2. package/contributions/autoplay.service.ts +1 -1
  3. package/contributions/bridge.command.ts +3 -3
  4. package/contributions/canvas/component-board.service.ts +16 -0
  5. package/contributions/canvas/design-time-mount.service.ts +45 -0
  6. package/contributions/canvas-story-capture.service.ts +12 -0
  7. package/contributions/edit-mode-audio.service.ts +2 -2
  8. package/contributions/edit-mode-networking.service.ts +1 -1
  9. package/contributions/gameplay.command.ts +4 -4
  10. package/contributions/generation.service.ts +1 -1
  11. package/contributions/godot.style.ts +26 -6
  12. package/contributions/godot.view.ts +6 -0
  13. package/contributions/ingest.service.ts +2 -2
  14. package/contributions/instances.command.ts +1 -1
  15. package/contributions/navmesh.menu.ts +1 -1
  16. package/contributions/network-observer.service.ts +14 -0
  17. package/contributions/play.command.ts +10 -4
  18. package/contributions/react/component-board.service.ts +1 -1
  19. package/contributions/react/design-time-mount.service.ts +1 -1
  20. package/contributions/scene-document.service.ts +3 -3
  21. package/contributions/state-watch.menu.ts +1 -1
  22. package/contributions/state-watch.utility.tsx +1 -1
  23. package/contributions/team-playtest.service.ts +4 -4
  24. package/contributions/three/component-board.service.ts +1 -1
  25. package/contributions/three/component-verbs.command.ts +6 -6
  26. package/contributions/three/three-authoring.service.ts +8 -5
  27. package/contributions/three-story-capture.service.ts +12 -0
  28. package/contributions/unity.style.ts +17 -7
  29. package/contributions/unity.view.ts +6 -0
  30. package/contributions/unreal.style.ts +10 -2
  31. package/contributions/unreal.view.ts +5 -0
  32. package/package.json +20 -10
  33. package/src/asset-budget/AssetBudgetPanel.tsx +1 -1
  34. package/src/asset-budget/asset-budget-model.ts +1 -1
  35. package/src/audio/AudioDebuggerPanel.tsx +1 -1
  36. package/src/bridge/dispatch.ts +14 -14
  37. package/src/bridge/live-frames.ts +1 -1
  38. package/src/bridge/screenshot.ts +3 -3
  39. package/src/build/BuildProfilesPanel.tsx +3 -3
  40. package/src/canvas/canvas-board/CanvasBoardDocument.tsx +676 -0
  41. package/src/canvas/canvas-board/canvas-board-model.ts +407 -0
  42. package/src/canvas/canvas-board/canvas-component-board.ts +56 -0
  43. package/src/canvas/canvas-design-mount.ts +524 -0
  44. package/src/canvas/design-time-canvas-mount.ts +79 -0
  45. package/src/coverage/live-authoring-surface.ts +5 -5
  46. package/src/coverage/live-project-verbs.ts +1 -1
  47. package/src/coverage/native-system-coverage.ts +5 -5
  48. package/src/coverage/root-coverage.ts +1 -1
  49. package/src/coverage/session-coverage.ts +3 -3
  50. package/src/design-system-stories/ApplicationChrome.stories.tsx +5 -5
  51. package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +7 -7
  52. package/src/edit-mode/edit-mode-audio.ts +4 -4
  53. package/src/edit-mode/edit-mode-networking.ts +2 -2
  54. package/src/game-document/GameCaptureFrameButton.tsx +1 -1
  55. package/src/game-document/GameDocument.tsx +2 -2
  56. package/src/game-document/GamePanel.tsx +4 -4
  57. package/src/game-document/InstanceInspectorPicker.tsx +2 -2
  58. package/src/game-document/crowd-debug.ts +1 -1
  59. package/src/game-document/device-preview.ts +10 -11
  60. package/src/game-document/physics-debug.ts +1 -1
  61. package/src/generation/GenerationActivity.tsx +3 -3
  62. package/src/generation/generation-documents.tsx +5 -5
  63. package/src/generation/generation-jobs.ts +1 -1
  64. package/src/host/adapter-runtime-bindings.ts +96 -5
  65. package/src/host/authoring/babylon-authoring-adapter.ts +6 -6
  66. package/src/host/authoring/gesture-persist.ts +1 -1
  67. package/src/host/authoring/ingest-data-writer.ts +1 -1
  68. package/src/host/authoring/ingest-source-persistence.ts +4 -4
  69. package/src/host/authoring/mounted-authoring.ts +4 -4
  70. package/src/host/authoring/phaser-live-authoring-adapter.ts +4 -4
  71. package/src/host/authoring/pixi-authoring-adapter.ts +40 -15
  72. package/src/host/authoring/pixi-creation-site-write-target.ts +2 -2
  73. package/src/host/authoring/pixi-live-write-target.ts +4 -4
  74. package/src/host/authoring/pixi-source-identity.ts +3 -3
  75. package/src/host/authoring/pixi-source-write-target.ts +1614 -0
  76. package/src/host/authoring/pixi-still-presentation.ts +1 -1
  77. package/src/host/authoring/pixi-structure-history.ts +2 -2
  78. package/src/host/authoring/pixi-transform-channels.ts +16 -14
  79. package/src/host/authoring/source-persistence-backend.ts +3 -3
  80. package/src/host/authoring/struct-write-pipe.ts +1 -1
  81. package/src/host/binding-resolver.ts +8 -9
  82. package/src/host/browser-transpile.ts +1 -1
  83. package/src/host/canvas-entry-runtime.ts +58 -47
  84. package/src/host/canvas-preview-frames.ts +482 -0
  85. package/src/host/components/CameraAuthoringOverlay.tsx +1 -1
  86. package/src/host/components/HeaderTelemetry.tsx +4 -4
  87. package/src/host/components/PixiIsolationSceneContent.tsx +10 -10
  88. package/src/host/components/ThreeIsolationSceneContent.tsx +3 -3
  89. package/src/host/components/frame-debugger-model.ts +2 -2
  90. package/src/host/components/header-telemetry-model.ts +2 -2
  91. package/src/host/components/scene-document.tsx +14 -14
  92. package/src/host/components/utility-view-state.ts +1 -1
  93. package/src/host/components/world-root-stage-binding.tsx +12 -12
  94. package/src/host/components/world-root-stage.ts +70 -49
  95. package/src/host/coverage/system-adapter-coverage.ts +3 -4
  96. package/src/host/design-system-stories/fixtures/editor-runtime.tsx +9 -11
  97. package/src/host/document-preview-three.ts +1 -1
  98. package/src/host/entry-adjudication.ts +6 -6
  99. package/src/host/game-css-scope-transform.ts +4 -0
  100. package/src/host/game-realm-page.ts +1 -1
  101. package/src/host/gameplay-export.ts +25 -14
  102. package/src/host/gameplay-recording.ts +5 -5
  103. package/src/host/gated-globals.ts +2 -2
  104. package/src/host/history/json-history-resource.ts +1 -1
  105. package/src/host/projection/pixi.ts +24 -2
  106. package/src/host/r3f-entry-runtime.ts +65 -34
  107. package/src/host/react-mount-runtime.ts +7 -48
  108. package/src/host/realm-services.ts +1 -1
  109. package/src/host/roots/canvas-root.tsx +361 -0
  110. package/src/host/roots/r3f-root.tsx +473 -0
  111. package/src/host/roots/react-root.ts +9 -43
  112. package/src/host/served-bundle-runtime-modules.ts +1 -19
  113. package/src/host/server-log-bridge.ts +2 -2
  114. package/src/host/stories/mounted-story-viewport-source.ts +1 -1
  115. package/src/host/stories/pixi-story-model.ts +30 -0
  116. package/src/host/stories/story-media-captures.ts +46 -0
  117. package/src/host/stories/story-media-presence.ts +3 -3
  118. package/src/host/stories/story-pixi-preview.ts +408 -0
  119. package/src/host/stories/story-three-preview.ts +806 -0
  120. package/src/host/stories/three-story-captures.ts +35 -0
  121. package/src/host/story-three-preview-runtime.ts +56 -0
  122. package/src/host/use-active-performance-source.ts +2 -2
  123. package/src/host/viewport-pose-memory.ts +1 -1
  124. package/src/host/viewport-root-presentation.ts +6 -5
  125. package/src/ingest/active-ingest.ts +1 -1
  126. package/src/ingest/authoring/ingest-dom-surface-authoring.ts +4 -4
  127. package/src/ingest/authoring/ingest-root-adapter.ts +8 -8
  128. package/src/ingest/deferred-ingest-play.ts +6 -6
  129. package/src/ingest/discovery-public-ingest.ts +2 -2
  130. package/src/ingest/ingest-boot-viewport.ts +2 -2
  131. package/src/ingest/ingest-canvas-scene-document.tsx +8 -8
  132. package/src/ingest/ingest-canvas-scene.ts +3 -3
  133. package/src/ingest/ingest-evidence-hook.ts +1 -1
  134. package/src/ingest/ingest-frame-snapshot.ts +1 -1
  135. package/src/ingest/ingest-play-control.ts +1 -1
  136. package/src/ingest/ingest-render-debug.ts +10 -10
  137. package/src/ingest/ingest-siblings.ts +13 -25
  138. package/src/ingest/module-mode.ts +14 -14
  139. package/src/ingest/mount-canvas-ingest-root.ts +21 -21
  140. package/src/ingest/mount-coverage.ts +2 -2
  141. package/src/ingest/mount-dom-ingest-root.ts +11 -11
  142. package/src/ingest/mount-ingest-root.ts +8 -8
  143. package/src/ingest/mount-three-ingest-root.ts +8 -8
  144. package/src/ingest/resolve-canvas.ts +1 -1
  145. package/src/ingest/served-html-boot.ts +1 -1
  146. package/src/ingest/surface-canvas.ts +1 -1
  147. package/src/ingest/unmount-ingest-root.ts +5 -5
  148. package/src/navmesh/navmesh-handler.ts +24 -16
  149. package/src/network/NetworkInspectorPanel.tsx +449 -14
  150. package/src/network/network-inspector-model.ts +14 -2
  151. package/src/play/play-log-events.ts +1 -1
  152. package/src/play/play-mode.ts +76 -133
  153. package/src/play/play-recording.ts +1 -1
  154. package/src/play/react-play-live-authoring.ts +3 -3
  155. package/src/play/run-selection.ts +93 -0
  156. package/src/play-bar/PlayBar.tsx +20 -39
  157. package/src/profiler/FrameDebuggerPanel.tsx +1 -1
  158. package/src/profiler/PerformancePanel.tsx +3 -3
  159. package/src/react/design-time-react-mount.ts +23 -65
  160. package/src/react/dom-authoring-adapter.ts +9 -9
  161. package/src/react/react-inspector-section.tsx +5 -5
  162. package/src/react/react-world-authoring-adapter.ts +11 -11
  163. package/src/react/story-documents/story-documents.tsx +9 -9
  164. package/src/react/ui-board-document.tsx +7 -7
  165. package/src/react/ui-component-board.ts +2 -2
  166. package/src/runtime/adapter/audio-meter.ts +21 -0
  167. package/src/runtime/adapter/first-party-audio-system.ts +230 -0
  168. package/src/runtime/adapter/ingest/contract-debug-adapter.ts +114 -0
  169. package/src/runtime/adapter/ingest/contract-system-adapters.ts +256 -0
  170. package/src/runtime/adapter/ingest/merge-debug-adapters.ts +197 -0
  171. package/src/runtime/adapter/ingest/observation-debug-adapter.ts +162 -0
  172. package/src/runtime/adapter/ingest/upstream-pin.ts +51 -0
  173. package/src/runtime/adapter/native-debug-module.ts +498 -0
  174. package/src/runtime/audio/bus-mixer.ts +161 -0
  175. package/src/runtime/audio/pose-guard.ts +80 -0
  176. package/src/runtime/core/frame-pacing.ts +126 -0
  177. package/src/runtime/core/game-loop.ts +225 -0
  178. package/src/runtime/core/game-scoped-slot.ts +28 -0
  179. package/src/runtime/core/seeded-random.ts +162 -0
  180. package/src/runtime/core/sim-clock.ts +391 -0
  181. package/src/runtime/core/system-runner.ts +269 -0
  182. package/src/runtime/core/types.ts +104 -0
  183. package/src/runtime/create-runtime.ts +1128 -0
  184. package/src/runtime/debug-bridge.ts +570 -0
  185. package/src/runtime/debug-registry.ts +899 -0
  186. package/src/runtime/dev/chrome-trace.ts +153 -0
  187. package/src/runtime/dev/instruments.ts +403 -0
  188. package/src/runtime/dev/logger.ts +119 -0
  189. package/src/runtime/dev/performance-profiler.ts +367 -0
  190. package/src/runtime/dev/register-render-vitals.ts +276 -0
  191. package/src/runtime/dev/render-census.ts +354 -0
  192. package/src/runtime/dev/render-debug-adapter.ts +218 -0
  193. package/src/runtime/dev/render-memory.ts +226 -0
  194. package/src/runtime/dev/render-vitals.ts +338 -0
  195. package/src/runtime/dev/static-batch-advisor.ts +188 -0
  196. package/src/runtime/dev/webgl-frame-capture.ts +366 -0
  197. package/src/runtime/dev/webgl-gpu-timer.ts +53 -0
  198. package/src/runtime/dev-build.ts +47 -0
  199. package/src/runtime/game.ts +1636 -0
  200. package/src/runtime/gameplay-rng-trap.ts +135 -0
  201. package/src/runtime/host-context.ts +64 -0
  202. package/src/runtime/input-router.ts +169 -0
  203. package/src/runtime/mount-manifest.ts +480 -0
  204. package/src/runtime/pixi/authoring.ts +706 -0
  205. package/src/runtime/pixi/ingest.ts +116 -0
  206. package/src/runtime/pixi/physics-registry.ts +49 -0
  207. package/src/runtime/pixi/render-pass-bracket.ts +117 -0
  208. package/src/runtime/pixi/scene-capture.ts +179 -0
  209. package/src/runtime/pixi/system-adapters.ts +69 -0
  210. package/src/runtime/playtest.ts +22 -0
  211. package/src/runtime/presentation.ts +141 -0
  212. package/src/runtime/render-control.ts +642 -0
  213. package/src/runtime/render-seed.ts +77 -0
  214. package/src/runtime/run-ticks-settled.ts +73 -0
  215. package/src/runtime/setup/setup-audio.ts +72 -0
  216. package/src/services/audio-pose-guard.ts +2 -2
  217. package/src/services/game-audio.ts +152 -0
  218. package/src/services/game-network.ts +657 -0
  219. package/src/services/game-physics.ts +334 -0
  220. package/src/state-watch/StateWatchPanel.tsx +1 -1
  221. package/src/three/authoring/camera-runtime-inspector-section.tsx +3 -2
  222. package/src/three/authoring/constraint-inspector-section.tsx +6 -5
  223. package/src/three/authoring/model-asset-inspector-section.tsx +7 -6
  224. package/src/three/authoring/oid-source-persistence.ts +7 -7
  225. package/src/three/authoring/r3f-design-session.ts +58 -54
  226. package/src/three/authoring/r3f-source-authoring-adapter.ts +95 -91
  227. package/src/three/authoring/reflection-probe-inspector-section.tsx +3 -2
  228. package/src/three/authoring/three-authoring-adapter.ts +29 -29
  229. package/src/three/component-verbs/extract-menu.ts +7 -6
  230. package/src/three/component-verbs/fork-menu.ts +7 -6
  231. package/src/three/component-verbs/internals-menu.ts +2 -2
  232. package/src/three/story-documents/three-story-documents.tsx +13 -13
  233. package/src/three/three-board/ThreeBoardDocument.tsx +13 -12
  234. package/src/three/three-board/board-scene.ts +6 -6
  235. package/src/three/three-board/three-component-board.ts +2 -2
  236. package/src/services/game-audio-unlock.ts +0 -48
@@ -0,0 +1,473 @@
1
+ /**
2
+ * The editor's mount of a `three` root whose entry module DEFAULT-EXPORTS a
3
+ * React component — the world a game's own boot renders inside `<Canvas>`:
4
+ *
5
+ * ```tsx
6
+ * export default function World() {
7
+ * return <><ambientLight /><mesh>…</mesh></>;
8
+ * }
9
+ * ```
10
+ *
11
+ * The world is an ordinary R3F app. The editor renders it bare into a Fiber
12
+ * root on the host's canvas and renderer, drives Fiber's `frameloop: 'never'`
13
+ * scheduler from the game's own loop. A game's input is its own: the session's
14
+ * input door reaches it through the entry's `debug.input` export.
15
+ *
16
+ * WHOSE REACT AND FIBER. The entry's hooks resolve `react` and
17
+ * `@react-three/fiber` through the project's module graph, so the root that
18
+ * renders it must be built with the same instances: {@link R3FRuntime} is
19
+ * handed in by `r3f-entry-runtime.ts`, which takes them from the R3F doorway
20
+ * under the packaged runtime and from its own imports in a checkout, where one
21
+ * Vite graph already shares them.
22
+ *
23
+ * three.js identity: nothing here imports `three` for scene objects. Fiber's
24
+ * catalogue is extended with the host's `three` (`host.three`), the same
25
+ * instance the returned scene and camera belong to.
26
+ */
27
+
28
+ import type * as Fiber from '@react-three/fiber';
29
+ import type { RootState } from '@react-three/fiber';
30
+ import type { MountedThreeRoot, RootAdapter } from '@volter/editor-project/adapter';
31
+ import type { SystemAdapters } from '@volter/editor-project/adapter/system-adapter';
32
+ import {
33
+ type RenderVitalsRegistration,
34
+ registerRenderVitals,
35
+ } from '../../runtime/dev/register-render-vitals';
36
+ import {
37
+ createRenderDebugAdapter,
38
+ frameCaptureContextFor,
39
+ type RenderDebugWiring,
40
+ } from '../../runtime/dev/render-debug-adapter';
41
+ import { collectRenderMemory } from '../../runtime/dev/render-memory';
42
+ import { RENDER_SUBMIT_PHASE } from '../../runtime/dev/render-vitals';
43
+ import { createWebGLFrameCapture } from '../../runtime/dev/webgl-frame-capture';
44
+ import { getDebugRegistry } from '../../runtime/debug-registry';
45
+ import { devBuildEnabled } from '../../runtime/dev-build';
46
+ import type { GameThreeHostContext } from '../../runtime/host-context';
47
+ import type * as React from 'react';
48
+ import type { ComponentType, PropsWithChildren } from 'react';
49
+
50
+ /** The React and Fiber a three world is mounted with — the ones its own hooks resolve. */
51
+ export interface R3FRuntime {
52
+ readonly createElement: typeof React.createElement;
53
+ readonly Fragment: typeof React.Fragment;
54
+ readonly Component: typeof React.Component;
55
+ readonly useEffect: typeof React.useEffect;
56
+ readonly createRoot: typeof Fiber.createRoot;
57
+ readonly extend: typeof Fiber.extend;
58
+ readonly advance: typeof Fiber.advance;
59
+ readonly flushSync: typeof Fiber.flushSync;
60
+ readonly events: typeof Fiber.events;
61
+ }
62
+
63
+ /** The slice of `WebGLRenderer.info` the vitals reporter reads. Declared
64
+ * structurally rather than imported from `three`, per this module's own
65
+ * identity rule (see the header): a duck-typed host renderer legitimately has
66
+ * no `info` at all, which is why every read of it is guarded. */
67
+ interface RendererCounters {
68
+ readonly render: { readonly calls: number; readonly triangles: number; readonly frame: number };
69
+ readonly memory: { readonly geometries: number; readonly textures: number };
70
+ }
71
+
72
+ interface R3FEntryModuleExports {
73
+ /** The one shape: the world IS a component. */
74
+ readonly default?: ComponentType;
75
+ }
76
+
77
+ type MountErrorBoundaryProps = PropsWithChildren<{ onError: (error: Error) => void }>;
78
+
79
+ /** React errors must reject a mount in Node too, where there is no window
80
+ * error event. This boundary changes no authored content on successful mounts.
81
+ * Built on the runtime's own `Component`, once per runtime. */
82
+ const boundaries = new WeakMap<object, ComponentType<MountErrorBoundaryProps>>();
83
+ function mountErrorBoundary(runtime: R3FRuntime): ComponentType<MountErrorBoundaryProps> {
84
+ const known = boundaries.get(runtime.Component);
85
+ if (known) return known;
86
+ class MountErrorBoundary extends runtime.Component<MountErrorBoundaryProps, { failed: boolean }> {
87
+ override state = { failed: false };
88
+ static getDerivedStateFromError(): { failed: boolean } {
89
+ return { failed: true };
90
+ }
91
+ override componentDidCatch(error: Error): void {
92
+ this.props.onError(error);
93
+ }
94
+ override render() {
95
+ return this.state.failed ? null : this.props.children;
96
+ }
97
+ }
98
+ boundaries.set(runtime.Component, MountErrorBoundary);
99
+ return MountErrorBoundary;
100
+ }
101
+
102
+ /**
103
+ * Build the `RootAdapter` for one default-exported R3F world. Mounts through
104
+ * react-three-fiber, gated entirely by the host's own loop and rendering
105
+ * through the host's own `WebGLRenderer` — never a second renderer, never a
106
+ * second `requestAnimationFrame` loop.
107
+ */
108
+ function threeWorldAdapter(id: string, component: ComponentType, runtime: R3FRuntime): RootAdapter {
109
+ const { createElement, Fragment, useEffect, createRoot, extend, advance, flushSync } = runtime;
110
+ const MountErrorBoundary = mountErrorBoundary(runtime);
111
+ const content = createElement(component);
112
+
113
+ return {
114
+ id,
115
+
116
+ async mount(host: GameThreeHostContext): Promise<MountedThreeRoot> {
117
+ // Headless mounts use this same Fiber reconciler and authored tree.
118
+ // Their host supplies an inert draw command; absence of a GPU is not
119
+ // permission to return a successful but empty scene.
120
+ // Fiber v9 made the THREE catalogue tree-shakeable: `<Canvas>` calls
121
+ // `extend(THREE)` for you, a bare `createRoot` does NOT — without this,
122
+ // the FIRST three intrinsic in the tree (`<color>`, `<ambientLight>`,
123
+ // …) throws "X is not part of the THREE namespace! Did you forget to
124
+ // extend?" at reconcile time. Extending with `host.three` (not a fresh
125
+ // `import * as THREE`) keeps the catalogue on the host's deduped three
126
+ // instance — the same identity rule the returned scene/camera rely on.
127
+ // `extend` merges into a module-global catalogue, so calling it once
128
+ // per mount is idempotent.
129
+ extend(host.three as unknown as Parameters<typeof extend>[0]);
130
+
131
+ const gameDebugRegistry = host.game ? getDebugRegistry(host.game) : null;
132
+
133
+ const canvas = host.surface.canvas;
134
+ const root = createRoot(canvas);
135
+
136
+ // `RootState` (the live scene/camera/gl fiber built) only arrives via
137
+ // the `onCreated` callback — `root.render()`'s return value is
138
+ // TECHNICALLY the same store, but `onCreated` is the supported hook, and
139
+ // waiting for it (rather than assuming the first commit already ran
140
+ // synchronously) is the honest choice under React 19's concurrent
141
+ // renderer, which does not guarantee a synchronous first commit the way
142
+ // legacy ReactDOM.render did.
143
+ let resolveState!: (state: RootState) => void;
144
+ const statePromise = new Promise<RootState>((resolve) => {
145
+ resolveState = resolve;
146
+ });
147
+ let resolveEffectsReady!: () => void;
148
+ const effectsReady = new Promise<void>((resolve) => {
149
+ resolveEffectsReady = resolve;
150
+ });
151
+ // Rendered after the game's content. React runs passive effects in tree
152
+ // order, so when this fires every provider/command effect in the
153
+ // initial tree has registered. A Play acknowledgement is a claim that
154
+ // the mounted game's command surface exists, not merely that Fiber
155
+ // allocated a scene.
156
+ function MountEffectsReady(): null {
157
+ useEffect(resolveEffectsReady, []);
158
+ return null;
159
+ }
160
+
161
+ // `root.configure()` is ASYNC in fiber v9 (`Promise<ReconcilerRoot>`) —
162
+ // it must be awaited before `render()`.
163
+ await root.configure({
164
+ // The engine's renderer, not a second one — fiber renders THROUGH
165
+ // `host.renderer` instead of constructing its own `WebGLRenderer`.
166
+ gl: host.renderer,
167
+ // …and the host's shadow map, not fiber's.
168
+ //
169
+ // `configure()` runs `gl.shadowMap.enabled = !!shadows`
170
+ // UNCONDITIONALLY, against its own default of `false`. Passing nothing
171
+ // therefore does not mean "leave it alone" — it means "turn shadows
172
+ // off on the renderer the host just configured" (FX-5: every three
173
+ // root silently read flat, with no error anywhere). Reading it back
174
+ // off the renderer is exactly "don't clobber": a boolean also pins
175
+ // `shadowMap.type` to `PCFSoftShadowMap`, which is what
176
+ // `createHostRenderer` already sets, so the round trip is lossless
177
+ // and the HOST stays the one deciding.
178
+ //
179
+ // Optional-chained even though `WebGLRenderer.shadowMap` is not
180
+ // optional: a headless/stub host renderer is a real shape in this
181
+ // repo's tests, and fiber's own `if (gl.shadowMap)` guard means the
182
+ // value it reads there is ignored anyway.
183
+ shadows: host.renderer.shadowMap?.enabled ?? false,
184
+ // The engine's gated loop is the ONLY loop — fiber must never run its
185
+ // own rAF (that would defeat editor pause; `drivesOwnLoop: false`).
186
+ frameloop: 'never',
187
+ // `RenderProps.size` types as the FULL `Size` (width/height/top/left),
188
+ // so `top`/`left` are pinned to 0 (this bridge always fills its whole
189
+ // canvas; no offset viewport).
190
+ size: { width: host.surface.width, height: host.surface.height, top: 0, left: 0 },
191
+ // Fiber's pointer-event manager, passed EXPLICITLY. There IS no
192
+ // default for a bare `createRoot`: the `events = createPointerEvents`
193
+ // default lives in the `<Canvas>` COMPONENT, which this mount does not
194
+ // use, and `configure` gates on `if (events && !state.events.handlers)`.
195
+ // With `events` undefined the store keeps its initial
196
+ // `{ priority, enabled, connected: false }` — no handlers, nothing
197
+ // bound to the canvas, no error — and every mesh-level pointer prop
198
+ // (`onClick`, `onPointerOver`, `onPointerMissed`) is dead. Proven by a
199
+ // control experiment differing ONLY in this property.
200
+ ...(host.headless ? {} : { events: runtime.events }),
201
+ onCreated: (state) => resolveState(state),
202
+ });
203
+
204
+ // No `<StrictMode>` — the host mounts once; StrictMode's deliberate
205
+ // double-invoke of effects would double-subscribe `useFrame` callbacks
206
+ // against a host loop that only ticks once per frame.
207
+ const world = createElement(
208
+ Fragment,
209
+ null,
210
+ content,
211
+ createElement(MountEffectsReady, { key: 'vgai-mount-effects-ready' }),
212
+ );
213
+ // A reconcile-time crash (e.g. a missing `extend` catalogue entry)
214
+ // surfaces as an uncaught window error and `onCreated` never fires —
215
+ // without this guard, `mount()` would await `statePromise` FOREVER and
216
+ // silently wedge every world declared after this one (roots mount
217
+ // sequentially). Convert that class of failure into a loud mount error.
218
+ const state = await new Promise<RootState>((resolve, reject) => {
219
+ const fail = (message: string) => {
220
+ cleanup();
221
+ root.unmount();
222
+ gameDebugRegistry?.strip(id);
223
+ reject(
224
+ new Error(`three world "${id}": fiber crashed before its first commit — ${message}`),
225
+ );
226
+ };
227
+ const onError = (event: ErrorEvent) => {
228
+ fail(event.message);
229
+ };
230
+ const timer = setTimeout(() => {
231
+ cleanup();
232
+ root.unmount();
233
+ gameDebugRegistry?.strip(id);
234
+ reject(
235
+ new Error(
236
+ `three world "${id}": onCreated did not fire within 10s — the R3F tree never ` +
237
+ 'reached its first commit (mount() fails loudly instead of hanging)',
238
+ ),
239
+ );
240
+ }, 10_000);
241
+ const cleanup = () => {
242
+ clearTimeout(timer);
243
+ if (typeof window !== 'undefined') window.removeEventListener('error', onError);
244
+ };
245
+ if (typeof window !== 'undefined') window.addEventListener('error', onError);
246
+ root.render(
247
+ createElement(MountErrorBoundary, { onError: (error) => fail(error.message) }, world),
248
+ );
249
+ void Promise.all([statePromise, effectsReady]).then(([s]) => {
250
+ cleanup();
251
+ resolve(s);
252
+ });
253
+ });
254
+
255
+ // Clock hardening: fiber's internal `update()` calls
256
+ // `state.clock.getDelta()` BEFORE its `frameloop:'never'` branch, and a
257
+ // RUNNING (or autoStart) `THREE.Clock` accumulates WALL time into
258
+ // `elapsedTime` as a side effect — skewing the deltas the 'never'
259
+ // branch derives from the game timestamps `update(dt)` feeds below.
260
+ // Stopped + autoStart=false makes `getDelta()` a pure no-op, so
261
+ // `useFrame` deltas come from game time alone.
262
+ state.clock.autoStart = false;
263
+ state.clock.stop();
264
+
265
+ // THE STORE IS THE TRUTH, NOT THE `onCreated` SNAPSHOT.
266
+ //
267
+ // `state` is one immutable zustand snapshot: every `set(...)` produces a
268
+ // NEW state object. A world that legitimately REPLACES the default
269
+ // camera — `useThree(s => s.set)({ camera })`, which is exactly what
270
+ // drei's `makeDefault` does — never reaches anything read off `state`
271
+ // directly, and fiber's own `update()` renders
272
+ // `state.gl.render(state.scene, state.camera)` off the state object it
273
+ // is HANDED. Measured: an authored `OrthographicCamera` world rendered
274
+ // from fiber's default (0, 0, 5) forever, culling every ground polygon.
275
+ // `state.get()` is fiber's own `StoreApi.getState`, carried on
276
+ // `RootState` for precisely this reason; it is a plain map read.
277
+ const live = (): RootState => state.get();
278
+
279
+ // --- Live render vitals, host-seeded ---------------------------------
280
+ // A running game must be able to explain its own frame cost through the
281
+ // debug registry, with no capability to install and nothing for a game
282
+ // to write. Gate: `devBuildEnabled()` (the ONE owner of "is this a dev
283
+ // context" — a ship build registers nothing and pays nothing) and a
284
+ // `Game` shell (the readings are folded out of that game's profiler
285
+ // frames). A headless host cannot supply GPU readings.
286
+ let renderVitals: RenderVitalsRegistration | null = null;
287
+ if (!host.headless && host.game && devBuildEnabled()) {
288
+ // The readings are folded out of profiler frames, and the profiler is
289
+ // a flag the editor already sets on play — under the dev gate this is
290
+ // the same cost arriving a little earlier, not a new one.
291
+ host.game.profiler.enabled = true;
292
+ if (!gameDebugRegistry) {
293
+ throw new Error(`three world "${id}": mounted Game has no debug registry.`);
294
+ }
295
+ renderVitals = registerRenderVitals({
296
+ registry: gameDebugRegistry,
297
+ worldId: id,
298
+ profiler: host.game.profiler,
299
+ scene: live().scene,
300
+ renderer: host.renderer,
301
+ });
302
+ }
303
+
304
+ // Engine-owned render introspection, built over the SAME renderer and
305
+ // scene Fiber uses. A design-time stand-in has no context; a headless
306
+ // mount also honestly omits the GPU capture capability.
307
+ const rendererContext =
308
+ typeof host.renderer.getContext === 'function' ? host.renderer.getContext() : undefined;
309
+ const frameCaptureContext = frameCaptureContextFor(host.headless ?? false, rendererContext);
310
+ const frameCapture = frameCaptureContext
311
+ ? createWebGLFrameCapture(frameCaptureContext)
312
+ : null;
313
+ const renderDebugWiring: RenderDebugWiring | null = frameCapture
314
+ ? createRenderDebugAdapter({
315
+ capture: frameCapture,
316
+ scene: state.scene,
317
+ memory: () => collectRenderMemory(live().scene, host.renderer.info),
318
+ })
319
+ : null;
320
+ // The session debug registry is host instrumentation, not component
321
+ // context. Publishing the existing adapter here lets coverage and
322
+ // editor panels see the same door without wrapping the R3F tree. The
323
+ // world's own declared systems merge in from the entry module at the
324
+ // Game layer (`installDeclaredSystemAdapters`).
325
+ const systemAdapters: SystemAdapters = gameDebugRegistry
326
+ ? { debug: gameDebugRegistry.adapter }
327
+ : {};
328
+ if (renderDebugWiring) systemAdapters.renderDebug = renderDebugWiring.adapter;
329
+
330
+ // The engine drives every `useFrame` through the mounted world's
331
+ // `update(dt)` hook, never off a raw Fiber loop. This mount declares
332
+ // that indivisible Fiber advance at DISPLAY cadence below: the real
333
+ // host calls it once per presentation, a paused world gets dt=0, and an
334
+ // explicit `Game.play.step()` still ticks it exactly once.
335
+ //
336
+ // `advance(timestamp, runGlobalEffects, state)`'s `timestamp` is
337
+ // consumed as `THREE.Clock.elapsedTime` DIRECTLY when
338
+ // `frameloop:'never'` (verified against fiber's `update()`
339
+ // implementation): `delta = timestamp - clock.elapsedTime;
340
+ // clock.elapsedTime = timestamp`. So `timestamp` must be a
341
+ // monotonically increasing SECONDS value — GAME time, not wall time:
342
+ // `update` simply isn't called while this world is frozen, so
343
+ // accumulating its `dt` means fiber's clock does not advance across a
344
+ // pause. Wall clock would leak the pause duration into the first
345
+ // resumed frame as one giant `useFrame` delta.
346
+ let elapsed = 0;
347
+
348
+ return {
349
+ kind: 'three',
350
+ // Fiber's REAL `THREE.Scene`/`THREE.Camera`. Checkout development
351
+ // dedupes `three`; a registry-installed project and the prebundled
352
+ // editor can legitimately have distinct constructor identities, so
353
+ // editor boundaries recognize Three's stable structural brands
354
+ // (`isScene`, `isCamera`) rather than relying only on `instanceof`.
355
+ //
356
+ // Both are GETTERS over the live store, not snapshot fields: the host
357
+ // and the editor must see the camera the world actually renders
358
+ // through, including one the world swapped in after the first commit
359
+ // (see `live` above).
360
+ get scene() {
361
+ return live().scene;
362
+ },
363
+ get camera() {
364
+ return live().camera;
365
+ },
366
+ drivesOwnLoop: false,
367
+ // Fiber's `advance()` cannot separate its `useFrame` callbacks from
368
+ // its WebGL draw. Running this opaque update in every fixed catch-up
369
+ // substep renders N complete frames inside one rAF callback: once a
370
+ // scene costs more than 16.7ms, the accumulator asks for more draws,
371
+ // those draws make the next gap larger, and the loop spirals to its
372
+ // max-substep ceiling. The host's display pass is the one clock that
373
+ // must own this inseparable native advance.
374
+ updateCadence: 'display',
375
+ // Adapter surface: `debug` (the shared game registry) and, when a real
376
+ // WebGL2 context exists, `renderDebug` are engine-seeded. The game's
377
+ // own capabilities arrive as the entry module's declared `systems`.
378
+ systems: systemAdapters,
379
+ systemScope: live().scene,
380
+ update(dt: number): void {
381
+ elapsed += dt;
382
+ const current = live();
383
+ // Fiber receives an ABSOLUTE timestamp and reconstructs `delta` by subtraction. Publish
384
+ // the exact host-owned dt beside the synchronous advance so compatibility adapters that
385
+ // reproduce another engine's frame clock do not inherit one-ulp cancellation residue.
386
+ try {
387
+ renderDebugWiring?.beforeRender();
388
+ const profiler = host.game?.profiler;
389
+ if (!profiler?.enabled) {
390
+ advance(elapsed, true, current);
391
+ return;
392
+ }
393
+ // The frame's CPU submission cost, produced where the draw
394
+ // actually happens: `advance()` under `frameloop: 'never'` is what
395
+ // calls `gl.render(scene, camera)`, and this bracket is also what
396
+ // makes this frame a PRESENTATION as far as the vitals fold is
397
+ // concerned (`../dev/render-vitals.ts`: no bracket, no display
398
+ // frame). The profiler's phase clock is a stack, so bracketing
399
+ // here cannot truncate an enclosing phase.
400
+ const info = host.renderer.info as RendererCounters | undefined;
401
+ const passesBefore = info?.render.frame ?? 0;
402
+ profiler.beginPhase();
403
+ try {
404
+ advance(elapsed, true, current);
405
+ } finally {
406
+ profiler.endPhase(RENDER_SUBMIT_PHASE);
407
+ }
408
+ // Checked, not assumed — a duck-typed/design-time renderer
409
+ // supplies only what a mount needs, and a mount with no counters
410
+ // must report NOTHING rather than throw once per frame. `gpuMs` is
411
+ // null: this path runs no GPU timer, and the profiler keeps the
412
+ // previous reading rather than inventing a zero.
413
+ if (info) {
414
+ profiler.reportRender({
415
+ gpuMs: null,
416
+ drawCalls: info.render.calls,
417
+ triangles: info.render.triangles,
418
+ geometries: info.memory.geometries,
419
+ textures: info.memory.textures,
420
+ renderPasses: info.render.frame - passesBefore,
421
+ });
422
+ }
423
+ } finally {
424
+ renderDebugWiring?.afterRender();
425
+ }
426
+ },
427
+ resize(width: number, height: number): void {
428
+ live().setSize(width, height);
429
+ },
430
+ dispose(): void {
431
+ // Reject/restore a pending capture while its renderables still
432
+ // exist, then unmount component effects and strip this world's
433
+ // registrations.
434
+ renderDebugWiring?.dispose();
435
+ // The editor audits and disposes the mounted game's window/document
436
+ // realm immediately after `session.stop()`. Fiber's ordinary
437
+ // `unmount()` submits a concurrent reconciler update, so component
438
+ // effect cleanups could still be pending at that boundary and the
439
+ // audit would truthfully report/reclaim listeners the world was
440
+ // about to remove itself. Force the unmount COMMIT (not fiber's
441
+ // deliberately delayed renderer/context disposal) to finish here: a
442
+ // stopped root owns no live component lifecycle beyond this method.
443
+ flushSync(() => root.unmount());
444
+ // The vitals fold owns two `profiler.subscribe` registrations —
445
+ // ended here, before the strip, so a subscription can never outlive
446
+ // the door it feeds.
447
+ renderVitals?.dispose();
448
+ renderVitals = null;
449
+ // `strip(id)` ends this world's PROVIDERS AND COMMANDS (the
450
+ // render-vitals door above is the one this mount registers under
451
+ // `id`); sibling roots are untouched. It does NOT end the input
452
+ // seams `wireGameInputSeams` set — see that module's LIFETIME note.
453
+ gameDebugRegistry?.strip(id);
454
+ },
455
+ };
456
+ },
457
+ };
458
+ }
459
+
460
+ /**
461
+ * What a three entry module MEANS: a default-exported component is a world;
462
+ * anything else is `null`, and each caller decides what that means (Play
463
+ * refuses the root by name, the design session declines the world).
464
+ */
465
+ export function resolveR3FEntryAdapter(
466
+ entryModule: unknown,
467
+ rootId: string,
468
+ runtime: R3FRuntime,
469
+ ): RootAdapter | null {
470
+ const mod = entryModule as R3FEntryModuleExports | undefined;
471
+ if (typeof mod?.default === 'function') return threeWorldAdapter(rootId, mod.default, runtime);
472
+ return null;
473
+ }
@@ -3,11 +3,9 @@
3
3
  * `ingest-react` (a vendored React game reached through a host shim).
4
4
  *
5
5
  * They mount through the SAME react host stack: the same realm entry load, the
6
- * same default-export-component validation, the same `<WorldProvider>` wrap,
7
- * the same `createRoot(...).render(...)` body. Only the identity label
8
- * differs, so a failure is attributed to the right one. "One stacking model,
9
- * no special case": the automatic `<WorldProvider>` wrap is context-only and
10
- * harmless to a foreign tree.
6
+ * same default-export-component validation, the same `createRoot(...).render(<Entry />)`
7
+ * the game's own boot performs. Entry validation names the identity, so a failure is
8
+ * attributed to the right one.
11
9
  *
12
10
  * An ingest-react world's `entry` is NOT a first-party component — it is a
13
11
  * HOST-SHIM module living beside the ingested game's manifest, importing the
@@ -17,12 +15,10 @@
17
15
  * declared `entry` is ever imported, so that entry point is never reached.
18
16
  */
19
17
 
20
- import type { MountedReactGame, ReactRootAdapter } from '@volter/game-runtime/runtime/create-runtime';
21
- import type { Game } from '@volter/game-runtime/runtime/game';
22
- import type { GameDomHostContext } from '@volter/game-runtime/runtime/host-context';
18
+ import type { MountedReactGame, ReactRootAdapter } from '../../runtime/create-runtime';
19
+ import type { GameDomHostContext } from '../../runtime/host-context';
23
20
  import type { ResolvedAdapterRoot } from '@volter/editor-project/manifest/load';
24
21
  import type { ComponentType } from 'react';
25
- import { resolveWorldProviderForProject } from '../react-mount-runtime';
26
22
  import type { RealmServices } from '../realm-services';
27
23
 
28
24
  /**
@@ -47,33 +43,10 @@ function loadReactEntryComponent(
47
43
  return mod.default as ComponentType;
48
44
  }
49
45
 
50
- /**
51
- * `DomHostContext.game` is a typed, guaranteed field on the native multi-root
52
- * mount path. The loud check here stays for the one host that legitimately
53
- * carries none — a `dom` sibling mounted BESIDE an ingest root
54
- * (`ingest-siblings.ts`) has no native `Game`, and that host renders the entry
55
- * component BARE rather than fabricating an empty Game into `<WorldProvider>`.
56
- * A hand-rolled host that skipped the engine's mount path must fail loudly
57
- * here, never lie an `undefined` Game into the provider.
58
- */
59
- function requireHostGame(world: ResolvedAdapterRoot, host: GameDomHostContext, label: string): Game {
60
- const game = host.game;
61
- if (!game) {
62
- throw new Error(
63
- `resolveRootBinding: world "${world.id}" (${label}) mount() received a DomHostContext ` +
64
- 'with no `game` — WorldProvider needs a live Game at mount time and this host did not ' +
65
- 'carry one.',
66
- );
67
- }
68
- return game;
69
- }
70
-
71
46
  /**
72
47
  * The shared entry-loading step, exported so a composite's `dom` SIBLING mount
73
48
  * (`ingest-siblings.ts`) reuses the EXACT SAME `entry`-required check and
74
- * component-shape validation without duplicating any of it. The sibling then
75
- * renders the returned component bare; {@link resolveDomAdapter} below wraps
76
- * it in `<WorldProvider>`.
49
+ * component-shape validation without duplicating any of it.
77
50
  */
78
51
  export async function resolveReactAdapterRootComponent(
79
52
  world: ResolvedAdapterRoot,
@@ -96,9 +69,7 @@ export async function resolveReactAdapterRootComponent(
96
69
  /** Build the `mount` half both identities share. */
97
70
  function reactMount(
98
71
  world: ResolvedAdapterRoot,
99
- label: 'dom' | 'ingest-react',
100
72
  Entry: ComponentType,
101
- WorldProvider: ComponentType<{ game: Game; children?: unknown }>,
102
73
  realm: RealmServices,
103
74
  /** Commit the initial DOM synchronously. The play authoring adapter is
104
75
  * installed immediately after `mount()` resolves, so a first-party root
@@ -109,7 +80,6 @@ function reactMount(
109
80
  return {
110
81
  id: world.id,
111
82
  async mount(host: GameDomHostContext): Promise<MountedReactGame> {
112
- const game = requireHostGame(world, host, label);
113
83
  // The runtime comes from the realm — the PROJECT's own react under the
114
84
  // packaged runtime, the editor's static imports otherwise — so this
115
85
  // mount always shares ONE react instance with the entry component's own
@@ -117,9 +87,7 @@ function reactMount(
117
87
  const runtime = await realm.reactDomRuntime();
118
88
  const root = runtime.createRoot(host.container);
119
89
  const render = () => {
120
- root.render(
121
- runtime.createElement(WorldProvider, { game, children: runtime.createElement(Entry) }),
122
- );
90
+ root.render(runtime.createElement(Entry));
123
91
  };
124
92
  if (flushInitialRender) runtime.flushSync(render);
125
93
  else render();
@@ -142,8 +110,7 @@ export async function resolveDomAdapter(
142
110
  onEntryModule?: ((module: Record<string, unknown>) => void) | undefined,
143
111
  ): Promise<ReactRootAdapter> {
144
112
  const Entry = await resolveReactAdapterRootComponent(world, realm, onEntryModule);
145
- const WorldProvider = await resolveWorldProviderForProject();
146
- return reactMount(world, 'dom', Entry, WorldProvider, realm, true);
113
+ return reactMount(world, Entry, realm, true);
147
114
  }
148
115
 
149
116
  /**
@@ -173,9 +140,8 @@ export async function resolveIngestReactAdapter(
173
140
  }
174
141
  const mod = await realm.loadEntryModule(world.entry, world.id, 'dom');
175
142
  const Entry = loadReactEntryComponent(world, mod, 'ingest-react');
176
- const WorldProvider = await resolveWorldProviderForProject();
177
143
  return {
178
- adapter: reactMount(world, 'ingest-react', Entry, WorldProvider, realm, false),
144
+ adapter: reactMount(world, Entry, realm, false),
179
145
  module: mod,
180
146
  };
181
147
  }
@@ -103,8 +103,6 @@ export const BUNDLE_RUNTIME_MODULE_LOADERS: Record<string, () => Promise<unknown
103
103
  // the editor share one live module namespace.
104
104
  '@volter/threejs-runtime/adapter/constraint': () =>
105
105
  import('@volter/threejs-runtime/adapter/constraint'),
106
- '@volter/game-runtime/adapter/first-party-audio-system': () =>
107
- import('@volter/game-runtime/adapter/first-party-audio-system'),
108
106
  '@volter/threejs-runtime/adapter/first-party-navigation-system': () =>
109
107
  import('@volter/threejs-runtime/adapter/first-party-navigation-system'),
110
108
  '@volter/threejs-runtime/adapter/hierarchy-marks': () =>
@@ -126,17 +124,7 @@ export const BUNDLE_RUNTIME_MODULE_LOADERS: Record<string, () => Promise<unknown
126
124
  }),
127
125
  '@volter/game-runtime/data/curve': () => import('@volter/game-runtime/data/curve'),
128
126
  '@volter/game-runtime/data/data-asset': () => import('@volter/game-runtime/data/data-asset'),
129
- '@volter/game-runtime/dev/instruments': () => import('@volter/game-runtime/dev/instruments'),
130
127
  '@volter/threejs-runtime/loader': () => import('@volter/threejs-runtime/loader'),
131
- '@volter/game-runtime/react/world-state': () => import('@volter/game-runtime/react/world-state'),
132
- '@volter/game-runtime/runtime/debug-registry': () =>
133
- import('@volter/game-runtime/runtime/debug-registry'),
134
- '@volter/game-runtime/runtime/game': () => import('@volter/game-runtime/runtime/game'),
135
- '@volter/game-runtime/world3d-react': () => import('@volter/game-runtime/world3d-react'),
136
- '@volter/game-runtime/world3d-react/rapier-physics-bridge': () =>
137
- import('@volter/game-runtime/world3d-react/rapier-physics-bridge'),
138
- // …and the factory that mounts an example's default-exported world. A
139
- // three root cannot mount hosted without it.
140
128
  // The mesh-kit's two library-backed modifiers. `src/lib/mesh/modifiers.ts` —
141
129
  // scaffolded into EVERY default project by the humanoid capability, and
142
130
  // present verbatim in the template and four examples — imports both at
@@ -165,17 +153,11 @@ export const BUNDLE_RUNTIME_MODULE_LOADERS: Record<string, () => Promise<unknown
165
153
  '@volter/threejs-runtime/setup/setup-renderer': () =>
166
154
  import('@volter/threejs-runtime/setup/setup-renderer'),
167
155
  // littlest-tokyo (GLTF quick-start) extras:
168
- '@volter/game-runtime/input/rebind-controller': () =>
169
- import('@volter/game-runtime/input/rebind-controller'),
170
156
  // The generic fetched-asset parse error — the template's and
171
157
  // top-down-strategy's `runtime/pixi-adapter.ts` raise it, so a canvas root
172
158
  // cannot mount hosted without it registered.
173
159
  '@volter/threejs-runtime/asset-parse-error': () =>
174
160
  import('@volter/threejs-runtime/asset-parse-error'),
175
- // rendering-scale's render lab drives the engine's batch renderer + scoped
176
- // render settings directly:
177
- '@volter/threejs-runtime/render/render-batch-system': () =>
178
- import('@volter/threejs-runtime/render/render-batch-system'),
179
161
  '@volter/threejs-runtime/render/render-settings': () =>
180
162
  import('@volter/threejs-runtime/render/render-settings'),
181
163
  // The bundle inlines @colyseus/schema (modular-action's rooms), and the
@@ -189,7 +171,7 @@ export const BUNDLE_RUNTIME_MODULE_LOADERS: Record<string, () => Promise<unknown
189
171
  // capabilities. Each entry names its importer, so a removal is checked
190
172
  // against the same sweep.
191
173
  '@colyseus/sdk': () => import('@colyseus/sdk'), // template main.ts
192
- 'react-dom': () => import('react-dom'), // react-root capability's flushSync
174
+ 'react-dom': () => import('react-dom'), // template main.ts
193
175
  // ---- Editor-citizen modules PROJECT TOOL CONTRIBUTIONS import. A
194
176
  // contribution is project source rendered inside the editor's own panels,
195
177
  // so these must resolve to the editor's LIVE modules — a second copy of the
@@ -6,9 +6,9 @@
6
6
  * endpoint and the console is this shell-side bridge, mounted once by the
7
7
  * session's viewport panel.
8
8
  */
9
- import { getServerValidationLog } from '@volter/editor-core/api/project-state';
9
+ import { getServerValidationLog } from '@volter/editor-sdk/kit/api/project-state';
10
10
  import { emitServerLogEntry } from '@volter/editor-sdk/kit/editor-console';
11
- import { connectEvents } from '@volter/editor-core/editor-presence';
11
+ import { connectEvents } from '@volter/editor-sdk/kit/editor-presence';
12
12
 
13
13
  /**
14
14
  * Connect to the editor SSE endpoint and pipe `server-log` events
@@ -16,7 +16,7 @@
16
16
  * the owner has retired AND no borrow is outstanding.
17
17
  */
18
18
 
19
- import { disposeStoryObject3D, type MountedStoryObject3D } from '@volter/editor-core/stories/story-three-preview';
19
+ import { disposeStoryObject3D, type MountedStoryObject3D } from './story-three-preview';
20
20
 
21
21
  export interface MountedStoryViewportSource {
22
22
  readonly build: () => {