@volter/editor-game 0.5.65

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 (302) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +23 -0
  3. package/contributions/asset-budget-asset.menu.ts +26 -0
  4. package/contributions/asset-budget.action.ts +18 -0
  5. package/contributions/asset-budget.document.tsx +21 -0
  6. package/contributions/asset-budget.menu.ts +22 -0
  7. package/contributions/audio-unlock.service.ts +17 -0
  8. package/contributions/audio.utility.tsx +15 -0
  9. package/contributions/autoplay.service.ts +48 -0
  10. package/contributions/bridge.command.ts +172 -0
  11. package/contributions/build-profiles.document.tsx +21 -0
  12. package/contributions/build-progress.status.tsx +45 -0
  13. package/contributions/build.action.ts +26 -0
  14. package/contributions/build.command.ts +27 -0
  15. package/contributions/build.header.tsx +37 -0
  16. package/contributions/build.menu.ts +31 -0
  17. package/contributions/build.service.ts +31 -0
  18. package/contributions/connection.status.tsx +43 -0
  19. package/contributions/coverage.service.ts +118 -0
  20. package/contributions/edit-mode-audio.service.ts +23 -0
  21. package/contributions/edit-mode-networking.service.ts +30 -0
  22. package/contributions/game-document.service.ts +25 -0
  23. package/contributions/game-eval.command.ts +210 -0
  24. package/contributions/game.layout.ts +19 -0
  25. package/contributions/gameplay.command.ts +255 -0
  26. package/contributions/generation.service.ts +96 -0
  27. package/contributions/generations.status.tsx +53 -0
  28. package/contributions/ingest.service.ts +80 -0
  29. package/contributions/instances.command.ts +75 -0
  30. package/contributions/navmesh.menu.ts +39 -0
  31. package/contributions/navmesh.service.ts +19 -0
  32. package/contributions/network.utility.tsx +17 -0
  33. package/contributions/play.command.ts +355 -0
  34. package/contributions/profiler.action.ts +16 -0
  35. package/contributions/profiler.menu.ts +22 -0
  36. package/contributions/profiler.utility.tsx +17 -0
  37. package/contributions/react/component-board.service.ts +21 -0
  38. package/contributions/react/design-time-mount.service.ts +60 -0
  39. package/contributions/react/pasteboard.action.ts +41 -0
  40. package/contributions/react/react-inspector.service.ts +85 -0
  41. package/contributions/react/story-documents.service.ts +44 -0
  42. package/contributions/scene-document.service.ts +32 -0
  43. package/contributions/state-watch.action.ts +17 -0
  44. package/contributions/state-watch.menu.ts +23 -0
  45. package/contributions/state-watch.utility.tsx +20 -0
  46. package/contributions/team-playtest.service.ts +124 -0
  47. package/contributions/three/camera-runtime.inspector.tsx +34 -0
  48. package/contributions/three/component-board.service.ts +24 -0
  49. package/contributions/three/component-verbs.command.ts +110 -0
  50. package/contributions/three/component-verbs.service.ts +92 -0
  51. package/contributions/three/constraints.inspector.tsx +34 -0
  52. package/contributions/three/model-asset-sections.service.ts +26 -0
  53. package/contributions/three/reflection-probe-capture.inspector.tsx +32 -0
  54. package/contributions/three/story-documents.service.ts +31 -0
  55. package/contributions/three/three-authoring.service.ts +66 -0
  56. package/contributions/transport.header.tsx +19 -0
  57. package/contributions/xstate-behavior.action.ts +42 -0
  58. package/contributions/xstate-behavior.document.tsx +73 -0
  59. package/contributions/xstate-behavior.inspector.tsx +28 -0
  60. package/contributions/xstate-behavior.menu.ts +23 -0
  61. package/package.json +144 -0
  62. package/src/asset-budget/AssetBudgetPanel.tsx +1172 -0
  63. package/src/asset-budget/asset-budget-model.ts +799 -0
  64. package/src/asset-budget/basis-encoder.ts +165 -0
  65. package/src/asset-budget/gltf-io.ts +154 -0
  66. package/src/asset-budget/gltf-optimize.ts +384 -0
  67. package/src/asset-budget/image-dims.ts +78 -0
  68. package/src/asset-budget/optimize-apply.ts +221 -0
  69. package/src/audio/AudioDebuggerPanel.tsx +457 -0
  70. package/src/audio/audio-debugger-model.ts +87 -0
  71. package/src/bridge/call.ts +60 -0
  72. package/src/bridge/dispatch.ts +459 -0
  73. package/src/bridge/live-frames.ts +25 -0
  74. package/src/bridge/screenshot.ts +316 -0
  75. package/src/build/BuildProfilesPanel.tsx +476 -0
  76. package/src/build/build-session.ts +247 -0
  77. package/src/build/format-bytes.ts +14 -0
  78. package/src/command-results.ts +36 -0
  79. package/src/coverage/live-authoring-surface.ts +30 -0
  80. package/src/coverage/live-project-verbs.ts +162 -0
  81. package/src/coverage/native-system-coverage.ts +143 -0
  82. package/src/coverage/root-coverage.ts +79 -0
  83. package/src/coverage/session-coverage.ts +193 -0
  84. package/src/design-system-stories/ApplicationChrome.stories.tsx +100 -0
  85. package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +406 -0
  86. package/src/edit-mode/edit-mode-audio.ts +56 -0
  87. package/src/edit-mode/edit-mode-networking.ts +112 -0
  88. package/src/game-document/DevicePresetPicker.tsx +84 -0
  89. package/src/game-document/GameCaptureFrameButton.tsx +45 -0
  90. package/src/game-document/GameDocument.tsx +215 -0
  91. package/src/game-document/GamePanel.tsx +662 -0
  92. package/src/game-document/InstanceInspectorPicker.tsx +183 -0
  93. package/src/game-document/crowd-debug.ts +183 -0
  94. package/src/game-document/device-preview.ts +336 -0
  95. package/src/game-document/game-view-store.ts +107 -0
  96. package/src/game-document/physics-debug.ts +187 -0
  97. package/src/generation/GenerationActivity.tsx +421 -0
  98. package/src/generation/GenerationGallery.css +231 -0
  99. package/src/generation/generation-documents.tsx +338 -0
  100. package/src/generation/generation-jobs.ts +128 -0
  101. package/src/generation/generation-presentation.ts +257 -0
  102. package/src/host/adapter-reach.ts +445 -0
  103. package/src/host/adapter-runtime-bindings.ts +303 -0
  104. package/src/host/after-paint.ts +112 -0
  105. package/src/host/api/configurations.ts +66 -0
  106. package/src/host/authoring/babylon-authoring-adapter.ts +703 -0
  107. package/src/host/authoring/canvas-runtime-recognition.ts +50 -0
  108. package/src/host/authoring/contract-hierarchy-authoring.ts +144 -0
  109. package/src/host/authoring/contract-scenes-stories.ts +204 -0
  110. package/src/host/authoring/creation-site-related.ts +55 -0
  111. package/src/host/authoring/ephemeral-persistence.ts +29 -0
  112. package/src/host/authoring/gesture-persist.ts +84 -0
  113. package/src/host/authoring/ingest-data-writer.ts +232 -0
  114. package/src/host/authoring/ingest-source-persistence.ts +826 -0
  115. package/src/host/authoring/mount-isolated-pixi-screen.ts +250 -0
  116. package/src/host/authoring/mounted-authoring.ts +41 -0
  117. package/src/host/authoring/owned-pixi-ticker-listeners.ts +96 -0
  118. package/src/host/authoring/phaser-live-authoring-adapter.ts +265 -0
  119. package/src/host/authoring/pixi-authoring-adapter.ts +1554 -0
  120. package/src/host/authoring/pixi-creation-site-write-target.ts +60 -0
  121. package/src/host/authoring/pixi-isolation-assets.ts +25 -0
  122. package/src/host/authoring/pixi-live-write-target.ts +979 -0
  123. package/src/host/authoring/pixi-source-identity.ts +141 -0
  124. package/src/host/authoring/pixi-still-presentation.ts +71 -0
  125. package/src/host/authoring/pixi-structure-history.ts +237 -0
  126. package/src/host/authoring/pixi-transform-channels.ts +205 -0
  127. package/src/host/authoring/selection-remount-handoff.ts +23 -0
  128. package/src/host/authoring/source-persistence-backend.ts +373 -0
  129. package/src/host/authoring/source-refresh-revisions.ts +81 -0
  130. package/src/host/authoring/struct-write-pipe.ts +143 -0
  131. package/src/host/auto-frame-window.ts +89 -0
  132. package/src/host/binding-resolver.ts +393 -0
  133. package/src/host/browser-transpile.ts +631 -0
  134. package/src/host/canvas-entry-runtime.ts +95 -0
  135. package/src/host/components/CameraAuthoringOverlay.tsx +216 -0
  136. package/src/host/components/HeaderTelemetry.tsx +341 -0
  137. package/src/host/components/PixiIsolationSceneContent.tsx +280 -0
  138. package/src/host/components/ResolutionPicker.tsx +89 -0
  139. package/src/host/components/ThreeIsolationSceneContent.tsx +180 -0
  140. package/src/host/components/frame-debugger-model.ts +579 -0
  141. package/src/host/components/header-telemetry-model.ts +74 -0
  142. package/src/host/components/scene-document.tsx +447 -0
  143. package/src/host/components/utility-view-state.ts +87 -0
  144. package/src/host/components/world-root-stage-binding.tsx +133 -0
  145. package/src/host/components/world-root-stage.ts +1046 -0
  146. package/src/host/coverage/authoring-read-probe.ts +583 -0
  147. package/src/host/coverage/capability-coverage.ts +1587 -0
  148. package/src/host/coverage/coverage-accounting.ts +261 -0
  149. package/src/host/coverage/game-contract-seam-evidence.ts +82 -0
  150. package/src/host/coverage/project-verb-coverage.ts +148 -0
  151. package/src/host/coverage/system-adapter-coverage.ts +373 -0
  152. package/src/host/design-system-stories/StoryLayout.tsx +104 -0
  153. package/src/host/design-system-stories/fixtures/authoring.ts +247 -0
  154. package/src/host/design-system-stories/fixtures/editor-runtime.tsx +115 -0
  155. package/src/host/document-preview-three.ts +110 -0
  156. package/src/host/entry-adjudication.ts +89 -0
  157. package/src/host/game-location-guard.ts +150 -0
  158. package/src/host/game-module-access.ts +196 -0
  159. package/src/host/game-realm-page.ts +358 -0
  160. package/src/host/game-realm-reclaim.ts +55 -0
  161. package/src/host/game-realm-storage.ts +104 -0
  162. package/src/host/gameplay-export.ts +288 -0
  163. package/src/host/gameplay-recording.ts +717 -0
  164. package/src/host/gated-globals.ts +1511 -0
  165. package/src/host/history/json-history-resource.ts +254 -0
  166. package/src/host/ingest/registry.ts +261 -0
  167. package/src/host/instance-extract-actions.ts +120 -0
  168. package/src/host/instance-fork-actions.ts +120 -0
  169. package/src/host/play-control-hook.ts +26 -0
  170. package/src/host/playwright-shim.ts +479 -0
  171. package/src/host/projection/dom.ts +295 -0
  172. package/src/host/projection/pixi.ts +288 -0
  173. package/src/host/r3f-entry-runtime.ts +77 -0
  174. package/src/host/react-mount-runtime.ts +162 -0
  175. package/src/host/realm-services.ts +148 -0
  176. package/src/host/recording-preview.ts +106 -0
  177. package/src/host/roots/module-root.ts +203 -0
  178. package/src/host/roots/react-root.ts +181 -0
  179. package/src/host/same-realm-loop-gate.ts +544 -0
  180. package/src/host/scene-view-drawability.ts +69 -0
  181. package/src/host/sdk/tools.ts +31 -0
  182. package/src/host/served-bundle-runtime-modules.ts +331 -0
  183. package/src/host/server-log-bridge.ts +60 -0
  184. package/src/host/staged-projects.ts +24 -0
  185. package/src/host/stories/mounted-story-viewport-source.ts +64 -0
  186. package/src/host/stories/story-arg-descriptors.ts +50 -0
  187. package/src/host/stories/story-media-presence.ts +152 -0
  188. package/src/host/surface-content.ts +87 -0
  189. package/src/host/take-named-export.ts +23 -0
  190. package/src/host/three-ingest-runtime.ts +76 -0
  191. package/src/host/types-fastnoise-lite.d.ts +7 -0
  192. package/src/host/types-mikktspace.d.ts +20 -0
  193. package/src/host/types-troika-three-text.d.ts +7 -0
  194. package/src/host/use-active-performance-source.ts +53 -0
  195. package/src/host/viewport-pose-memory.ts +48 -0
  196. package/src/host/viewport-root-presentation.ts +40 -0
  197. package/src/ingest/active-ingest.ts +251 -0
  198. package/src/ingest/active-scene-navigation.ts +43 -0
  199. package/src/ingest/authoring/ingest-dom-surface-authoring.ts +191 -0
  200. package/src/ingest/authoring/ingest-root-adapter.ts +897 -0
  201. package/src/ingest/capture-wait-report.ts +122 -0
  202. package/src/ingest/deferred-ingest-play.ts +216 -0
  203. package/src/ingest/deferred-ingest-session.ts +23 -0
  204. package/src/ingest/discovery-public-ingest.ts +242 -0
  205. package/src/ingest/dom-stub-mark.ts +7 -0
  206. package/src/ingest/entry-load.ts +56 -0
  207. package/src/ingest/game-contract-realm.ts +34 -0
  208. package/src/ingest/game-pointer-lock.ts +89 -0
  209. package/src/ingest/held-scene-repaint.ts +73 -0
  210. package/src/ingest/host-surface-box.ts +174 -0
  211. package/src/ingest/ingest-boot-viewport.ts +54 -0
  212. package/src/ingest/ingest-canvas-scene-document.tsx +177 -0
  213. package/src/ingest/ingest-canvas-scene.ts +50 -0
  214. package/src/ingest/ingest-evidence-hook.ts +131 -0
  215. package/src/ingest/ingest-frame-snapshot.ts +183 -0
  216. package/src/ingest/ingest-play-commands.ts +134 -0
  217. package/src/ingest/ingest-play-control.ts +299 -0
  218. package/src/ingest/ingest-render-debug.ts +320 -0
  219. package/src/ingest/ingest-siblings.ts +487 -0
  220. package/src/ingest/ingest-status.ts +62 -0
  221. package/src/ingest/live-ingest-facet.ts +46 -0
  222. package/src/ingest/module-mode.ts +229 -0
  223. package/src/ingest/mount-canvas-ingest-root.ts +899 -0
  224. package/src/ingest/mount-coverage.ts +296 -0
  225. package/src/ingest/mount-dom-ingest-root.ts +282 -0
  226. package/src/ingest/mount-ingest-root.ts +744 -0
  227. package/src/ingest/mount-three-ingest-root.ts +366 -0
  228. package/src/ingest/resolve-canvas.ts +47 -0
  229. package/src/ingest/resolve-three.ts +123 -0
  230. package/src/ingest/served-bundle.ts +109 -0
  231. package/src/ingest/served-html-boot.ts +292 -0
  232. package/src/ingest/surface-canvas.ts +84 -0
  233. package/src/ingest/surface-dom.ts +84 -0
  234. package/src/ingest/surface-three.ts +128 -0
  235. package/src/ingest/types.ts +76 -0
  236. package/src/ingest/unmount-ingest-root.ts +224 -0
  237. package/src/navmesh/navmesh-actions.ts +27 -0
  238. package/src/navmesh/navmesh-handler.ts +237 -0
  239. package/src/navmesh/navmesh-workflow-store.ts +78 -0
  240. package/src/network/NetworkInspectorPanel.tsx +644 -0
  241. package/src/network/network-inspector-model.ts +225 -0
  242. package/src/play/play-boot-stall.ts +118 -0
  243. package/src/play/play-log-events.ts +26 -0
  244. package/src/play/play-mode.ts +2553 -0
  245. package/src/play/play-recording.ts +335 -0
  246. package/src/play/react-play-live-authoring.ts +165 -0
  247. package/src/play-bar/PlayBar.tsx +488 -0
  248. package/src/play-bar/PlayerCountPicker.tsx +100 -0
  249. package/src/profiler/FrameDebuggerPanel.tsx +458 -0
  250. package/src/profiler/PerformancePanel.tsx +1068 -0
  251. package/src/profiler/ProfilerPanel.tsx +53 -0
  252. package/src/profiler/frame-debugger-store.ts +97 -0
  253. package/src/profiler/main-thread-busy.ts +90 -0
  254. package/src/react/design-time-react-mount.ts +782 -0
  255. package/src/react/dom-authoring-adapter.ts +764 -0
  256. package/src/react/pasteboard-materialize.ts +154 -0
  257. package/src/react/react-inspector-section.tsx +2496 -0
  258. package/src/react/react-world-authoring-adapter.ts +3795 -0
  259. package/src/react/story-documents/story-args-section.ts +19 -0
  260. package/src/react/story-documents/story-documents.tsx +1380 -0
  261. package/src/react/story-paint-bounds.ts +94 -0
  262. package/src/react/ui-board-document.tsx +101 -0
  263. package/src/react/ui-board-title.ts +9 -0
  264. package/src/react/ui-component-board.ts +72 -0
  265. package/src/services/audio-pose-guard.ts +81 -0
  266. package/src/services/game-audio-unlock.ts +48 -0
  267. package/src/state-watch/StateWatchPanel.tsx +535 -0
  268. package/src/three/authoring/camera-runtime-inspector-section.tsx +135 -0
  269. package/src/three/authoring/constraint-inspector-section.tsx +189 -0
  270. package/src/three/authoring/design-time-renderer.ts +189 -0
  271. package/src/three/authoring/model-asset-inspector-section.css +41 -0
  272. package/src/three/authoring/model-asset-inspector-section.tsx +869 -0
  273. package/src/three/authoring/oid-source-persistence.ts +557 -0
  274. package/src/three/authoring/r3f-design-session.ts +1174 -0
  275. package/src/three/authoring/r3f-source-authoring-adapter.ts +5949 -0
  276. package/src/three/authoring/reflection-probe-inspector-section.tsx +76 -0
  277. package/src/three/authoring/spatial-audio-handles.ts +215 -0
  278. package/src/three/authoring/spatial-collider-handles.ts +229 -0
  279. package/src/three/authoring/spatial-joint-handles.ts +114 -0
  280. package/src/three/authoring/spatial-light-handles.ts +178 -0
  281. package/src/three/authoring/spatial-lod-handles.ts +93 -0
  282. package/src/three/authoring/spatial-particle-handles.ts +368 -0
  283. package/src/three/authoring/three-authoring-adapter.ts +1953 -0
  284. package/src/three/authoring/three-scene-identity.ts +19 -0
  285. package/src/three/authoring/three-spatial-handles.ts +161 -0
  286. package/src/three/authoring/typed-three-inspector.ts +528 -0
  287. package/src/three/component-verbs/extract-menu.ts +71 -0
  288. package/src/three/component-verbs/fork-menu.ts +78 -0
  289. package/src/three/component-verbs/internals-menu.ts +91 -0
  290. package/src/three/story-documents/three-story-documents.tsx +607 -0
  291. package/src/three/three-board/ThreeBoardDocument.tsx +888 -0
  292. package/src/three/three-board/board-framing.ts +412 -0
  293. package/src/three/three-board/board-layout.ts +401 -0
  294. package/src/three/three-board/board-scene.ts +901 -0
  295. package/src/three/three-board/three-component-board.ts +62 -0
  296. package/src/xstate/XStateBehaviorSection.tsx +130 -0
  297. package/src/xstate/XStateMachineInspector.tsx +566 -0
  298. package/src/xstate/character-animation-machine.fixture.ts +74 -0
  299. package/src/xstate/live-behaviors.ts +106 -0
  300. package/src/xstate/use-live-actor-state.ts +44 -0
  301. package/src/xstate/xstate-graph.ts +235 -0
  302. package/src/xstate/xstate-layout.ts +76 -0
@@ -0,0 +1,251 @@
1
+ /**
2
+ * THE single live-ingest slot: the shape of a mounted `{ ingest }` root's
3
+ * session, the one module-level variable holding it, and the queue that keeps
4
+ * entry to it serial.
5
+ *
6
+ * This module deliberately holds STATE and nothing else — no mounting, no
7
+ * teardown, no measurement. `mount-three-ingest-root.ts` /
8
+ * `mount-canvas-ingest-root.ts` / `mount-dom-ingest-root.ts` publish into the
9
+ * slot, `unmount-ingest-root.ts` empties it, and everything that merely READS a
10
+ * live session (`ingest-play-control.ts`, `mount-coverage.ts`,
11
+ * `live-ingest-facet.ts`) imports from here. Keeping the slot a leaf is what
12
+ * makes that whole graph acyclic.
13
+ */
14
+
15
+ import type { EditorShellStore } from '@volter/editor-core/editor-shell-store';
16
+ import type { MeasuredLoop } from '../host/same-realm-loop-gate';
17
+ import type { DomAuthoringAdapter } from '../react/dom-authoring-adapter';
18
+ import type { AuthoringAdapter } from '@volter/editor-project/adapter/authoring';
19
+ import type { IngestMount } from './authoring/ingest-root-adapter';
20
+ import type { SiblingMount } from './ingest-siblings';
21
+ import type { MountCoverageInputs } from './mount-coverage';
22
+
23
+ /**
24
+ * The ONE lifecycle control the editor consumes for an ingested game.
25
+ *
26
+ * Mount code resolves the game's raw contract and the substrate's fallback
27
+ * into this shape once. Consumers never choose between those mechanisms:
28
+ * `start` owns the cold -> started edge, while `setPaused` is present only
29
+ * when one complete pause/resume mechanism exists. A missing capability keeps
30
+ * its exact reason on `pauseGap` so Edit can refuse loudly rather than treating
31
+ * a fabricated no-op function as a successful hold.
32
+ */
33
+ export interface IngestLifecycleControl {
34
+ readonly start: () => void;
35
+ readonly setPaused?: ((paused: boolean) => void) | undefined;
36
+ readonly pauseGap?: string | undefined;
37
+ readonly step?: (() => void) | undefined;
38
+ readonly canStep?: (() => boolean) | undefined;
39
+ }
40
+
41
+ export interface IngestSession {
42
+ store: EditorShellStore;
43
+ lifecycle: IngestLifecycleControl;
44
+ /** The capture path (a real authoring adapter over the live scene). */
45
+ mount?: IngestMount;
46
+ /** Keeps the captured game renderer sized to its container (host lifecycle). */
47
+ resizeObserver?: ResizeObserver;
48
+ /** Drops the store subscription that releases a game-held pointer lock when
49
+ * the input gate closes (`ingest/game-pointer-lock.ts`). */
50
+ inputGateUnsubscribe?: () => void;
51
+ }
52
+
53
+ export interface IngestSession2D {
54
+ store: EditorShellStore;
55
+ lifecycle: IngestLifecycleControl;
56
+ adapter?: AuthoringAdapter & { dispose(): void };
57
+ /** The realm host box the game appended into — Scene reparents this. */
58
+ hostEl?: HTMLElement;
59
+ /** Measured loop truth for a reachable canvas realm — a verdict CARRIES the
60
+ * evidence it was concluded from ({@link MeasuredLoop}). `null` when the
61
+ * probe ran but learned nothing (`reason` says what it looked at) — never a
62
+ * verdict forced out of an absence of evidence. */
63
+ loop?: () => { loop: MeasuredLoop | null; reason: string };
64
+ dispose: () => void;
65
+ }
66
+
67
+ export interface IngestSessionReact {
68
+ store: EditorShellStore;
69
+ lifecycle: IngestLifecycleControl;
70
+ stop: () => void;
71
+ /** Live DOM-authoring adapter; owns a session history resource. */
72
+ adapter: DomAuthoringAdapter;
73
+ }
74
+
75
+ /**
76
+ * The world kind of the currently-active ingest session, if any (F26):
77
+ * ingest is an ADAPTER ROUTE, not a kind of its own — D-V1
78
+ * collapsed the three per-kind session slots this used to fixed-priority-scan
79
+ * (`_session` / `_session2D` / `_sessionReact`) into the single `_active` slot
80
+ * below, so at most one session is ever active, of any kind, and
81
+ * {@link activeIngestKind} is just that slot's tag. The play surface
82
+ * ({@link import('./ingest-play-control').getIngestPlayControl}) and
83
+ * {@link isIngestActive} read it, never a slot directly.
84
+ */
85
+ export type IngestKind = 'three' | 'canvas' | 'dom';
86
+
87
+ /**
88
+ * F26 slot collapse (D-V1): at most one ingest session is EVER active, of
89
+ * any kind — {@link activeIngest} is the single module-level slot for it.
90
+ * Pre-collapse, this was three independent per-kind singletons
91
+ * (`_session`/`_session2D`/`_sessionReact`, one per `IngestSession*` shape
92
+ * above), each with its own entry guard that only tore down its OWN slot —
93
+ * so switching kinds leaked the prior kind's live session (the F26
94
+ * residual).
95
+ *
96
+ * `siblings` (D-V2, composite): a composite manifest's OTHER roots, mounted
97
+ * beside this session's ingest world (`ingest-siblings.ts`'s
98
+ * `mountIngestSiblings`) — always `[]` for every non-composite entry path
99
+ * (the in-tree fixture glob route, a single-world manifest).
100
+ * `unmount-ingest-root.ts`'s `exitActiveIngest` disposes these, reverse order,
101
+ * in EVERY kind's teardown path — a sibling never outlives the session it was
102
+ * mounted beside.
103
+ */
104
+ export type ActiveIngest = (
105
+ | { kind: 'three'; session: IngestSession }
106
+ | { kind: 'canvas'; session: IngestSession2D }
107
+ | { kind: 'dom'; session: IngestSessionReact }
108
+ ) & {
109
+ siblings: SiblingMount[];
110
+ /** The mounted world's id — the SAME id every log line and the
111
+ * `__vgaiIngest` hook's `gameId` names. Carried on the slot (rather than
112
+ * re-derived) so the status facet can report WHICH world is live
113
+ * without a second source of truth (S-1). */
114
+ worldId: string;
115
+ /** The coverage inputs that can only be read AT MOUNT (see
116
+ * {@link MountCoverageInputs}). Absent until a mount path records them, and
117
+ * absent is honestly "not measured" — never a clean bill of health. */
118
+ coverage?: MountCoverageInputs;
119
+ };
120
+
121
+ let _active: ActiveIngest | null = null;
122
+
123
+ /** The live session, or `null`. Read it; never cache it across an await. */
124
+ export function activeIngest(): ActiveIngest | null {
125
+ return _active;
126
+ }
127
+
128
+ /** THE one write to the slot. Publishing happens only after a mount SUCCEEDS,
129
+ * which is what makes a non-null slot the mount evidence itself (S-1). */
130
+ export function setActiveIngest(next: ActiveIngest | null): void {
131
+ _active = next;
132
+ }
133
+
134
+ export function activeIngestKind(): IngestKind | null {
135
+ return _active?.kind ?? null;
136
+ }
137
+
138
+ /** True if an unmodified game is currently ingested — ANY kind (F26). */
139
+ export function isIngestActive(): boolean {
140
+ return activeIngestKind() !== null;
141
+ }
142
+
143
+ /** True if an unmodified PixiJS game is currently ingested. */
144
+ export function isIngestActive2D(): boolean {
145
+ return _active?.kind === 'canvas';
146
+ }
147
+
148
+ /** True if an ingested native-React game is currently mounted. */
149
+ export function isIngestActiveReact(): boolean {
150
+ return _active?.kind === 'dom';
151
+ }
152
+
153
+ /**
154
+ * D-V2: attach a composite's sibling handles onto the JUST-published session
155
+ * (set by the primary ingest world's own mount call, immediately before this
156
+ * runs) — a no-op if the slot is somehow no longer set (defensive only;
157
+ * nothing in that synchronous call chain clears it between the primary mount
158
+ * and this attach).
159
+ */
160
+ export function attachIngestSiblings(siblings: readonly SiblingMount[]): void {
161
+ if (_active) _active.siblings = [...siblings];
162
+ }
163
+
164
+ /**
165
+ * Serializes ingest ENTRY, exactly as `play-mode.ts`'s `_enterQueue` does for
166
+ * play — and for the same reason it had to.
167
+ *
168
+ * Every entry point opens with a synchronous `exitActiveIngest()`, which reads
169
+ * the slot. But the slot is not assigned until AFTER the mount's awaits
170
+ * resolve. Two overlapping calls therefore both observe an empty slot,
171
+ * both tear down nothing, and both mount — leaving two live sessions. That is
172
+ * the react-rpg double mount: two React roots, two `<div id="window">`, two
173
+ * "Story Mode" buttons, intermittently, because it is a race rather than a
174
+ * code path. Nothing kind-specific about it; every entry point shares the
175
+ * shape, react merely had the test that caught it.
176
+ *
177
+ * Queued, the second caller's `exitActiveIngest()` runs after the first has
178
+ * published the slot, so it disposes it and exactly one session stays live.
179
+ * Pinned by `ingest-root-entry-race.test.ts`.
180
+ */
181
+ let _enterQueue: Promise<void> = Promise.resolve();
182
+
183
+ export function serializeEntry<T>(run: () => Promise<T>): Promise<T> {
184
+ const result = _enterQueue.then(run);
185
+ // Advance unconditionally so one caller's rejection can never wedge every
186
+ // subsequent ingest attempt — each caller still observes its OWN failure
187
+ // through the promise returned here (same contract as play-mode's queue).
188
+ _enterQueue = result.then(
189
+ () => undefined,
190
+ () => undefined,
191
+ );
192
+ return result;
193
+ }
194
+
195
+ import { registerLiveSession } from '@volter/editor-core/live-session-registry';
196
+ // THE INGEST LANE, as the host sees it (`live-session-registry.ts`). Playing
197
+ // is the host play-control latch, never an inferred loop
198
+ // (`editor-session-mode.ts` records why); a deferred-ingest play counts.
199
+ import { activeContractScenes } from './active-scene-navigation';
200
+ import { deferredIngestPlayActive } from './deferred-ingest-session';
201
+ import { armIngestFrameSnapshot, ingestFrameCanvas } from './ingest-frame-snapshot';
202
+ import { handleIngestPlayControl } from './ingest-play-commands';
203
+ import { getIngestPlayControl, ingestPlaying } from './ingest-play-control';
204
+ // `mount-coverage.ts` imports this module back, so the pair is a CYCLE — and
205
+ // the lane's report is read through it directly rather than pushed in by a
206
+ // setter. A setter is what a cycle cannot survive: `mount-coverage`'s
207
+ // module-scope `setIngestCoverageSource(...)` ran while this module's own body
208
+ // was still in its `let`'s temporal dead zone whenever the graph happened to
209
+ // evaluate this file first, and the editor failed to start with "Cannot access
210
+ // 'coverageSource' before initialization" (measured on `bunnymark`,
211
+ // 2026-09-17, when an import-sort reshuffle changed which file the graph
212
+ // reached first). `ingestCoverageReport` is a FUNCTION DECLARATION: ESM
213
+ // initializes it at instantiation, before any body runs, so reading it across
214
+ // the cycle is order-independent by construction.
215
+ import { ingestCoverageReport } from './mount-coverage';
216
+ import { exitActiveIngest } from './unmount-ingest-root';
217
+
218
+ registerLiveSession({
219
+ id: 'ingest',
220
+ priority: 1,
221
+ mounted: isIngestActive,
222
+ playing: () => ingestPlaying() || deferredIngestPlayActive(),
223
+ stop: exitActiveIngest,
224
+ instanceContainer: () => null,
225
+ frameCanvas: ingestFrameCanvas,
226
+ snapshotFrame: () => armIngestFrameSnapshot(),
227
+ // S-2 (the SimCity ledger): an ingest session OWNS its mount, and the
228
+ // first-party play boot cannot share it — `play`/`stop`/`pause`/`resume`/
229
+ // `step` drive the ingested game's own loop (`ingest-play-commands.ts`).
230
+ command: (cmd) => {
231
+ const control = isIngestActive() ? getIngestPlayControl() : null;
232
+ return control
233
+ ? handleIngestPlayControl(control, cmd.type, { stopEndsRun: deferredIngestPlayActive() })
234
+ : null;
235
+ },
236
+ scenes: activeContractScenes,
237
+ surface: activeIngestKind,
238
+ coverage: ingestCoverageReport,
239
+ // An ingested native-React game renders as a DOM layer under the host
240
+ // React — no scene graph, no renderer to introspect — and authoring is
241
+ // deliberately off for react ingests (the editor never writes JSX back into
242
+ // an unmodified game). The game is hosted, sized and disposed; play it in
243
+ // the Game tab.
244
+ authoringRefusal: () =>
245
+ _active?.kind === 'dom'
246
+ ? 'This is an ingested native-React game: its components render directly as a DOM layer ' +
247
+ 'under the host React — there is no scene graph or renderer to introspect, and authoring ' +
248
+ 'is deliberately off for react ingests (the editor never writes JSX back into an ' +
249
+ 'unmodified game). The game is hosted, sized, and disposed; play it in the Game tab.'
250
+ : null,
251
+ });
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The BOUND scenes projection of whatever game is live in this session, or
3
+ * `null`.
4
+ *
5
+ * One question, one reader. The `open` verb's live half
6
+ * (`scene-live-open.ts`) needs the very object the mount installed — the same
7
+ * one the inspector's scene picker drives, carrying that mount's held-repaint
8
+ * seam — so it is resolved off the single ingest slot (`active-ingest.ts`),
9
+ * never rebuilt from the contract and never taken from a COMPOSITE adapter's
10
+ * `stories` pass-through (that is a fresh delegating object; the projection's
11
+ * `goToScene` does not survive it).
12
+ *
13
+ * The marker, not the session kind, is what decides: a plain `StoriesProvider`
14
+ * (a react world's design-time CSF states) is not a game's own screens and must
15
+ * not be navigated as though it were.
16
+ */
17
+
18
+ import {
19
+ type ContractScenesStories,
20
+ isContractScenesStories,
21
+ } from '../host/authoring/contract-scenes-stories';
22
+ import type { AuthoringAdapter } from '@volter/editor-project/adapter/authoring';
23
+ import { activeIngest } from './active-ingest';
24
+
25
+ export function activeContractScenes(): ContractScenesStories | null {
26
+ const active = activeIngest();
27
+ if (!active) return null;
28
+ // Every kind's own live authoring adapter — asked the same question, so a
29
+ // projection bound on a lane that does not have one today answers the day it
30
+ // does, without a second reader here.
31
+ const adapter: AuthoringAdapter | null | undefined = (() => {
32
+ switch (active.kind) {
33
+ case 'canvas':
34
+ return active.session.adapter;
35
+ case 'dom':
36
+ return active.session.adapter;
37
+ case 'three':
38
+ return active.session.mount?.authoring;
39
+ }
40
+ })();
41
+ const stories = adapter?.stories;
42
+ return isContractScenesStories(stories) ? stories : null;
43
+ }
@@ -0,0 +1,191 @@
1
+ /**
2
+ * Turns a readable canvas game's own DOM UI into an ordinary nested authoring
3
+ * surface. Detection stays at the mount boundary; once built, the editor sees
4
+ * only AuthoringAdapter/CompositeAuthoringAdapter currency.
5
+ */
6
+
7
+ import { CompositeAuthoringAdapter } from '@volter/editor-core/authoring/composite-authoring-adapter';
8
+ import {
9
+ findCanvasUiElements,
10
+ findPrimaryCanvas,
11
+ type OverlayElement,
12
+ } from '../../host/coverage/capability-coverage';
13
+ import type { EditorShellStore } from '@volter/editor-core/editor-shell-store';
14
+ import { authoringJournal } from '../../host/history/json-history-resource';
15
+ import { sourceWriteBackendIfPrimed } from '@volter/editor-core/ui-source/tier-source-write-backend';
16
+ import { DomAuthoringAdapter, type DomElementLike } from '../../react/dom-authoring-adapter';
17
+ import {
18
+ type OidElementLike,
19
+ ReactRootAuthoringAdapter,
20
+ walkOidTree,
21
+ } from '../../react/react-world-authoring-adapter';
22
+ import type { AuthoringAdapter } from '@volter/editor-project/adapter';
23
+ import type { VgaiGameContract } from '@volter/editor-project/adapter/ingest/game-contract';
24
+
25
+ type ReadableDomRoot = OverlayElement & DomElementLike & { readonly ownerDocument?: Document };
26
+
27
+ function isReadableDomRoot(value: unknown): value is ReadableDomRoot {
28
+ const candidate = value as Partial<ReadableDomRoot> | null;
29
+ return (
30
+ candidate !== null &&
31
+ typeof candidate === 'object' &&
32
+ typeof candidate.tagName === 'string' &&
33
+ candidate.children !== undefined &&
34
+ typeof candidate.getBoundingClientRect === 'function'
35
+ );
36
+ }
37
+
38
+ function iframeDocumentRoot(hostEl: HTMLElement): ReadableDomRoot | null {
39
+ try {
40
+ const iframe =
41
+ hostEl.tagName.toLowerCase() === 'iframe'
42
+ ? (hostEl as HTMLIFrameElement)
43
+ : (hostEl.querySelector('iframe') as HTMLIFrameElement | null);
44
+ const body = iframe?.contentDocument?.body;
45
+ return isReadableDomRoot(body) ? body : null;
46
+ } catch {
47
+ return null;
48
+ }
49
+ }
50
+
51
+ function resolveGameDomRoot(
52
+ hostEl: HTMLElement,
53
+ declaredRoot: VgaiGameContract['root'] | undefined,
54
+ ): ReadableDomRoot | null {
55
+ if (isReadableDomRoot(declaredRoot)) return declaredRoot;
56
+ return iframeDocumentRoot(hostEl) ?? (isReadableDomRoot(hostEl) ? hostEl : null);
57
+ }
58
+
59
+ function withMutationSubscription(
60
+ adapter: DomAuthoringAdapter | ReactRootAuthoringAdapter,
61
+ gameRoot: ReadableDomRoot,
62
+ ): AuthoringAdapter {
63
+ const subscribe = (listener: () => void): (() => void) => {
64
+ const unsubscribe = adapter.subscribe(listener);
65
+ const Observer = gameRoot.ownerDocument?.defaultView?.MutationObserver;
66
+ if (!Observer) return unsubscribe;
67
+ const observer = new Observer(() => listener());
68
+ observer.observe(gameRoot as unknown as Node, {
69
+ childList: true,
70
+ subtree: true,
71
+ });
72
+ return () => {
73
+ observer.disconnect();
74
+ unsubscribe();
75
+ };
76
+ };
77
+ return new Proxy(adapter, {
78
+ get(target, property) {
79
+ if (property === 'subscribe') return subscribe;
80
+ const value = Reflect.get(target, property, target);
81
+ return typeof value === 'function' ? value.bind(target) : value;
82
+ },
83
+ });
84
+ }
85
+
86
+ /**
87
+ * Which authoring adapter this mount's DOM UI gets — decided by ONE probe, the
88
+ * same probe and the same two outcomes `ingest-siblings.ts`'s
89
+ * `mountReactSibling` already uses for a react sibling layer: does the live DOM
90
+ * actually carry `data-oid` stamps?
91
+ *
92
+ * - **Stamped** ⇒ {@link ReactRootAuthoringAdapter} over the OID tree, with the
93
+ * dev-server source-write backend. Node ids are OID-derived, so the HUD's own
94
+ * JSX — the component the elements came from — is the subject, and a
95
+ * className/style/text edit prepares next-file text and commits it through
96
+ * `/__ui-source/write`. For a REPO-VENDORED game that endpoint's
97
+ * `writeEditableSource` routes the write through
98
+ * `server/vendored-lock-recorder.ts`, exactly as a three root's writes are
99
+ * routed — the choke point is the endpoint, not the adapter, so this lane
100
+ * inherits lock recording without knowing anything about vendoring.
101
+ * - **Unstamped** ⇒ {@link DomAuthoringAdapter} unchanged: structural ids
102
+ * over the live DOM and no write seam at all. That is the honest answer for
103
+ * DOM the stamping pipeline declined to instrument (a bundled/minified ingest,
104
+ * a foreign game excluded by identity) — never a write seam over source the
105
+ * editor cannot address.
106
+ *
107
+ * A PARTIALLY stamped tree counts as stamped: `walkOidTree` already skips
108
+ * untagged elements transparently and attaches their children to the nearest
109
+ * tagged ancestor, so this stays a single "any node at all?" question rather
110
+ * than a completeness one.
111
+ *
112
+ * The ROUTE gates the backend, not the adapter choice (`../ui-source/tier-
113
+ * source-write-backend.ts`): on a host that serves no `/__ui-source/*`
114
+ * recorder the OID lane still gives correct selection/inspection and reports
115
+ * its writes unavailable through `persistence.destination`.
116
+ */
117
+ function domSurfaceAdapter(
118
+ liveUiRoot: DomElementLike & OidElementLike,
119
+ store: EditorShellStore,
120
+ /** The detected surface's own id — a held surface, so its journal is the
121
+ * world's and survives the ingest remounting (see the ownership block in
122
+ * `../history/json-history-resource.ts`). */
123
+ domUiJournalId: string,
124
+ ): DomAuthoringAdapter | ReactRootAuthoringAdapter {
125
+ if (walkOidTree(liveUiRoot).nodes.size === 0) {
126
+ return new DomAuthoringAdapter(liveUiRoot, store, {
127
+ journal: authoringJournal(domUiJournalId),
128
+ });
129
+ }
130
+ const writeBackend = sourceWriteBackendIfPrimed('An ingested game’s DOM UI surface');
131
+ return new ReactRootAuthoringAdapter(liveUiRoot, store, {
132
+ ...(writeBackend ? { writeBackend } : {}),
133
+ });
134
+ }
135
+
136
+ /**
137
+ * Preserve `primary` byte-for-byte when this mount has no readable canvas/UI
138
+ * split. Otherwise expose Canvas as the runtime surface and DOM UI as its
139
+ * nested document. The UI root's children are getters over live geometry, so
140
+ * menus that appear later enter the same surface without a mirror tree.
141
+ */
142
+ export function withDetectedDomSurface(args: {
143
+ primary: AuthoringAdapter;
144
+ worldId: string;
145
+ hostEl: HTMLElement;
146
+ declaredRoot?: VgaiGameContract['root'];
147
+ store: EditorShellStore;
148
+ }): AuthoringAdapter {
149
+ const gameRoot = resolveGameDomRoot(args.hostEl, args.declaredRoot);
150
+ if (!gameRoot) return args.primary;
151
+ const canvas = findPrimaryCanvas(gameRoot);
152
+ if (!canvas || findCanvasUiElements(gameRoot, canvas).length === 0) return args.primary;
153
+
154
+ const domUiId = `${args.worldId}:dom-ui`;
155
+ // Composite world ids are editor identity and legitimately use `:`. A
156
+ // history resource id is a project-relative path and must not: conflating
157
+ // the two made ResourceRegistry parse `world:dom-ui/...` as a URL scheme and
158
+ // refuse every detected DOM sibling before the ingest could mount.
159
+ const domUiJournalId = `${args.worldId}/dom-ui`;
160
+ const liveUiRoot: DomElementLike & OidElementLike = {
161
+ tagName: 'vgai-dom-surface',
162
+ id: '',
163
+ className: '',
164
+ get children() {
165
+ const currentCanvas = findPrimaryCanvas(gameRoot);
166
+ return currentCanvas
167
+ ? (findCanvasUiElements(gameRoot, currentCanvas) as DomElementLike[] & OidElementLike[])
168
+ : [];
169
+ },
170
+ // A synthetic container the host manufactured, not a rendered element —
171
+ // it carries no oid of its own, and `walkOidTree` transparently skips an
172
+ // untagged node, attaching the game's real HUD elements as tree roots.
173
+ getAttribute: () => null,
174
+ };
175
+ const dom = withMutationSubscription(
176
+ domSurfaceAdapter(liveUiRoot, args.store, domUiJournalId),
177
+ gameRoot,
178
+ );
179
+ const canvasId = `${args.worldId}:canvas`;
180
+ return new CompositeAuthoringAdapter([
181
+ { worldId: canvasId, kind: 'canvas', adapter: args.primary },
182
+ {
183
+ worldId: domUiId,
184
+ kind: 'dom',
185
+ role: 'surface',
186
+ label: 'DOM UI',
187
+ parentRootId: canvasId,
188
+ adapter: dom,
189
+ },
190
+ ]);
191
+ }