@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,152 @@
1
+ /**
2
+ * WHICH COMPONENT BOARDS THIS PROJECT HAS — the one answer all three boards'
3
+ * existence keys on.
4
+ *
5
+ * A component board exists because the project has STORIES of that board's
6
+ * medium, never because it declares a root of some kind (owner ruling,
7
+ * 2026-08-15; ARCHITECTURE-CORE §center-surface ontology). Root kinds are
8
+ * irrelevant to presence: a project with a three root and no three story has
9
+ * nothing to lay out on a 3D board, and a project whose UI components live
10
+ * inside an ingested game — no `dom` root anywhere in its manifest — still has
11
+ * a UI board the moment one of those components declares a story.
12
+ *
13
+ * ## Medium is DECLARED, not mounted
14
+ *
15
+ * ARCHITECTURE-CORE §Zero inference: the system answers this question from
16
+ * the story's CSF tag, the adapter's `include`/`mounts`, and the manifest
17
+ * root entry. See `story-declared-medium.ts`. A story the declarations
18
+ * cannot place is a named gap — it does not create a board, and nothing
19
+ * mounts it to guess.
20
+ *
21
+ * ## It is a LATCHING answer, and that is deliberate
22
+ *
23
+ * Classification is now synchronous, but a registry refresh can still
24
+ * replace one settled answer with another. During a refresh the PREVIOUS
25
+ * verdict stands rather than collapsing to "no boards": a project that has
26
+ * boards must not have them blink out of the workspace every time a story
27
+ * file is saved. The first pass over an empty registry therefore reports
28
+ * every medium false — a project with no stories has never had a board and
29
+ * never gets one — while a later pass only ever replaces one settled answer
30
+ * with another.
31
+ *
32
+ * A pass superseded by a newer registry change is abandoned; only the newest
33
+ * pass publishes.
34
+ */
35
+
36
+ import { getProjectStoryRegions } from '@volter/editor-core/stories/project-story-regions';
37
+ import {
38
+ __resetUndeclaredStoryMediumReportsForTest,
39
+ declaredStoryMedium,
40
+ reportUndeclaredStoryMedium,
41
+ type StoryMedium,
42
+ } from '@volter/editor-core/stories/story-declared-medium';
43
+ import { getProjectStoryModules, subscribeProjectStoryModules } from '@volter/editor-core/stories/story-registry';
44
+
45
+ /** Which component boards the project's stories call for. */
46
+ export interface StoryMediaPresence {
47
+ /** ≥1 story declared as `three` — the 3D board's existence. */
48
+ readonly three: boolean;
49
+ /** ≥1 story declared as `canvas` — the 2D board's existence. */
50
+ readonly canvas: boolean;
51
+ /** ≥1 story declared as `dom` — the UI board's existence. */
52
+ readonly dom: boolean;
53
+ }
54
+
55
+ const NONE: StoryMediaPresence = { three: false, canvas: false, dom: false };
56
+
57
+ let _presence: StoryMediaPresence = NONE;
58
+ let _pass = 0;
59
+ const _listeners = new Set<() => void>();
60
+ let _subscribed = false;
61
+
62
+ /** The current verdict (stable reference between changes). */
63
+ export function getStoryMediaPresence(): StoryMediaPresence {
64
+ ensureSubscribed();
65
+ return _presence;
66
+ }
67
+
68
+ /** Subscribe to verdict changes. Returns an unsubscribe function. */
69
+ export function subscribeStoryMediaPresence(listener: () => void): () => void {
70
+ ensureSubscribed();
71
+ _listeners.add(listener);
72
+ return () => {
73
+ _listeners.delete(listener);
74
+ };
75
+ }
76
+
77
+ function ensureSubscribed(): void {
78
+ if (_subscribed) return;
79
+ _subscribed = true;
80
+ subscribeProjectStoryModules(() => {
81
+ void recomputeStoryMediaPresence();
82
+ });
83
+ void recomputeStoryMediaPresence();
84
+ }
85
+
86
+ function publish(next: StoryMediaPresence): void {
87
+ if (
88
+ next.three === _presence.three &&
89
+ next.canvas === _presence.canvas &&
90
+ next.dom === _presence.dom
91
+ )
92
+ return;
93
+ _presence = next;
94
+ for (const listener of _listeners) listener();
95
+ }
96
+
97
+ function classifyDeclared(story: { readonly modulePath?: string }): StoryMedium | undefined {
98
+ const declared = declaredStoryMedium({
99
+ modulePath: story.modulePath,
100
+ regions: getProjectStoryRegions(),
101
+ });
102
+ if (declared.medium) return declared.medium;
103
+ if (story.modulePath) reportUndeclaredStoryMedium(story.modulePath, declared.reason);
104
+ return undefined;
105
+ }
106
+
107
+ /**
108
+ * Re-read the open project's story media from declarations. Exported so a
109
+ * caller that just refreshed the registry can await the verdict instead of
110
+ * racing it — the subscription above drives the ordinary case.
111
+ *
112
+ * Never throws. Never mounts. An undeclared story is named once and does not
113
+ * count toward any board.
114
+ */
115
+ export async function recomputeStoryMediaPresence(): Promise<StoryMediaPresence> {
116
+ const pass = ++_pass;
117
+ const stories = getProjectStoryModules().flatMap((module_) =>
118
+ module_.ok
119
+ ? module_.stories.map((story) => ({ ...story, modulePath: module_.modulePath }))
120
+ : [],
121
+ );
122
+
123
+ // The empty registry is the one case with no measuring to do, and the one
124
+ // case that must be able to take boards AWAY (a project whose last story was
125
+ // deleted, and — the common case — a project that never had one).
126
+ if (stories.length === 0) {
127
+ publish(NONE);
128
+ return _presence;
129
+ }
130
+
131
+ const found: Record<StoryMedium, boolean> = { three: false, canvas: false, dom: false };
132
+ for (const story of stories) {
133
+ const medium = classifyDeclared(story);
134
+ if (medium) found[medium] = true;
135
+ if (pass !== _pass) return _presence;
136
+ }
137
+ const { three, canvas, dom } = found;
138
+ if (pass !== _pass) return _presence;
139
+ publish({ three, canvas, dom });
140
+ return _presence;
141
+ }
142
+
143
+ /** Test-only: forget the current verdict and the registry subscription, so a
144
+ * suite can drive `recomputeStoryMediaPresence` against its own fixtures
145
+ * without a previous suite's answer latching into it. */
146
+ export function __resetStoryMediaPresenceForTest(): void {
147
+ _presence = NONE;
148
+ _pass = 0;
149
+ _listeners.clear();
150
+ _subscribed = false;
151
+ __resetUndeclaredStoryMediumReportsForTest();
152
+ }
@@ -0,0 +1,87 @@
1
+ import type * as THREE from 'three';
2
+
3
+ const RENDERABLE_DOM_TAGS = new Set([
4
+ 'canvas',
5
+ 'svg',
6
+ 'img',
7
+ 'picture',
8
+ 'video',
9
+ 'input',
10
+ 'button',
11
+ ]);
12
+ const NON_RENDERING_DOM_TAGS = new Set(['script', 'style', 'template']);
13
+
14
+ function elementIsHidden(element: Element): boolean {
15
+ if (element.hasAttribute('hidden')) return true;
16
+ if (typeof getComputedStyle !== 'function') return false;
17
+ const style = getComputedStyle(element);
18
+ return style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0';
19
+ }
20
+
21
+ function styledBoxRenders(element: Element): boolean {
22
+ if (typeof getComputedStyle !== 'function') return false;
23
+ const style = getComputedStyle(element);
24
+ const rect = element.getBoundingClientRect();
25
+ if (!(rect.width > 0 && rect.height > 0)) return false;
26
+ const hasBackground =
27
+ style.backgroundImage !== 'none' ||
28
+ (style.backgroundColor !== 'transparent' && style.backgroundColor !== 'rgba(0, 0, 0, 0)');
29
+ return hasBackground || style.borderTopStyle !== 'none' || style.outlineStyle !== 'none';
30
+ }
31
+
32
+ function domNodeRenders(node: Node): boolean {
33
+ if (node.nodeType === Node.TEXT_NODE) return Boolean(node.textContent?.trim());
34
+ if (!(node instanceof Element)) return false;
35
+ const tag = node.tagName.toLowerCase();
36
+ if (NON_RENDERING_DOM_TAGS.has(tag) || elementIsHidden(node)) return false;
37
+ if (RENDERABLE_DOM_TAGS.has(tag)) return true;
38
+ for (const child of node.childNodes) if (domNodeRenders(child)) return true;
39
+ return styledBoxRenders(node);
40
+ }
41
+
42
+ /** Structural DOM content test used after React has committed into a root. */
43
+ export function domHasRenderableContent(root: HTMLElement): boolean {
44
+ for (const child of root.childNodes) if (domNodeRenders(child)) return true;
45
+ return false;
46
+ }
47
+
48
+ function threeObjectCanRender(object: THREE.Object3D, ignoredLayer?: number): boolean {
49
+ const candidate = object as THREE.Object3D & {
50
+ isMesh?: boolean;
51
+ isLine?: boolean;
52
+ isPoints?: boolean;
53
+ isSprite?: boolean;
54
+ material?: THREE.Material | THREE.Material[];
55
+ };
56
+ if (ignoredLayer !== undefined && candidate.layers.mask >>> 0 === (1 << ignoredLayer) >>> 0) {
57
+ return false;
58
+ }
59
+ if (!candidate.isMesh && !candidate.isLine && !candidate.isPoints && !candidate.isSprite) {
60
+ return false;
61
+ }
62
+ const materials = Array.isArray(candidate.material)
63
+ ? candidate.material
64
+ : candidate.material
65
+ ? [candidate.material]
66
+ : [];
67
+ return materials.length === 0 || materials.some((material) => material.visible);
68
+ }
69
+
70
+ /** Whether a Three scene can contribute pixels without editor-only helpers. */
71
+ export function threeSceneHasRenderableContent(
72
+ scene: THREE.Scene,
73
+ options: { readonly includeBackground?: boolean; readonly ignoredLayer?: number } = {},
74
+ ): boolean {
75
+ if (options.includeBackground !== false && scene.background !== null) return true;
76
+ const pending: THREE.Object3D[] = [scene];
77
+ while (pending.length > 0) {
78
+ const object = pending.pop();
79
+ if (!object || !object.visible) continue;
80
+ if (object !== scene && threeObjectCanRender(object, options.ignoredLayer)) return true;
81
+ for (let index = object.children.length - 1; index >= 0; index -= 1) {
82
+ const child = object.children[index];
83
+ if (child) pending.push(child);
84
+ }
85
+ }
86
+ return false;
87
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Import a module by URL and take the export the adapter's scene table
3
+ * names — with the one refusal sentence both scene-loading surfaces show
4
+ * when the table and the module disagree. Byte-identical copies of this
5
+ * (refusal text included) lived in two components before this home.
6
+ */
7
+ export async function takeNamedExport<T>(
8
+ url: string,
9
+ path: string,
10
+ exportName: string | undefined,
11
+ ): Promise<T> {
12
+ const module_ = (await import(/* @vite-ignore */ url)) as Record<string, unknown>;
13
+ const name = exportName ?? 'default';
14
+ const Component = module_[name];
15
+ if (typeof Component !== 'function') {
16
+ throw new Error(
17
+ `${path} does not export ${
18
+ exportName === undefined ? 'a default component' : `\`${exportName}\``
19
+ }, which is what the adapter's scene table names. The table and the module disagree.`,
20
+ );
21
+ }
22
+ return Component as T;
23
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Resolve the THREE primitives an INGEST root's capture trap is installed on
3
+ * from the same module graph as the game's own entry — the three sibling of
4
+ * `canvas-entry-runtime.ts`'s `resolveCanvasPixiForEditor`.
5
+ *
6
+ * See `../vite-plugin-module-doorways.ts` for the measured failure this
7
+ * closes and for why the ingest lane does not borrow `/__vgai-r3f-runtime`.
8
+ *
9
+ * A mount site resolves this ONCE and hands what it gets to everything it
10
+ * builds for that game. Nothing further down reaches for a static import: a
11
+ * namespace that could differ between two collaborators of the same game is
12
+ * the bug, not the seam.
13
+ */
14
+
15
+ import * as shellThree from 'three';
16
+ import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
17
+ import { CSS3DRenderer } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
18
+ import { THREE_INGEST_RUNTIME_PATH } from '@volter/editor-sdk/host';
19
+ import { isPackagedRuntime } from '@volter/editor-core/packaged-runtime';
20
+
21
+ /**
22
+ * THE three of the graph an ingested game's modules resolve — the project's
23
+ * under the packaged runtime, this bundle's own copy otherwise (where one
24
+ * Vite instance's `resolve.dedupe` has already made them the same object, so
25
+ * the packaged branch's absence is not a degrade).
26
+ *
27
+ * The addons travel with the namespace deliberately: they are files inside
28
+ * the same `three` package tree, so "which three?" and "which EffectComposer?"
29
+ * are ONE question and answering them separately is how they drift.
30
+ */
31
+ export interface ThreeIngestRuntime {
32
+ readonly three: typeof shellThree;
33
+ readonly EffectComposer: typeof EffectComposer;
34
+ readonly CSS3DRenderer: typeof CSS3DRenderer;
35
+ }
36
+
37
+ const shellRuntime: ThreeIngestRuntime = {
38
+ three: shellThree,
39
+ EffectComposer,
40
+ CSS3DRenderer,
41
+ };
42
+
43
+ let cachedPackagedRuntime: Promise<ThreeIngestRuntime> | null = null;
44
+
45
+ async function packagedRuntime(): Promise<ThreeIngestRuntime> {
46
+ const mod = (await import(/* @vite-ignore */ THREE_INGEST_RUNTIME_PATH)) as {
47
+ projectThree?: unknown;
48
+ EffectComposer?: unknown;
49
+ CSS3DRenderer?: unknown;
50
+ };
51
+ const missing = (['projectThree', 'EffectComposer', 'CSS3DRenderer'] as const).filter(
52
+ (name) => mod[name] === undefined || mod[name] === null,
53
+ );
54
+ if (missing.length > 0) {
55
+ // Loud, not a fallback to the shell's copy: falling back would put the
56
+ // trap on a `WebGLRenderer.prototype` the game never constructs and the
57
+ // mount would then fail 10 seconds later saying the game "bundles its own
58
+ // (un-shared) copy of three" — a sentence about the GAME, for a defect in
59
+ // this host.
60
+ throw new Error(
61
+ "The packaged runtime's synthetic three-ingest module did not export " +
62
+ `${missing.join(', ')} — see vite-plugin-module-doorways.ts.`,
63
+ );
64
+ }
65
+ return {
66
+ three: mod.projectThree as typeof shellThree,
67
+ EffectComposer: mod.EffectComposer as typeof EffectComposer,
68
+ CSS3DRenderer: mod.CSS3DRenderer as typeof CSS3DRenderer,
69
+ };
70
+ }
71
+
72
+ export async function resolveThreeIngestRuntimeForEditor(): Promise<ThreeIngestRuntime> {
73
+ if (!(await isPackagedRuntime())) return shellRuntime;
74
+ if (!cachedPackagedRuntime) cachedPackagedRuntime = packagedRuntime();
75
+ return cachedPackagedRuntime;
76
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Ambient declaration for `fastnoise-lite`, which ships no types. It enters
3
+ * the editor only through the browser dependency table
4
+ * (`served-bundle-runtime-modules.ts`) so a hosted project importing it — the
5
+ * Godot compat noise protocols do — resolves; the editor never calls into it.
6
+ */
7
+ declare module 'fastnoise-lite';
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Ambient declaration for three's MikkTSpace wasm helper. `@types/three`
3
+ * carried `examples/jsm/libs/mikktspace.module.d.ts` until 0.180, which ships
4
+ * types for `fflate`, `lil-gui`, `meshopt_decoder`, `stats` and `tween` only —
5
+ * so the module the browser dependency table registers
6
+ * (`served-bundle-runtime-modules.ts`, for a hosted project that computes tangents)
7
+ * became untyped in place. The shape below is the module's own export list
8
+ * (`three/examples/jsm/libs/mikktspace.module.js`).
9
+ */
10
+ declare module 'three/addons/libs/mikktspace.module.js' {
11
+ /** Resolves once the embedded wasm module has been instantiated. */
12
+ export const ready: Promise<void>;
13
+ /** True after {@link ready} settles. */
14
+ export let isReady: boolean;
15
+ export function generateTangents(
16
+ position: Float32Array,
17
+ normal: Float32Array,
18
+ texcoord: Float32Array,
19
+ ): Float32Array;
20
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Ambient declaration for `troika-three-text`, which ships no types. It
3
+ * enters the editor only through the browser dependency table
4
+ * (`served-bundle-runtime-modules.ts`) so a hosted project importing it resolves;
5
+ * the editor never calls into it.
6
+ */
7
+ declare module 'troika-three-text';
@@ -0,0 +1,53 @@
1
+ /**
2
+ * THE one answer to "which profiler is the UI showing right now?".
3
+ *
4
+ * Subscribes to the three registries the answer depends on (workspace
5
+ * documents, performance sources, the inspected instance) and resolves the
6
+ * `workspace:game` → per-instance → per-document ladder. The header
7
+ * telemetry and the Performance panel both render THIS — two copies of the
8
+ * ladder is how the header and the panel silently disagree about which
9
+ * document's profiler they show.
10
+ */
11
+
12
+ import { useSyncExternalStore } from 'react';
13
+ import {
14
+ inspectedInstanceId,
15
+ inspectedInstanceVersion,
16
+ subscribeInspectedInstance,
17
+ } from '@volter/editor-core/authoring/active-systems';
18
+ import {
19
+ type EditorPerformanceSource,
20
+ performanceSourceForDocument,
21
+ performanceSourceForGameInstance,
22
+ performanceSourcesVersion,
23
+ subscribePerformanceSources,
24
+ } from '@volter/editor-core/performance-sources';
25
+ import {
26
+ activeWorkspaceDocumentId,
27
+ subscribeWorkspaceDocuments,
28
+ workspaceDocumentRegistryVersion,
29
+ } from '@volter/editor-core/workspace-document-registry';
30
+
31
+ export function useActivePerformanceSource(): EditorPerformanceSource | null {
32
+ useSyncExternalStore(
33
+ subscribeWorkspaceDocuments,
34
+ workspaceDocumentRegistryVersion,
35
+ workspaceDocumentRegistryVersion,
36
+ );
37
+ useSyncExternalStore(
38
+ subscribePerformanceSources,
39
+ performanceSourcesVersion,
40
+ performanceSourcesVersion,
41
+ );
42
+ useSyncExternalStore(
43
+ subscribeInspectedInstance,
44
+ inspectedInstanceVersion,
45
+ inspectedInstanceVersion,
46
+ );
47
+ const documentId = activeWorkspaceDocumentId();
48
+ return (
49
+ (documentId === 'workspace:game'
50
+ ? performanceSourceForGameInstance(inspectedInstanceId())
51
+ : null) ?? performanceSourceForDocument(documentId)
52
+ );
53
+ }
@@ -0,0 +1,48 @@
1
+ /**
2
+ * The Scene viewport camera pose a reader left behind, remembered per project.
3
+ *
4
+ * WHY. A reload auto-framed from scratch, so the view someone had carefully
5
+ * settled on was gone after every F5 and they had to hunt for their objects
6
+ * again (runhuman pass 56). The auto-frame design's own outranking rule
7
+ * (auto-frame-window.ts: "the reader's first camera gesture ends the window")
8
+ * already says what a remembered pose is — a gesture that has ALREADY
9
+ * happened — so restoring one replaces the whole window: no measured framing,
10
+ * no game-camera seed. A project with no remembered pose (first ever open)
11
+ * keeps the designed first-look framing untouched.
12
+ *
13
+ * Storage: the `viewportPose` section of the project-local document
14
+ * (`project-local-state.ts`) — per checkout, keyed by the folder itself.
15
+ */
16
+
17
+ import type * as THREE from 'three';
18
+ import { projectLocalSection, writeProjectLocalSection } from '@volter/editor-core/project-local-state';
19
+
20
+ /** The section of the project-local document this module owns. */
21
+ const SECTION = 'viewportPose';
22
+
23
+ export interface ViewportPose {
24
+ readonly position: readonly [number, number, number];
25
+ readonly target: readonly [number, number, number];
26
+ }
27
+
28
+ /** The remembered pose for the OPEN project, or null (first ever open). */
29
+ export function savedViewportPose(): ViewportPose | null {
30
+ const pose = projectLocalSection<ViewportPose>(SECTION);
31
+ return pose &&
32
+ Array.isArray(pose.position) &&
33
+ pose.position.length === 3 &&
34
+ pose.position.every(Number.isFinite) &&
35
+ Array.isArray(pose.target) &&
36
+ pose.target.length === 3 &&
37
+ pose.target.every(Number.isFinite)
38
+ ? pose
39
+ : null;
40
+ }
41
+
42
+ /** Remember where the reader's gesture left the camera. */
43
+ export function saveViewportPose(camera: THREE.PerspectiveCamera, target: THREE.Vector3): void {
44
+ writeProjectLocalSection(SECTION, {
45
+ position: [camera.position.x, camera.position.y, camera.position.z],
46
+ target: [target.x, target.y, target.z],
47
+ } satisfies ViewportPose);
48
+ }
@@ -0,0 +1,40 @@
1
+ import type { ViewportRoot } from '@volter/editor-sdk/host';
2
+ import { stampMountedAuthoringIds } from './authoring/mounted-authoring';
3
+ import type { EditorShellStore } from '@volter/editor-core/editor-shell-store';
4
+ import { stampThreeIdentities } from '@volter/editor-core/projection/three';
5
+
6
+ /**
7
+ * The Three viewport's PRESENTER — what `the world root's stage` binds as
8
+ * `host.viewport.presentRoots` (`viewport-door.ts`). The lane that mounted
9
+ * the roots sees only the returned presentation handle. Native Three scene
10
+ * adoption, identity stamping, and restoration stay here, beside the
11
+ * viewport surface that understands them. A composition with no Three root
12
+ * simply has no subject for this viewport to present.
13
+ */
14
+ export function presentThreeRoots(
15
+ store: Pick<EditorShellStore, 'enterPlayScene' | 'releaseAdoptedScene'>,
16
+ roots: readonly ViewportRoot[],
17
+ ): { readonly worldId: string; dispose(): void } | null {
18
+ const world = roots.find((candidate) => candidate.mounted.kind === 'three');
19
+ if (!world || world.mounted.kind !== 'three') return null;
20
+ const scene = world.mounted.scene;
21
+ const mountedAuthoring = world.mounted.authoring;
22
+ if (mountedAuthoring) stampMountedAuthoringIds(scene, mountedAuthoring);
23
+ stampThreeIdentities(scene, liveWorldId(world.id));
24
+ store.enterPlayScene(scene, world.mounted.rendererConfig);
25
+ let disposed = false;
26
+ return {
27
+ worldId: world.id,
28
+ dispose(): void {
29
+ if (disposed) return;
30
+ disposed = true;
31
+ store.releaseAdoptedScene(scene);
32
+ },
33
+ };
34
+ }
35
+
36
+ /** The id namespace minted ids carry. The fallback supports legacy sessions
37
+ * that predate manifest root identity. */
38
+ export function liveWorldId(worldId: string | null): string {
39
+ return worldId ?? 'main';
40
+ }