@volter/editor-core 0.5.57

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 (719) hide show
  1. package/BUNDLED_NOTICES +1527 -0
  2. package/LICENSE +686 -0
  3. package/LICENSE-APACHE +202 -0
  4. package/NOTICE +18 -0
  5. package/README.md +24 -0
  6. package/dist/build/chunk-JS4D4XZ3.js +175 -0
  7. package/dist/build/chunk-JS4D4XZ3.js.map +7 -0
  8. package/dist/build/vite-plugin-product-contributions.js +100 -0
  9. package/dist/build/vite-plugin-product-contributions.js.map +7 -0
  10. package/dist/build/vite-plugin-shared-react.js +25 -0
  11. package/dist/build/vite-plugin-shared-react.js.map +7 -0
  12. package/dist/build/vite-plugin-shared-three.js +86 -0
  13. package/dist/build/vite-plugin-shared-three.js.map +7 -0
  14. package/dist/server/chunk-KMJC7AGO.js +98 -0
  15. package/dist/server/chunk-KMJC7AGO.js.map +7 -0
  16. package/dist/server/chunk-PSILUMGS.js +46 -0
  17. package/dist/server/chunk-PSILUMGS.js.map +7 -0
  18. package/dist/server/frame-proxy.js +255 -0
  19. package/dist/server/frame-proxy.js.map +7 -0
  20. package/dist/server/open-browser.js +53 -0
  21. package/dist/server/open-browser.js.map +7 -0
  22. package/dist/server/process-shutdown.js +127 -0
  23. package/dist/server/process-shutdown.js.map +7 -0
  24. package/dist/server/session-registry.js +141 -0
  25. package/dist/server/session-registry.js.map +7 -0
  26. package/dist/server/spawn-opener.js +11 -0
  27. package/dist/server/spawn-opener.js.map +7 -0
  28. package/dist/server/worktree-identity.js +11 -0
  29. package/dist/server/worktree-identity.js.map +7 -0
  30. package/dist-server/packaged.mjs +88816 -0
  31. package/package.json +118 -0
  32. package/scripts/build-plugins.mjs +14 -0
  33. package/scripts/build-server.mjs +26 -0
  34. package/scripts/build-session.mjs +9 -0
  35. package/server/account-credentials.ts +60 -0
  36. package/server/account-service.ts +1434 -0
  37. package/server/adapter-region-includes.ts +47 -0
  38. package/server/asset-catalog-v2.ts +175 -0
  39. package/server/asset-history-snapshots.ts +196 -0
  40. package/server/asset-ledger-store.ts +195 -0
  41. package/server/asset-library-routes.ts +1595 -0
  42. package/server/blender-wasm-artifact.ts +430 -0
  43. package/server/boot-timings.ts +106 -0
  44. package/server/canonical-path.ts +50 -0
  45. package/server/checkout-workspace-preflight.ts +349 -0
  46. package/server/cloud-asset-catalog.ts +158 -0
  47. package/server/coding-inference-launch.ts +237 -0
  48. package/server/collaboration-account-client.ts +202 -0
  49. package/server/collaboration-attribution.ts +122 -0
  50. package/server/collaboration-session.ts +934 -0
  51. package/server/comfyui-bridge.ts +248 -0
  52. package/server/console-ledger.ts +499 -0
  53. package/server/creation-site-transform.ts +304 -0
  54. package/server/creation-site-write.ts +648 -0
  55. package/server/data-file-serialize.ts +158 -0
  56. package/server/editor-brand-html.ts +72 -0
  57. package/server/editor-control-socket.ts +366 -0
  58. package/server/editor-server-options.ts +190 -0
  59. package/server/editor-server.ts +1483 -0
  60. package/server/editor-sse.ts +352 -0
  61. package/server/engine-provenance.ts +169 -0
  62. package/server/engine-source-restart.ts +117 -0
  63. package/server/frame-bridge.ts +115 -0
  64. package/server/frame-proxy.ts +490 -0
  65. package/server/frame-workbench.ts +259 -0
  66. package/server/frontend-handoff.ts +229 -0
  67. package/server/game-globals-shadow.ts +113 -0
  68. package/server/gameplay-session-retention.ts +149 -0
  69. package/server/gameplay-sessions.ts +123 -0
  70. package/server/generation-jobs.ts +201 -0
  71. package/server/generation-reconciler.ts +137 -0
  72. package/server/generative-execution-context.ts +59 -0
  73. package/server/git-workflow.ts +612 -0
  74. package/server/harness-chat-caller.ts +36 -0
  75. package/server/harness-chat-service.ts +1681 -0
  76. package/server/idle-shutdown.ts +170 -0
  77. package/server/js-profiling-policy.ts +52 -0
  78. package/server/launcher-settings.ts +84 -0
  79. package/server/local-asset-catalog.ts +410 -0
  80. package/server/managed-account-defaults.ts +22 -0
  81. package/server/mock-control-plane.ts +540 -0
  82. package/server/model-import-conversion.ts +345 -0
  83. package/server/native-credential-store.ts +162 -0
  84. package/server/open-browser.ts +118 -0
  85. package/server/packaged.ts +1192 -0
  86. package/server/play-stall.ts +126 -0
  87. package/server/process-shutdown.ts +218 -0
  88. package/server/product-presets.ts +55 -0
  89. package/server/project-build-artifact.ts +95 -0
  90. package/server/project-components.ts +100 -0
  91. package/server/project-dependency-invalidation.ts +114 -0
  92. package/server/project-file-scan.ts +235 -0
  93. package/server/project-hmr-files.ts +215 -0
  94. package/server/project-install-roots.ts +210 -0
  95. package/server/project-kinds.ts +71 -0
  96. package/server/project-mcp-servers.ts +82 -0
  97. package/server/project-module-freshness.ts +109 -0
  98. package/server/project-module-instance.ts +375 -0
  99. package/server/project-optimize-deps-entries.ts +827 -0
  100. package/server/project-output-writer.ts +679 -0
  101. package/server/project-package-origin.ts +63 -0
  102. package/server/project-root-surface.ts +831 -0
  103. package/server/project-scratch-path.ts +47 -0
  104. package/server/project-script-hmr.ts +619 -0
  105. package/server/project-serving-plugins.ts +236 -0
  106. package/server/project-tools.ts +653 -0
  107. package/server/project-validation.ts +250 -0
  108. package/server/project-verbs.ts +127 -0
  109. package/server/project-view.ts +52 -0
  110. package/server/project-watch.ts +1134 -0
  111. package/server/project-work-coordinator.ts +288 -0
  112. package/server/provider-credentials.ts +331 -0
  113. package/server/recent-projects-store.ts +98 -0
  114. package/server/redact-secrets.ts +71 -0
  115. package/server/repository-presence.ts +267 -0
  116. package/server/routes/account.ts +357 -0
  117. package/server/routes/agents.ts +60 -0
  118. package/server/routes/assets.ts +154 -0
  119. package/server/routes/build.ts +13 -0
  120. package/server/routes/collaboration.ts +595 -0
  121. package/server/routes/configurations.ts +419 -0
  122. package/server/routes/context.ts +264 -0
  123. package/server/routes/control-plane.ts +1811 -0
  124. package/server/routes/logs.ts +272 -0
  125. package/server/routes/project-identity.ts +302 -0
  126. package/server/routes/project-open.ts +429 -0
  127. package/server/routes/project-source.ts +686 -0
  128. package/server/routes/project-state.ts +383 -0
  129. package/server/routes/relay.ts +1029 -0
  130. package/server/routes/route-helpers.ts +29 -0
  131. package/server/routes/served-modules.ts +138 -0
  132. package/server/routes/session-tabs.ts +196 -0
  133. package/server/routes/settings.ts +121 -0
  134. package/server/routes/share-control.ts +486 -0
  135. package/server/routes/themes.ts +116 -0
  136. package/server/routes/tools.ts +230 -0
  137. package/server/routes/worktrees.ts +595 -0
  138. package/server/scoped-game-css.ts +461 -0
  139. package/server/server-utils.ts +1194 -0
  140. package/server/session-product.ts +112 -0
  141. package/server/session-registry.ts +230 -0
  142. package/server/share-claims.ts +138 -0
  143. package/server/share-host.ts +526 -0
  144. package/server/share-session-gateway.ts +1362 -0
  145. package/server/share-tunnel.ts +123 -0
  146. package/server/spawn-opener.ts +74 -0
  147. package/server/support/play/log-format.ts +76 -0
  148. package/server/support/play/session-record.ts +199 -0
  149. package/server/support/project/build-discipline.ts +718 -0
  150. package/server/support/project/inspection-node.ts +119 -0
  151. package/server/support/project/log-naming.ts +54 -0
  152. package/server/support/project/provenance.ts +290 -0
  153. package/server/support/project/run-name.ts +18 -0
  154. package/server/support/project/session-journal.ts +830 -0
  155. package/server/support/project/shared.ts +327 -0
  156. package/server/tab-bootstrap.ts +62 -0
  157. package/server/tab-heartbeat.ts +477 -0
  158. package/server/tab-lifecycle.ts +550 -0
  159. package/server/tab-presence.ts +1508 -0
  160. package/server/team-agent-mirror.ts +95 -0
  161. package/server/twin-auth.ts +138 -0
  162. package/server/vendored-lock-recorder.ts +968 -0
  163. package/server/worktree-identity.ts +121 -0
  164. package/server/worktree-management.ts +398 -0
  165. package/server/worktree-retention.d.mts +13 -0
  166. package/server/worktree-retention.mjs +205 -0
  167. package/src/EditorContext.tsx +169 -0
  168. package/src/account.ts +333 -0
  169. package/src/action-registry.ts +532 -0
  170. package/src/active-product.ts +74 -0
  171. package/src/active-project.ts +155 -0
  172. package/src/adapter-editor-config.ts +25 -0
  173. package/src/adapter-observation.ts +49 -0
  174. package/src/animation/stage-transport.ts +379 -0
  175. package/src/animation/three-clips-subject.ts +172 -0
  176. package/src/api/asset-library-wire.ts +45 -0
  177. package/src/api/assets.ts +365 -0
  178. package/src/api/base.ts +10 -0
  179. package/src/api/build.ts +99 -0
  180. package/src/api/git-wire.ts +56 -0
  181. package/src/api/logs.ts +92 -0
  182. package/src/api/project-identity.ts +74 -0
  183. package/src/api/project-open.ts +355 -0
  184. package/src/api/project-source.ts +162 -0
  185. package/src/api/project-state.ts +107 -0
  186. package/src/api/relay.ts +268 -0
  187. package/src/api/settings.ts +36 -0
  188. package/src/api/themes.ts +45 -0
  189. package/src/api/worktrees.ts +257 -0
  190. package/src/asset-compare-core.ts +171 -0
  191. package/src/asset-compare.ts +294 -0
  192. package/src/asset-editor-context.tsx +100 -0
  193. package/src/asset-events.ts +97 -0
  194. package/src/asset-inspector-actions.ts +87 -0
  195. package/src/asset-preview-framing.ts +357 -0
  196. package/src/asset-preview.ts +2802 -0
  197. package/src/asset-selection-viewer-registry.ts +113 -0
  198. package/src/asset-selection.ts +133 -0
  199. package/src/asset-workflow/asset-capabilities.ts +344 -0
  200. package/src/asset-workflow/asset-import-jobs.ts +106 -0
  201. package/src/asset-workflow/asset-ledger-backend.ts +126 -0
  202. package/src/asset-workflow/asset-ledger.ts +156 -0
  203. package/src/asset-workflow/asset-materialization-report.ts +140 -0
  204. package/src/asset-workflow/asset-pack-manifest.ts +320 -0
  205. package/src/asset-workflow/asset-types.ts +142 -0
  206. package/src/asset-workflow/asset-workflow-quality.ts +61 -0
  207. package/src/asset-workflow/audio-preview-player.ts +193 -0
  208. package/src/asset-workflow/audio-waveform.ts +22 -0
  209. package/src/asset-workflow/cloud-asset-client.ts +263 -0
  210. package/src/asset-workflow/folder-preview.ts +345 -0
  211. package/src/asset-workflow/hosted-asset-materialization.ts +236 -0
  212. package/src/asset-workflow/image-view-scale.ts +32 -0
  213. package/src/asset-workflow/import-contract.ts +124 -0
  214. package/src/asset-workflow/ledger-write-lock.ts +244 -0
  215. package/src/asset-workflow/model-inspection.ts +818 -0
  216. package/src/asset-workflow/pixi-spritesheet.ts +197 -0
  217. package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
  218. package/src/asset-workflow/project-asset-commands.ts +23 -0
  219. package/src/asset-workflow/project-asset-health.ts +139 -0
  220. package/src/asset-workflow/project-asset-operations.ts +154 -0
  221. package/src/asset-workflow/project-asset-roots.ts +68 -0
  222. package/src/asset-workflow/project-content.ts +288 -0
  223. package/src/asset-workflow/project-source-index.ts +550 -0
  224. package/src/asset-workflow/thumbnail-system.ts +256 -0
  225. package/src/authoring/active-adapter.ts +199 -0
  226. package/src/authoring/active-systems.ts +422 -0
  227. package/src/authoring/adapter-key.ts +18 -0
  228. package/src/authoring/authoring-asset-url.ts +27 -0
  229. package/src/authoring/bootstrap-state.ts +49 -0
  230. package/src/authoring/boundary-authoring-adapter.ts +184 -0
  231. package/src/authoring/breakpoint-state.ts +43 -0
  232. package/src/authoring/canvas-scene-guides.ts +76 -0
  233. package/src/authoring/component-instance-root.ts +171 -0
  234. package/src/authoring/composite-authoring-adapter.ts +2110 -0
  235. package/src/authoring/consumer-actions.ts +520 -0
  236. package/src/authoring/css-numeric-style.ts +97 -0
  237. package/src/authoring/design-time-layers.ts +849 -0
  238. package/src/authoring/design-time-mount-registry.ts +235 -0
  239. package/src/authoring/design-time-settle.ts +343 -0
  240. package/src/authoring/edit-mode-authoring.ts +619 -0
  241. package/src/authoring/eyedropper-session.ts +60 -0
  242. package/src/authoring/instance-source-menu-register.ts +13 -0
  243. package/src/authoring/instance-source-menu.ts +135 -0
  244. package/src/authoring/layered-pick.ts +183 -0
  245. package/src/authoring/live-gesture-lock.ts +50 -0
  246. package/src/authoring/live-object-transform.ts +62 -0
  247. package/src/authoring/mount-failure-report.ts +154 -0
  248. package/src/authoring/mounted-root-subjects.ts +144 -0
  249. package/src/authoring/no-authoring-adapter.ts +55 -0
  250. package/src/authoring/null-inspection-subjects.tsx +75 -0
  251. package/src/authoring/object3d-document-persistence.ts +106 -0
  252. package/src/authoring/object3d-document-session-registry.ts +120 -0
  253. package/src/authoring/object3d-document-session.ts +1326 -0
  254. package/src/authoring/object3d-gesture-controller.ts +113 -0
  255. package/src/authoring/panel-authoring.ts +121 -0
  256. package/src/authoring/prefab-instance-inspector-section.tsx +237 -0
  257. package/src/authoring/prefab-instance-section-model.ts +55 -0
  258. package/src/authoring/project-authoring-session.ts +105 -0
  259. package/src/authoring/provenance.ts +99 -0
  260. package/src/authoring/quarks-particle-systems.ts +19 -0
  261. package/src/authoring/react-canvas-navigation.ts +255 -0
  262. package/src/authoring/react-design-canvas-style.ts +20 -0
  263. package/src/authoring/react-story-board.ts +937 -0
  264. package/src/authoring/selection-scope.ts +195 -0
  265. package/src/authoring/shell-document-ops.ts +169 -0
  266. package/src/authoring/shell-object3d-document-write-policy.ts +108 -0
  267. package/src/authoring/shell-viewport-policy.ts +48 -0
  268. package/src/authoring/source-object3d-authoring-adapter.ts +526 -0
  269. package/src/authoring/stories-scope.ts +35 -0
  270. package/src/authoring/story-board-chrome-fit.ts +107 -0
  271. package/src/authoring/story-board-presentation.ts +111 -0
  272. package/src/authoring/three-projection-core.ts +226 -0
  273. package/src/authoring/viewport-pick-context.ts +40 -0
  274. package/src/authoring/viewport-raycast.ts +240 -0
  275. package/src/authoring/viewport-tool-context.ts +73 -0
  276. package/src/authoring/world-canvas-viewport-state.ts +35 -0
  277. package/src/authoring/world-hidden-viewport.ts +152 -0
  278. package/src/authoring/world-pan-state.ts +198 -0
  279. package/src/authoring/world-session-state.ts +101 -0
  280. package/src/authoring/write-pipe.ts +173 -0
  281. package/src/availability-tick.ts +66 -0
  282. package/src/bitmap-label.ts +120 -0
  283. package/src/blender-tab-metrics.ts +161 -0
  284. package/src/board-open-actions.ts +20 -0
  285. package/src/boot-routing.ts +391 -0
  286. package/src/build-identity.ts +15 -0
  287. package/src/bytes-codec.ts +62 -0
  288. package/src/camera-authoring.ts +175 -0
  289. package/src/cancellation-reason.ts +58 -0
  290. package/src/canvas-preview-frames.ts +482 -0
  291. package/src/capture-camera-pose.ts +77 -0
  292. package/src/chrome-registry.ts +159 -0
  293. package/src/chrome-slot-registry.ts +91 -0
  294. package/src/collaboration-client.ts +264 -0
  295. package/src/collaboration-presence.ts +41 -0
  296. package/src/command-dispatch.ts +19 -0
  297. package/src/command-listener.ts +2743 -0
  298. package/src/command-registry.ts +70 -0
  299. package/src/component-board-registry.ts +205 -0
  300. package/src/component-states-registry.ts +200 -0
  301. package/src/components/AgentPresentationNotice.tsx +75 -0
  302. package/src/components/AlignToolbar.tsx +204 -0
  303. package/src/components/AppRoot.tsx +394 -0
  304. package/src/components/ApplicationMenus.tsx +415 -0
  305. package/src/components/AssetBrowser.tsx +2843 -0
  306. package/src/components/AssetEditorShell.tsx +216 -0
  307. package/src/components/AssetInspectorToolSection.tsx +124 -0
  308. package/src/components/BoardRulers.tsx +354 -0
  309. package/src/components/CameraInfo.tsx +81 -0
  310. package/src/components/CanvasSceneViewport.tsx +556 -0
  311. package/src/components/CapabilityCoverageSection.tsx +185 -0
  312. package/src/components/CenterDocuments.tsx +22 -0
  313. package/src/components/ChromeSlot.tsx +20 -0
  314. package/src/components/CodeView.tsx +470 -0
  315. package/src/components/CollaborationHeaderControl.css +74 -0
  316. package/src/components/CompactInspectorCard.tsx +190 -0
  317. package/src/components/CompactInspectorShell.tsx +39 -0
  318. package/src/components/ConsolePanel.css +55 -0
  319. package/src/components/ConsolePanel.tsx +270 -0
  320. package/src/components/DefaultEditorLayout.tsx +106 -0
  321. package/src/components/DocumentHeaderStrip.tsx +96 -0
  322. package/src/components/DocumentShelfRail.tsx +45 -0
  323. package/src/components/DocumentThumbnail.tsx +345 -0
  324. package/src/components/EditorLeaseGuard.tsx +372 -0
  325. package/src/components/ErrorBanner.tsx +123 -0
  326. package/src/components/FolderPreviewTile.tsx +159 -0
  327. package/src/components/GameHierarchy.tsx +3842 -0
  328. package/src/components/GameplaySessionTimeline.tsx +295 -0
  329. package/src/components/InspectionProjection.tsx +968 -0
  330. package/src/components/Inspector.tsx +233 -0
  331. package/src/components/InspectorCanvasPreview.tsx +35 -0
  332. package/src/components/InspectorFieldsSection.tsx +290 -0
  333. package/src/components/InspectorObjectPreview.tsx +57 -0
  334. package/src/components/InspectorStoriesSection.tsx +92 -0
  335. package/src/components/InspectorToolSection.tsx +96 -0
  336. package/src/components/InspectorTransformSection.tsx +245 -0
  337. package/src/components/LightExplorerPanel.tsx +433 -0
  338. package/src/components/MediaProperties.tsx +145 -0
  339. package/src/components/NonThreeAuthoringBootstrap.tsx +19 -0
  340. package/src/components/Object3DDocumentToolbar.css +100 -0
  341. package/src/components/Object3DDocumentToolbar.tsx +454 -0
  342. package/src/components/Object3DDocumentViewport.tsx +58 -0
  343. package/src/components/ProjectHeader.css +617 -0
  344. package/src/components/ProjectHeader.tsx +328 -0
  345. package/src/components/ProjectLayout.tsx +249 -0
  346. package/src/components/ReactCanvasControls.tsx +284 -0
  347. package/src/components/RootSelectionOverlay.tsx +3162 -0
  348. package/src/components/RootTextEditor.tsx +79 -0
  349. package/src/components/SaveStatus.tsx +70 -0
  350. package/src/components/StageHost.tsx +1954 -0
  351. package/src/components/StageOverlays.tsx +21 -0
  352. package/src/components/StartupErrorScreen.tsx +76 -0
  353. package/src/components/StartupLoadingScreen.tsx +65 -0
  354. package/src/components/StatsOverlay.tsx +78 -0
  355. package/src/components/SurfaceStateOverlay.tsx +24 -0
  356. package/src/components/ToolContributionSurfaces.tsx +65 -0
  357. package/src/components/ToolHost.tsx +370 -0
  358. package/src/components/ToolObject3DPreview.tsx +39 -0
  359. package/src/components/Toolbar.tsx +595 -0
  360. package/src/components/TransientHint.tsx +44 -0
  361. package/src/components/TransportStrip.tsx +174 -0
  362. package/src/components/VersionControlSection.tsx +470 -0
  363. package/src/components/VgaiLogo.css +83 -0
  364. package/src/components/VgaiLogo.tsx +35 -0
  365. package/src/components/ViewportControlsHint.tsx +60 -0
  366. package/src/components/ViewportFurniture.tsx +401 -0
  367. package/src/components/ViewportOverlay.tsx +145 -0
  368. package/src/components/ViewportOverlaysMenu.tsx +153 -0
  369. package/src/components/ViewportShadingMenu.tsx +300 -0
  370. package/src/components/ViewportViewMenu.tsx +101 -0
  371. package/src/components/WorkspaceDocumentSurface.tsx +204 -0
  372. package/src/components/WorkspaceUtilitySurface.tsx +22 -0
  373. package/src/components/WorktreeSwitcher.css +239 -0
  374. package/src/components/WorktreeSwitcher.tsx +890 -0
  375. package/src/components/account-documents.tsx +1162 -0
  376. package/src/components/asset-documents.tsx +812 -0
  377. package/src/components/asset-editor-persistence.ts +216 -0
  378. package/src/components/asset-selection-section.tsx +535 -0
  379. package/src/components/asset-thumbnails.tsx +312 -0
  380. package/src/components/asset-viewers/AudioViewer.tsx +201 -0
  381. package/src/components/asset-viewers/EntityModelDocument.tsx +122 -0
  382. package/src/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
  383. package/src/components/asset-viewers/GenericJsonViewer.tsx +102 -0
  384. package/src/components/asset-viewers/ImageViewer.tsx +300 -0
  385. package/src/components/asset-viewers/JsonAssetDocument.tsx +93 -0
  386. package/src/components/asset-viewers/LiveModuleDocument.tsx +417 -0
  387. package/src/components/asset-viewers/LutAssetDocument.tsx +444 -0
  388. package/src/components/asset-viewers/ModelAssetDocument.tsx +105 -0
  389. package/src/components/asset-viewers/Object3DPreview.tsx +359 -0
  390. package/src/components/asset-viewers/OnlineAssetDetail.tsx +421 -0
  391. package/src/components/asset-viewers/PasteboardModuleDocument.tsx +67 -0
  392. package/src/components/asset-viewers/QuarksAssetDocument.tsx +526 -0
  393. package/src/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
  394. package/src/components/asset-viewers/SourceAssetViewer.tsx +281 -0
  395. package/src/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
  396. package/src/components/asset-viewers/VideoViewer.tsx +101 -0
  397. package/src/components/asset-viewers/shader-source.ts +144 -0
  398. package/src/components/asset-workflow.css +731 -0
  399. package/src/components/board-guides.ts +150 -0
  400. package/src/components/compact-inspector.css +539 -0
  401. package/src/components/core-utilities.tsx +90 -0
  402. package/src/components/editor-notifications.css +40 -0
  403. package/src/components/engine-workspace.css +274 -0
  404. package/src/components/inspector-preview-section.tsx +238 -0
  405. package/src/components/inspector-property-grouping.ts +64 -0
  406. package/src/components/inspector-revert-label.ts +20 -0
  407. package/src/components/inspector-selection.ts +42 -0
  408. package/src/components/inspector-stories-gating.ts +171 -0
  409. package/src/components/inspector-transform-subject.ts +11 -0
  410. package/src/components/inspector-transform.ts +75 -0
  411. package/src/components/kind-documents.tsx +500 -0
  412. package/src/components/palette-action-publisher.tsx +140 -0
  413. package/src/components/primitives/DraftColorInput.tsx +74 -0
  414. package/src/components/product-shell.css +1829 -0
  415. package/src/components/project-tool-documents.tsx +410 -0
  416. package/src/components/scene-documents.tsx +223 -0
  417. package/src/components/stage-keyboard.tsx +37 -0
  418. package/src/components/stage-overlay-set.tsx +107 -0
  419. package/src/components/stage-presence-markers.ts +478 -0
  420. package/src/components/standard-viewport-dressing.ts +417 -0
  421. package/src/components/status-contributions.tsx +403 -0
  422. package/src/components/tool-documents.tsx +309 -0
  423. package/src/components/tool-schema-form.tsx +262 -0
  424. package/src/components/use-after-paint.ts +41 -0
  425. package/src/components/use-project-image-assets.ts +86 -0
  426. package/src/components/viewport-header-controls.css +90 -0
  427. package/src/components/viewport-surface-status.tsx +55 -0
  428. package/src/components/workspace-history.ts +32 -0
  429. package/src/components/workspace-static-panel-registry.tsx +152 -0
  430. package/src/components/workspace-surfaces.css +621 -0
  431. package/src/components/world-documents.tsx +586 -0
  432. package/src/components/world-overlay-gestures.ts +1694 -0
  433. package/src/composite-screenshot.ts +1641 -0
  434. package/src/console-sync.ts +131 -0
  435. package/src/constraint-helper.ts +338 -0
  436. package/src/content-entry-source-registry.ts +184 -0
  437. package/src/coverage/authoring-seam-evidence.ts +300 -0
  438. package/src/coverage/canvas-reveal.ts +192 -0
  439. package/src/coverage/design-time-surfaces.ts +101 -0
  440. package/src/coverage/live-seam-evidence.ts +11 -0
  441. package/src/coverage/ontology-invariants.ts +466 -0
  442. package/src/coverage/session-vitals.ts +501 -0
  443. package/src/coverage/system-seam-evidence.ts +72 -0
  444. package/src/crash-null-boundary.ts +36 -0
  445. package/src/creation-site-edit.ts +1479 -0
  446. package/src/creation-site-registry.ts +160 -0
  447. package/src/delegate-harness-registry.ts +143 -0
  448. package/src/document-context-registry.ts +177 -0
  449. package/src/document-open-registry.ts +200 -0
  450. package/src/document-preview-source.ts +20 -0
  451. package/src/document-renderer-session.ts +138 -0
  452. package/src/editor-api.ts +46 -0
  453. package/src/editor-chrome-capture.ts +139 -0
  454. package/src/editor-commands.ts +164 -0
  455. package/src/editor-console.ts +563 -0
  456. package/src/editor-current-view.ts +82 -0
  457. package/src/editor-document-probe.ts +881 -0
  458. package/src/editor-git-client.ts +115 -0
  459. package/src/editor-host-door.ts +462 -0
  460. package/src/editor-hotkeys.ts +842 -0
  461. package/src/editor-lease-view.ts +39 -0
  462. package/src/editor-lease.ts +415 -0
  463. package/src/editor-mode.ts +19 -0
  464. package/src/editor-notifications.ts +140 -0
  465. package/src/editor-presence.ts +563 -0
  466. package/src/editor-presentation-activity.ts +50 -0
  467. package/src/editor-presentation-notice.ts +42 -0
  468. package/src/editor-runtime.tsx +145 -0
  469. package/src/editor-server-response.ts +86 -0
  470. package/src/editor-session-attribution.ts +75 -0
  471. package/src/editor-session-mode.ts +54 -0
  472. package/src/editor-shell-store.ts +1304 -0
  473. package/src/editor-state-facets.ts +74 -0
  474. package/src/editor-styles.css +33 -0
  475. package/src/editor-view-presentation.ts +810 -0
  476. package/src/editor-viewport.ts +5302 -0
  477. package/src/entity-lod.ts +31 -0
  478. package/src/entity-object.ts +91 -0
  479. package/src/files/file-provider.ts +62 -0
  480. package/src/files/project-files.ts +264 -0
  481. package/src/finders/index.ts +136 -0
  482. package/src/finders/scenes-from-entrypoint-selection.ts +387 -0
  483. package/src/frame/bridge.tsx +1408 -0
  484. package/src/frame/product.ts +77 -0
  485. package/src/gameplay-dom-recording.ts +319 -0
  486. package/src/gameplay-export-state.ts +14 -0
  487. package/src/gameplay-replay.ts +417 -0
  488. package/src/gameplay-session-time.ts +9 -0
  489. package/src/gameplay-sessions.ts +204 -0
  490. package/src/harness-chat-types.ts +253 -0
  491. package/src/hierarchy-component-marks.ts +298 -0
  492. package/src/hierarchy-drop.ts +91 -0
  493. package/src/hierarchy-expansion-state.ts +80 -0
  494. package/src/hierarchy-header-slot.ts +52 -0
  495. package/src/hierarchy-internals.ts +197 -0
  496. package/src/hierarchy-kind-icon.ts +217 -0
  497. package/src/hierarchy-mark-reader.ts +73 -0
  498. package/src/hierarchy-menu-registry.ts +67 -0
  499. package/src/hierarchy-node-rows.ts +307 -0
  500. package/src/hierarchy-panel-view.ts +280 -0
  501. package/src/hierarchy-projection.ts +76 -0
  502. package/src/hierarchy-row-cache.ts +243 -0
  503. package/src/hierarchy-row-model.ts +308 -0
  504. package/src/hierarchy-rows.ts +11 -0
  505. package/src/hierarchy-walk.ts +86 -0
  506. package/src/history/editor-session.ts +25 -0
  507. package/src/history/history-commands.ts +147 -0
  508. package/src/history/history-delegate.ts +163 -0
  509. package/src/history/history-limit-notices.ts +43 -0
  510. package/src/history/history-service.ts +1173 -0
  511. package/src/history/persistence-coordinator.ts +35 -0
  512. package/src/history/project-file-history.ts +386 -0
  513. package/src/history/project-root-history-backends.ts +139 -0
  514. package/src/history/resource-registry.ts +209 -0
  515. package/src/history/snapshot-store.ts +103 -0
  516. package/src/history/source-history-backend.ts +546 -0
  517. package/src/history/types.ts +124 -0
  518. package/src/hmr-registration-group.ts +67 -0
  519. package/src/hmr-stable-react-context.ts +23 -0
  520. package/src/hotkeys.ts +188 -0
  521. package/src/inference-diagnostics.ts +69 -0
  522. package/src/initial-project.ts +80 -0
  523. package/src/inspection/active-subject.ts +578 -0
  524. package/src/inspection/active-surface.ts +142 -0
  525. package/src/inspection/compose.ts +1064 -0
  526. package/src/inspection/display.ts +170 -0
  527. package/src/inspection/document-subject.ts +109 -0
  528. package/src/inspection/game-subject.ts +85 -0
  529. package/src/inspection/model.ts +542 -0
  530. package/src/inspection/null-subject.ts +115 -0
  531. package/src/inspection/serialize.ts +357 -0
  532. package/src/inspection/use-active-inspection.ts +180 -0
  533. package/src/inspector-presentation.ts +201 -0
  534. package/src/inspector-section-registry.ts +221 -0
  535. package/src/instance-source-actions.ts +163 -0
  536. package/src/instanced-presentation.ts +164 -0
  537. package/src/js-heap.ts +71 -0
  538. package/src/key-actions.ts +91 -0
  539. package/src/keymap-presets.ts +401 -0
  540. package/src/layout-policy.ts +31 -0
  541. package/src/learn-links.ts +73 -0
  542. package/src/light-explorer-model.ts +134 -0
  543. package/src/live-canvas-frame.ts +55 -0
  544. package/src/live-document.ts +279 -0
  545. package/src/live-module-source.ts +230 -0
  546. package/src/live-session-registry.ts +220 -0
  547. package/src/live-transition.ts +633 -0
  548. package/src/manifest-project.ts +107 -0
  549. package/src/model-thumbnail.ts +523 -0
  550. package/src/native-selection-style.ts +202 -0
  551. package/src/object3d-document-write-policy.ts +137 -0
  552. package/src/packaged-runtime.ts +108 -0
  553. package/src/palettes/maya.palette.json +57 -0
  554. package/src/palettes/substance.palette.json +57 -0
  555. package/src/pasteboard-module.ts +81 -0
  556. package/src/performance-profiler.ts +367 -0
  557. package/src/performance-sources.ts +69 -0
  558. package/src/play-boot-phase.ts +145 -0
  559. package/src/presentation-surface.ts +248 -0
  560. package/src/project-adapter.ts +1143 -0
  561. package/src/project-asset-refresh.ts +26 -0
  562. package/src/project-declaration-refresh.ts +31 -0
  563. package/src/project-local-state.ts +118 -0
  564. package/src/project-manager.ts +243 -0
  565. package/src/project-module-changes.ts +163 -0
  566. package/src/project-module-split.ts +266 -0
  567. package/src/project-provenance.ts +115 -0
  568. package/src/project-ready.ts +42 -0
  569. package/src/project-session-reset.ts +67 -0
  570. package/src/project-shape.ts +68 -0
  571. package/src/project-tool-discovery.ts +66 -0
  572. package/src/project-tools.ts +107 -0
  573. package/src/project-work-types.ts +149 -0
  574. package/src/projection/three.ts +898 -0
  575. package/src/projection/types.ts +44 -0
  576. package/src/readiness.ts +113 -0
  577. package/src/reflection-probe-helper.ts +142 -0
  578. package/src/reported-play-state.ts +90 -0
  579. package/src/resolve-relative-specifier.ts +33 -0
  580. package/src/scene-document-plan.ts +315 -0
  581. package/src/scene-framing.ts +315 -0
  582. package/src/scene-live-open.ts +210 -0
  583. package/src/scene-view-fog.ts +89 -0
  584. package/src/scoped-game-css.ts +152 -0
  585. package/src/session-orphan-record.ts +193 -0
  586. package/src/session-tombstone.ts +126 -0
  587. package/src/settings/settings-provider.ts +82 -0
  588. package/src/settings-store.ts +345 -0
  589. package/src/shared-view-restore.ts +42 -0
  590. package/src/shell-store-door.ts +45 -0
  591. package/src/source-conflict.ts +122 -0
  592. package/src/spatial-handle-visuals.ts +332 -0
  593. package/src/stage-context.ts +341 -0
  594. package/src/stage-store-registry.ts +63 -0
  595. package/src/stale-chunk-recovery.ts +34 -0
  596. package/src/state-report-deferral.ts +73 -0
  597. package/src/storage/host-files-storage.ts +97 -0
  598. package/src/storage/http-storage.ts +174 -0
  599. package/src/storage/index.ts +75 -0
  600. package/src/storage/mem-storage.ts +149 -0
  601. package/src/storage/path-lock.ts +44 -0
  602. package/src/storage/paths.ts +26 -0
  603. package/src/storage/types.ts +116 -0
  604. package/src/stories/StoryComponentThumbnail.tsx +184 -0
  605. package/src/stories/StoryPreviewMount.tsx +306 -0
  606. package/src/stories/component-content-source.tsx +106 -0
  607. package/src/stories/component-states-source.ts +78 -0
  608. package/src/stories/compose-project-stories.ts +255 -0
  609. package/src/stories/pixi-story-model.ts +30 -0
  610. package/src/stories/prefabs-finder.ts +54 -0
  611. package/src/stories/prefabs-from-stories.ts +182 -0
  612. package/src/stories/project-story-discovery.ts +78 -0
  613. package/src/stories/project-story-regions.ts +24 -0
  614. package/src/stories/story-actions.ts +54 -0
  615. package/src/stories/story-capture-command.ts +124 -0
  616. package/src/stories/story-capture.ts +584 -0
  617. package/src/stories/story-declared-medium.ts +126 -0
  618. package/src/stories/story-discovery.ts +176 -0
  619. package/src/stories/story-document-openers.ts +36 -0
  620. package/src/stories/story-dom-runtime.ts +78 -0
  621. package/src/stories/story-grouping.ts +111 -0
  622. package/src/stories/story-lane.ts +88 -0
  623. package/src/stories/story-mount-turn.ts +27 -0
  624. package/src/stories/story-opener.ts +260 -0
  625. package/src/stories/story-pixi-preview.ts +408 -0
  626. package/src/stories/story-presentation.ts +215 -0
  627. package/src/stories/story-registry.ts +517 -0
  628. package/src/stories/story-three-preview.ts +807 -0
  629. package/src/stories/three-story-model.ts +96 -0
  630. package/src/story-three-preview-runtime.ts +56 -0
  631. package/src/surface-keyboard.ts +112 -0
  632. package/src/surface-state.ts +124 -0
  633. package/src/tab-bootstrap.js +628 -0
  634. package/src/tab-census.ts +198 -0
  635. package/src/tab-lifecycle-client.ts +196 -0
  636. package/src/theme-library.ts +844 -0
  637. package/src/theme-preference.ts +378 -0
  638. package/src/theme.css +3437 -0
  639. package/src/three-viewport/bone-selection-highlight.ts +119 -0
  640. package/src/three-viewport/camera-fit.ts +41 -0
  641. package/src/three-viewport/interactive-renderer.ts +132 -0
  642. package/src/three-viewport/selection-brackets.ts +310 -0
  643. package/src/three-viewport/selection-outline.ts +183 -0
  644. package/src/three-viewport/skeleton-helper.ts +61 -0
  645. package/src/three-viewport/source-color.ts +167 -0
  646. package/src/three-viewport/studio-environment.ts +96 -0
  647. package/src/three-viewport-presentation.ts +23 -0
  648. package/src/tool-contribution-play.ts +74 -0
  649. package/src/tool-loader.ts +1798 -0
  650. package/src/transform-mode-request.ts +57 -0
  651. package/src/transient-hint.ts +78 -0
  652. package/src/trigger-volume-helper.ts +116 -0
  653. package/src/ui-source/adapter-region-includes.ts +241 -0
  654. package/src/ui-source/ensure-import.ts +132 -0
  655. package/src/ui-source/file-region-resolver.ts +302 -0
  656. package/src/ui-source/inspect.ts +775 -0
  657. package/src/ui-source/oid-transform.ts +1845 -0
  658. package/src/ui-source/plan-csf-story.ts +196 -0
  659. package/src/ui-source/plan-extract-component.ts +421 -0
  660. package/src/ui-source/plan-fork-component.ts +621 -0
  661. package/src/ui-source/plan-named-style.ts +95 -0
  662. package/src/ui-source/plan-source-edit.ts +353 -0
  663. package/src/ui-source/r3f-contract-resolver.ts +327 -0
  664. package/src/ui-source/r3f-diagnostic-index.ts +165 -0
  665. package/src/ui-source/r3f-environment-binding.ts +133 -0
  666. package/src/ui-source/r3f-joint-binding.ts +251 -0
  667. package/src/ui-source/r3f-lod-binding.ts +91 -0
  668. package/src/ui-source/r3f-particle-binding.ts +235 -0
  669. package/src/ui-source/r3f-physics-binding.ts +483 -0
  670. package/src/ui-source/r3f-project-contracts.ts +444 -0
  671. package/src/ui-source/relative-import-specifier.ts +34 -0
  672. package/src/ui-source/reparent-guard.ts +544 -0
  673. package/src/ui-source/source-edit-request.ts +97 -0
  674. package/src/ui-source/source-write-backend.ts +618 -0
  675. package/src/ui-source/syntactic-prop-specs.ts +296 -0
  676. package/src/ui-source/tier-source-write-backend.ts +279 -0
  677. package/src/ui-source/ts-ast.ts +171 -0
  678. package/src/ui-source/utility-class-support.ts +169 -0
  679. package/src/ui-source/write-component-default.ts +133 -0
  680. package/src/ui-source/writer.ts +1989 -0
  681. package/src/viewport-activation-timings.ts +840 -0
  682. package/src/viewport-authoring-policy.ts +186 -0
  683. package/src/viewport-controls-hint.ts +57 -0
  684. package/src/viewport-door.ts +237 -0
  685. package/src/viewport-shading-boundary.ts +12 -0
  686. package/src/vite-error-surface.ts +141 -0
  687. package/src/wait-until.ts +37 -0
  688. package/src/workspace-areas.ts +156 -0
  689. package/src/workspace-aux-commands.ts +11 -0
  690. package/src/workspace-available-documents.ts +127 -0
  691. package/src/workspace-core-utilities.ts +31 -0
  692. package/src/workspace-document-ids.ts +59 -0
  693. package/src/workspace-document-registry.ts +612 -0
  694. package/src/workspace-document-restore.ts +145 -0
  695. package/src/workspace-host-commands.ts +141 -0
  696. package/src/workspace-persistence-gate.ts +40 -0
  697. package/src/workspace-play-utilities.ts +44 -0
  698. package/src/workspace-presets.ts +425 -0
  699. package/src/workspace-regions.ts +294 -0
  700. package/src/workspace-state-persistence.ts +536 -0
  701. package/src/workspace-static-panels.ts +73 -0
  702. package/src/workspace-status-registry.ts +121 -0
  703. package/src/workspace-style.ts +239 -0
  704. package/src/workspace-utility-commands.ts +74 -0
  705. package/src/workspace-utility-registry.ts +263 -0
  706. package/src/workspace-viewport-rect.ts +97 -0
  707. package/src/world-adoption.ts +115 -0
  708. package/src/world-document-routing.ts +104 -0
  709. package/vite-plugin-creation-site-write.ts +184 -0
  710. package/vite-plugin-creation-site.ts +80 -0
  711. package/vite-plugin-game-static.ts +303 -0
  712. package/vite-plugin-module-doorways.ts +336 -0
  713. package/vite-plugin-product-contributions.ts +197 -0
  714. package/vite-plugin-project-game-static.ts +125 -0
  715. package/vite-plugin-project-jsx-js.ts +65 -0
  716. package/vite-plugin-project-root-absolute-assets.ts +359 -0
  717. package/vite-plugin-shared-react.ts +420 -0
  718. package/vite-plugin-shared-three.ts +203 -0
  719. package/vite-plugin-ui-oid.ts +2106 -0
@@ -0,0 +1,849 @@
1
+ /**
2
+ * Design-time layer stack (B1) — the LIFECYCLE of the design-time layers a
3
+ * caller-owned authoring host shows, over mounts the surface PACKAGES
4
+ * register (`design-time-mount-registry.ts`). Runtime still uses a z-ordered
5
+ * composite, but the editor supplies a distinct host per world document; only
6
+ * Game presents the composed result. Layers default `pointer-events:none`;
7
+ * the eye (`world-session-state.ts`'s `hidden`) hides a layer; `interactive`
8
+ * forwards real pointer events for design-time hover/press testing (a mount
9
+ * whose medium IS the document's subject declares `alwaysInteractive` and
10
+ * skips that toggle); `pickLock` is stored only (B4 consumes it in the pick
11
+ * walk). A layer that throws on mount degrades to its A1
12
+ * `BoundaryAuthoringAdapter` error node (#18) via `mount-failure-report.ts` —
13
+ * never a blank editor; every OTHER world (and the focused three world in
14
+ * particular) keeps working regardless.
15
+ *
16
+ * On a successful mount, the world's initial Boundary adapter is REPLACED in
17
+ * the composite by the adapter its mount minted
18
+ * (`CompositeAuthoringAdapter.replaceChild`, B1).
19
+ *
20
+ * `dom` and `canvas` children are candidates for these hosts. The manifest's
21
+ * sole three content root renders through `the world root's stage`'s own
22
+ * `<canvas>` instead, so this module never mounts a competing Three document.
23
+ *
24
+ * WHAT THIS MODULE NO LONGER KNOWS (2026-09-18, WORK.md §The open-source
25
+ * launch item 12): how either medium mounts. It dispatched them in one
26
+ * expression — `candidate.kind === 'canvas' ? mountCanvasLayer : mountReactLayer`
27
+ * — against a `LayerMountResult` contract whose own docblock said the
28
+ * failure/teardown/play lifecycle was "the SAME for both media", and it
29
+ * hard-coded each medium's staleness rule besides (canvas remounted on a
30
+ * storage write, dom re-projected on a story publish). The contract is the
31
+ * seam; `@vgai/dom` and `@vgai/canvas` register against it now, and the play
32
+ * handoff below tears every layer down and restores its Boundary node the
33
+ * instant play starts exactly as it always did — which is why the LIFECYCLE
34
+ * stays here rather than moving with either mount.
35
+ */
36
+
37
+ import type { AuthoringAdapter, PickProvider } from '@volter/editor-project/adapter';
38
+ import { stackOrder } from '@volter/editor-project/adapter/root-stacking';
39
+ import { editorConsole } from '../editor-console';
40
+ import type { EditorShellStore } from '../editor-shell-store';
41
+ import { getCurrentProject } from '../project-manager';
42
+ import { subscribeToolContributions } from '../tool-loader';
43
+ import { BoundaryAuthoringAdapter, type BoundaryRootInfo } from './boundary-authoring-adapter';
44
+ import type { CompositeAuthoringAdapter } from './composite-authoring-adapter';
45
+ import {
46
+ type DesignTimeMount,
47
+ type DesignTimeMountContext,
48
+ designTimeMountFor,
49
+ designTimeMounts,
50
+ subscribeDesignTimeMounts,
51
+ } from './design-time-mount-registry';
52
+ import { queueEditModeRebuild } from './edit-mode-authoring';
53
+ import {
54
+ addMountFailureReport,
55
+ clearMountFailureReport,
56
+ formatMountFailureMessage,
57
+ } from './mount-failure-report';
58
+ import {
59
+ getRootCanvasViewport,
60
+ resetRootCanvasViewport,
61
+ setRootCanvasViewport,
62
+ subscribeRootCanvasViewport,
63
+ } from './world-canvas-viewport-state';
64
+ import {
65
+ getRootPan,
66
+ panTransformValue,
67
+ type RootViewController,
68
+ resetRootPan,
69
+ subscribeRootPan,
70
+ } from './world-pan-state';
71
+ import { isRootHidden, isRootInteractive } from './world-session-state';
72
+
73
+ /** D20: Play tears the design-time adapter down, so the selected CSF state
74
+ * must live one level above that adapter to survive Stop's rebuild. Keyed by
75
+ * project + root so equal root ids in different projects never bleed.
76
+ *
77
+ * IT IS STILL HERE, and it is the one lane word this module has left: the
78
+ * board's mount lives in `@vgai/dom` now, but `components/world-documents.tsx`
79
+ * reads the remembered frame for the UI board document's `presentation()`
80
+ * round-trip, and the host may not import a package. Closing it is the
81
+ * world-documents unit's, which is where the rest of that file's `dom`/
82
+ * `canvas` vocabulary lives too. */
83
+ const selectedPortableStoryByRoot = new Map<string, string>();
84
+
85
+ function portableStorySelectionKey(projectRoot: string, rootId: string): string {
86
+ return `${projectRoot}\0${rootId}`;
87
+ }
88
+
89
+ /** The story a board ROOT last applied (undefined before any selection) —
90
+ * the view-address door reads this so a presented workspace document can
91
+ * say which frame it is on (`EditorViewDocument`'s workspace `story`). */
92
+ export function rememberedPortableStory(projectRoot: string, rootId: string): string | undefined {
93
+ return selectedPortableStoryByRoot.get(portableStorySelectionKey(projectRoot, rootId));
94
+ }
95
+
96
+ /** The write half, for the registered mount that owns the board: it applies
97
+ * the story, so it is what knows which frame the root is on. */
98
+ export function rememberPortableStory(projectRoot: string, rootId: string, storyId: string): void {
99
+ selectedPortableStoryByRoot.set(portableStorySelectionKey(projectRoot, rootId), storyId);
100
+ }
101
+
102
+ /** Bottom-to-top layer z-index base — strictly below the selection overlay's
103
+ * z50 and `ToolStrip`+`ViewportOverlay`'s z1000, so canvas-local editing
104
+ * chrome is never occluded by a design-time layer. */
105
+ const BASE_Z_INDEX = 1;
106
+
107
+ /** One candidate world for a design-time layer — read entirely off its
108
+ * existing `BoundaryAuthoringAdapter` (kind/path/zOrder/pausable are already
109
+ * exposed there, independent of whether the composite has a writable manifest
110
+ * manifest surface — see that adapter's own inspector). */
111
+ export interface DesignTimeRootDescriptor {
112
+ readonly worldId: string;
113
+ readonly kind: 'dom' | 'canvas';
114
+ readonly path: string | undefined;
115
+ readonly zOrder: number;
116
+ readonly pausable: boolean;
117
+ /**
118
+ * Mount the declared `path` entry component ALONE, skipping the project
119
+ * story board a dom layer otherwise becomes whenever the project has dom
120
+ * stories. The pasteboard document's contract (queue item 4): the FILE is
121
+ * the canvas and its default export the one subject — a gallery of every
122
+ * project story on that surface would be a different document.
123
+ */
124
+ readonly entryOnly?: boolean;
125
+ }
126
+
127
+ function readCandidates(composite: CompositeAuthoringAdapter): DesignTimeRootDescriptor[] {
128
+ const out: DesignTimeRootDescriptor[] = [];
129
+ for (const { worldId, adapter } of composite.childAdapters()) {
130
+ if (!(adapter instanceof BoundaryAuthoringAdapter)) continue; // the focused (live) world
131
+ const kind = adapter.inspector.get(worldId, 'kind') as string;
132
+ if (kind !== 'dom' && kind !== 'canvas') continue;
133
+ const reason = adapter.inspector.get(worldId, 'reason');
134
+ if (reason !== undefined) continue; // already an error node — nothing new to attempt
135
+ // An `{ ingest }` root belongs to the ingest routes (`ingest/mount-ingest-root.ts`'s
136
+ // `tryManifestIngestRoute*`), which run the game's OWN code and capture
137
+ // what it renders. A design-time layer mounts a first-party document
138
+ // instead, so pointing it at an ingested game is a category error. A
139
+ // `{ module }` canvas root is the same case: its truth is the game's own
140
+ // imperative source, which this lane does not mount.
141
+ const declaredAdapter = adapter.inspector.get(worldId, 'adapter') as string | undefined;
142
+ if (declaredAdapter?.startsWith('ingest') || declaredAdapter?.startsWith('module')) continue;
143
+ out.push({
144
+ worldId,
145
+ kind,
146
+ path: adapter.inspector.get(worldId, 'path') as string | undefined,
147
+ zOrder: Number(adapter.inspector.get(worldId, 'zOrder') ?? 0),
148
+ pausable: adapter.inspector.get(worldId, 'pausable') !== false,
149
+ });
150
+ }
151
+ return out;
152
+ }
153
+
154
+ /** Snapshot the non-Three roots that can own an edit-time document. Capture
155
+ * this before any root mounts: a successful React mount upgrades its
156
+ * Boundary adapter in-place, while the document still needs the manifest
157
+ * path and stacking metadata that Boundary disclosed. */
158
+ export function designTimeRootDescriptors(
159
+ composite: CompositeAuthoringAdapter,
160
+ ): readonly DesignTimeRootDescriptor[] {
161
+ return readCandidates(composite);
162
+ }
163
+
164
+ /**
165
+ * The id the project's UI board mounts under when the project has NO dom root
166
+ * to own it — a project whose UI components live inside an ingested game, say,
167
+ * whose stories are ordinary discovered CSF but whose manifest declares no
168
+ * `dom` root at all.
169
+ *
170
+ * Board presence keys on STORIES, not roots (owner ruling, 2026-08-15), so
171
+ * this case is not exotic: it is simply the board without a root underneath
172
+ * it. The layer mount needs SOME id — it keys pan state, the remembered story
173
+ * selection and the story overlay — and this is that id.
174
+ *
175
+ * No colon, deliberately. `ReactRootAuthoringAdapter` spells `worldId` into a
176
+ * history `displayName` that `history/resource-registry.ts` validates as a
177
+ * project-relative PATH, and a leading `foo:` reads as a URI scheme and throws
178
+ * out of the constructor (measured on the ingest dom surface, whose
179
+ * `<world>:dom-ui` ids hit exactly that).
180
+ */
181
+ export const PROJECT_STORY_BOARD_ROOT_ID = 'ui-components-board';
182
+
183
+ /** Whether a descriptor names an existing child of the composite — the
184
+ * question `replaceChild` answers with a loud warn, asked BEFORE calling it so
185
+ * a rootless board (which by construction has no child) never trips it. */
186
+ function isCompositeChild(composite: CompositeAuthoringAdapter, worldId: string): boolean {
187
+ return composite.childAdapters().some((child) => child.worldId === worldId);
188
+ }
189
+
190
+ /** The rootless UI board's descriptor — no `path`, because there is no root
191
+ * entry to derive a default story from; the board opens on its first story
192
+ * the same way it does for a root whose entry names no story. */
193
+ export function projectStoryBoardDescriptor(): DesignTimeRootDescriptor {
194
+ return {
195
+ worldId: PROJECT_STORY_BOARD_ROOT_ID,
196
+ kind: 'dom',
197
+ path: undefined,
198
+ zOrder: 0,
199
+ pausable: true,
200
+ };
201
+ }
202
+
203
+ /** Re-applies session-local layer CSS (eye + interactive) — cheap, called on
204
+ * every store notification, never triggers a remount. */
205
+ function applySessionStyle(
206
+ layer: HTMLElement,
207
+ worldId: string,
208
+ kind: DesignTimeRootDescriptor['kind'] = 'dom',
209
+ ): void {
210
+ const display = isRootHidden(worldId) ? 'none' : 'block';
211
+ layer.style.display = display;
212
+ // A mount whose content IS the document's subject declares itself always
213
+ // interactive (a click on it is how you select in it). The `interactive`
214
+ // toggle exists for the other case, where design-time hover/press testing
215
+ // of real UI is opt-in because it competes with authoring gestures. Which
216
+ // medium is which is the mount's statement, not this module's.
217
+ layer.style.pointerEvents =
218
+ designTimeMountFor(kind)?.alwaysInteractive || isRootInteractive(worldId) ? 'auto' : 'none';
219
+ // React story labels live in editor chrome above the full-cover selection
220
+ // overlay so they remain clickable. Mirror the owning world's eye state
221
+ // onto that synchronized sibling.
222
+ for (const child of Array.from(layer.parentElement?.children ?? [])) {
223
+ if (child instanceof HTMLElement && child.dataset['vgaiStoryBoardChromeFor'] === worldId) {
224
+ child.style.display = display;
225
+ }
226
+ }
227
+ }
228
+
229
+ function createLayerElement(
230
+ container: HTMLElement,
231
+ candidate: DesignTimeRootDescriptor,
232
+ zIndex: number,
233
+ canvasScene: boolean,
234
+ ): HTMLElement {
235
+ const { worldId } = candidate;
236
+ const layer = document.createElement('div');
237
+ layer.dataset['worldId'] = worldId;
238
+ layer.dataset['vgaiRootSurface'] = 'true';
239
+ if (canvasScene) layer.dataset['vgaiCanvasScene'] = 'true';
240
+ layer.style.position = 'absolute';
241
+ layer.style.top = '0';
242
+ layer.style.left = '0';
243
+ layer.style.width = '100%';
244
+ layer.style.height = '100%';
245
+ layer.style.zIndex = String(zIndex);
246
+ // Same containment as the runtime's per-world surfaces (create-runtime.ts,
247
+ // world-surface cssText): make this layer the containing block for
248
+ // `position:fixed` descendants and clip to the world's rectangle, so a
249
+ // game's full-screen UI (`fixed; inset:0` — the natural idiom) renders
250
+ // inside the viewport instead of floating over the editor chrome. Also
251
+ // makes behavior pan-invariant: without it, space-pan's transform (below)
252
+ // suddenly captured `fixed` children that were page-anchored un-panned.
253
+ layer.style.contain = 'layout paint';
254
+ applySessionStyle(layer, worldId, candidate.kind);
255
+ // D4 (spec27 §8 "space-pan" row) — seed this layer with whatever pan is
256
+ // currently in effect (normally none — `mountDesignTimeLayers` resets pan
257
+ // on every fresh install, see its own doc comment — but re-applying here
258
+ // too keeps this function correct standalone). See `world-pan-state.ts`'s
259
+ // doc comment for the full lockstep-translate soundness argument: THIS
260
+ // element is exactly `this.root`/`hostRect()` in
261
+ // `react-world-authoring-adapter.ts`, i.e. the host `RectProvider.rect(id)`
262
+ // subtracts — panning it is what keeps `rect(id)` pan-invariant.
263
+ applyPanTransform(layer);
264
+ container.appendChild(layer);
265
+ return layer;
266
+ }
267
+
268
+ /** Re-applies the current shared world-pan transform to one layer element —
269
+ * called at creation and by `mountDesignTimeLayers`' pan subscription on
270
+ * every subsequent pan change (see `world-pan-state.ts`). */
271
+ function applyPanTransform(layer: HTMLElement): void {
272
+ // A native Canvas Scene renders through its own editor-camera matrix inside
273
+ // a viewport-sized Pixi surface. CSS-transforming that surface would turn
274
+ // it back into the fixed artboard the Scene document is replacing.
275
+ if (layer.dataset['vgaiCanvasScene'] === 'true') {
276
+ layer.style.transform = '';
277
+ layer.style.width = '100%';
278
+ layer.style.height = '100%';
279
+ return;
280
+ }
281
+ layer.style.transform = panTransformValue(getRootPan()) ?? '';
282
+ layer.style.transformOrigin = '0 0';
283
+ // A React story board owns a canvas larger than the visible document and
284
+ // sizes the layer to its complete grid. Responsive viewport changes resize
285
+ // each frame through `react-story-board.ts`; do not collapse that board back
286
+ // to the single-runtime-surface dimensions used by React/Pixi roots.
287
+ if (layer.dataset['vgaiReactStoryBoard'] === 'true') return;
288
+ const viewport = getRootCanvasViewport();
289
+ layer.style.width = viewport.width === null ? '100%' : `${viewport.width}px`;
290
+ layer.style.height = viewport.height === null ? '100%' : `${viewport.height}px`;
291
+ }
292
+
293
+ /**
294
+ * THE CONTRACT every design-time mount answers, whatever its medium — the
295
+ * one this module's own docblock already called "the SAME for both media",
296
+ * exported since the mounts moved into their surface packages
297
+ * (`design-time-mount-registry.ts`).
298
+ */
299
+ export interface LayerMountResult {
300
+ readonly adapter?: AuthoringAdapter;
301
+ /**
302
+ * D12 (B4) — a stage hit-test over opaque mounted content, in client pixels.
303
+ * Source-backed React/Pixi mounts carry picking on their full adapter;
304
+ * foreign Pixi mounts can return this narrower capability instead.
305
+ */
306
+ readonly pick?: PickProvider['pick'];
307
+ dispose(): void;
308
+ }
309
+
310
+ /**
311
+ * Mount design-time React worlds declared by `composite` into a caller's
312
+ * authoring `container`. `descriptors` narrows the mount to one world for the
313
+ * per-world document model; omission preserves the original multi-layer
314
+ * primitive for bounded callers and integration tests. Returns a disposer
315
+ * that must run before the owning composite is released.
316
+ *
317
+ * A no-op (returns a no-op disposer) when there are no layer candidates or no
318
+ * resolvable current project.
319
+ *
320
+ * Play-mode handoff: the editor's Scene and Game tabs are BOTH always
321
+ * mounted (only one is `display`-visible at a time — the Game tab hosts
322
+ * `play-mode.ts`'s OWN, entirely separate composite/mount over a REAL,
323
+ * ticking `Game`). The instant play starts, this module tears its
324
+ * design-time layers down for the rest of THIS edit session: two
325
+ * independent DOM trees for the SAME react world (this inert stand-in,
326
+ * play-mode's live one) would otherwise carry the SAME `data-testid`s
327
+ * (whatever the entry's own JSX declares, e.g. tri-world's `hud-count`)
328
+ * simultaneously in one document — not just visually confusing, but a
329
+ * genuine test/automation hazard (two elements answering to one testid).
330
+ * Stop symmetrically rebuilds edit-mode authoring and restores the selected
331
+ * portable CSF preview. Play and edit roots never coexist, but neither does
332
+ * Play permanently destroy the authoring surface.
333
+ */
334
+ export function mountDesignTimeLayers(
335
+ container: HTMLElement,
336
+ composite: CompositeAuthoringAdapter,
337
+ store: EditorShellStore,
338
+ descriptors?: readonly DesignTimeRootDescriptor[],
339
+ /**
340
+ * Reports the adapter a descriptor's layer minted, or `null` when its layer
341
+ * goes away. Only a descriptor that names an EXISTING composite child is
342
+ * published INTO the composite (`replaceChild` is an in-place swap; it
343
+ * cannot grow the array, by design) — so a rootless UI board or the Dev
344
+ * component board has no child to upgrade, and this is how its document
345
+ * reaches its adapter for Hierarchy/Inspector routing.
346
+ */
347
+ onAdapter?: (worldId: string, adapter: AuthoringAdapter | null) => void,
348
+ presentation?: {
349
+ /** Native Canvas Scene: independent editor camera and viewport renderer,
350
+ * not the DOM/story-board artboard presentation. */
351
+ readonly canvasSceneView: RootViewController;
352
+ },
353
+ /** When set, first-mount work writes named segments onto this document's
354
+ * activation clock (`viewport-activation-timings`). */
355
+ activationDocumentId?: string,
356
+ ): () => void {
357
+ // D4 (spec27 §8 "space-pan" row) — a fresh layer stack starts un-panned:
358
+ // every call here is a "world switch" (initial edit-mode install, or an
359
+ // acknowledged edit-mode re-install — see `world-root-stage.ts`'s
360
+ // `installAll`), so a stale pan offset from a PREVIOUS layer stack must
361
+ // never carry over onto a different one.
362
+ if (presentation) presentation.canvasSceneView.reset();
363
+ else resetRootPan();
364
+ resetRootCanvasViewport();
365
+ // A declared project resolution seeds DOM component preview frames from the
366
+ // same logical screen Play/standalone use. It deliberately does NOT size a
367
+ // native Canvas Scene: that document is an unbounded world view and receives
368
+ // the separate presentation branch above. A responsive preset may still
369
+ // override DOM previews live through ReactCanvasControls.
370
+ const activeProject = getCurrentProject();
371
+ if (!presentation && activeProject?.config.resolution) {
372
+ setRootCanvasViewport(
373
+ activeProject.config.resolution.width,
374
+ activeProject.config.resolution.height,
375
+ );
376
+ }
377
+ // ONE-WAY PLAY HANDOFF, ENTRY HALF. The exit half — the `store.subscribe`
378
+ // listener at the bottom of this function, which flips `playTeardownDone`
379
+ // and calls `suspendForPlay()` when play STARTS — covers "layers were
380
+ // already mounted, then the user hit Play". But it is a TRANSITION
381
+ // listener, so it does nothing at all when play is ALREADY running by the
382
+ // time this function is first called.
383
+ //
384
+ // That ordering is reachable. `the world root's stage`'s `installAll()` (the only
385
+ // caller) runs at the tail of an async chain that fetches
386
+ // `vgai.project.json` on the way here, so a Play issued the moment the
387
+ // editor shell is interactive wins the race on a slow or contended host and
388
+ // `store.playState` is already `'playing'` on arrival. Without this guard we
389
+ // mount a design-time react layer anyway — rendering the project's REAL
390
+ // entry component against `createDesignTimeGame()`'s INERT `Game`, whose
391
+ // `roots` array is `[]` forever and whose loop never ticks (see that
392
+ // function's doc comment). A HUD listing worlds therefore renders an empty
393
+ // list from a Game that never registers one — and, because no further
394
+ // playState TRANSITION ever arrives, `suspendForPlay()` never fires and the
395
+ // layer is never torn down. It also carries the duplicate-`data-testid`
396
+ // hazard this module's own doc comment calls out for the transition case.
397
+ //
398
+ // Play mode owns the react world outright while it runs (it mounts its own
399
+ // live tree over a REAL, ticking Game). So the correct behavior when play
400
+ // is already up is the same one `suspendForPlay()` produces: mount NOTHING.
401
+ // Stop requests a clean edit-mode rebuild below; this entry guard therefore
402
+ // needs to listen for Stop even though it mounted no layers itself.
403
+ if (store.playState !== 'stopped') {
404
+ let disposed = false;
405
+ let rebuildRequested = false;
406
+ const unsubscribe = store.subscribe(() => {
407
+ if (disposed || rebuildRequested || store.playState !== 'stopped') return;
408
+ rebuildRequested = true;
409
+ if (!disposed) queueEditModeRebuild();
410
+ });
411
+ return () => {
412
+ disposed = true;
413
+ unsubscribe();
414
+ };
415
+ }
416
+ const projectRoot = activeProject?.rootPath;
417
+ const candidates = descriptors ? [...descriptors] : readCandidates(composite);
418
+ // C3 — the scene-UI child (at most one, the Three world's): already a
419
+ // LIVE `UIAuthoringAdapter` at composite-install time (`edit-mode-
420
+ // authoring.ts` — no Boundary-to-live upgrade dance needed here, unlike
421
+ // react/pixi), so it's a mount candidate in its own right rather than one
422
+ // of `readCandidates`'s Boundary-only entries.
423
+ if (candidates.length === 0 || !projectRoot) {
424
+ return () => {};
425
+ }
426
+ // Narrowed once: `mountCandidate` below is a function declaration, where
427
+ // the guard's control-flow narrowing does not reach.
428
+ const projectRootPath: string = projectRoot;
429
+
430
+ // Bottom-to-top paint order — mirrors `create-runtime.ts`'s own stacking
431
+ // pass, over exactly the roots this module lays out (react/pixi
432
+ // candidates only; the Three world keeps its untouched canvas
433
+ // slot below all of these — see the module doc comment).
434
+ const sorted = stackOrder(candidates.map((c) => ({ id: c.worldId, zOrder: c.zOrder })));
435
+ const orderedCandidates = sorted.map((s) => candidates.find((c) => c.worldId === s.id)!);
436
+
437
+ const candidateByRoot = new Map(orderedCandidates.map((c) => [c.worldId, c]));
438
+ const layers = new Map<string, HTMLElement>();
439
+ const disposersByWorld = new Map<string, () => void>();
440
+ /** Per-world mount generation — a remount supersedes any in-flight mount. */
441
+ const mountEpochs = new Map<string, number>();
442
+ // Worlds whose Boundary child we upgraded to a LIVE design-time adapter
443
+ // (successful react mounts). Tracked so the one-way play handoff can put an
444
+ // HONEST disclosure node back in the composite instead of leaving a live
445
+ // adapter over torn-down DOM (which renders silently empty — a #18 violation).
446
+ const upgraded = new Set<string>();
447
+ /** Worlds whose medium has no registered mount YET — see `mountCandidate`. */
448
+ const awaitingMount = new Set<string>();
449
+ /** Worlds already told, in their Boundary node, that no package registered
450
+ * a mount for their medium. Said once per stack, never twice. */
451
+ const refusedMount = new Set<string>();
452
+ let torndown = false; // the whole-module disposer (the world root's stage unmount) ran
453
+ let playTeardownDone = false; // the one-way play-mode handoff (see doc comment) ran
454
+ let rebuildRequested = false;
455
+
456
+ function disposeEverything(): void {
457
+ for (const dispose of disposersByWorld.values()) {
458
+ try {
459
+ dispose();
460
+ } catch (err) {
461
+ editorConsole.error(`[design-time-layers] dispose failed: ${err}`, 'authoring');
462
+ }
463
+ }
464
+ disposersByWorld.clear();
465
+ for (const layer of layers.values()) layer.remove();
466
+ layers.clear();
467
+ // A reported adapter now points at torn-down DOM. Retract it in the same
468
+ // gesture that freed it — the composite half of this (`suspendForPlay`'s
469
+ // Boundary swap) exists for exactly the same reason.
470
+ for (const candidate of orderedCandidates) onAdapter?.(candidate.worldId, null);
471
+ }
472
+
473
+ /**
474
+ * Play handoff: dispose the design-time layers AND revert every
475
+ * upgraded world back to a `BoundaryAuthoringAdapter` disclosure node
476
+ * (#18 — never silently empty). Not called on the whole-module disposer
477
+ * (`torndown`, the world root's stage unmount), where the composite is going away
478
+ * anyway. Stop dispatches one edit-mode rebuild after the live runtime has
479
+ * torn down, restoring the CSF-backed design surface. The scene-UI layer's DOM is
480
+ * torn down the same way (play mode renders scene-UI through the real
481
+ * engine's HUD mount instead — two live DOM trees for the same testids
482
+ * would be the same hazard `design-time-layers.ts`'s own doc comment
483
+ * already calls out for react roots); its adapter is left alone (still
484
+ * live, just without a mounted layer) since there is no Boundary form of it.
485
+ */
486
+ function suspendForPlay(): void {
487
+ disposeEverything();
488
+ for (const worldId of upgraded) {
489
+ const c = candidateByRoot.get(worldId);
490
+ if (!c) continue;
491
+ const info: BoundaryRootInfo = {
492
+ id: c.worldId,
493
+ kind: c.kind,
494
+ entryOrScenePath: c.path,
495
+ zOrder: c.zOrder,
496
+ pausable: c.pausable,
497
+ };
498
+ composite.replaceChild(
499
+ worldId,
500
+ new BoundaryAuthoringAdapter(
501
+ store,
502
+ info,
503
+ 'Design-time layer suspended by play mode — Stop restores it.',
504
+ ),
505
+ );
506
+ }
507
+ upgraded.clear();
508
+ store.notifyIngestEdit();
509
+ }
510
+
511
+ function mountCandidate(
512
+ candidate: DesignTimeRootDescriptor,
513
+ i: number,
514
+ /** Runs once this mount has settled (mounted or failed) — the hook a
515
+ * re-projection uses to take the PREVIOUS layer down only then. */
516
+ onSettled?: () => void,
517
+ ): void {
518
+ const registered = designTimeMountFor(candidate.kind);
519
+ // NO REGISTERED MOUNT IS NOT A FAILURE, and specifically not YET a
520
+ // failure: contributions load asynchronously (the inventory pass waits
521
+ // for the first viewport frame), so a layer stack installed at project
522
+ // open routinely runs before the package that owns this medium has
523
+ // registered. Wait, silently, with no layer and no console line — the
524
+ // world's own Boundary node is already standing in the composite, which
525
+ // is the disclosure. `awaitingMount` is what the registration
526
+ // subscription below mounts once the package arrives, and what the
527
+ // contribution pass turns into a NAMED refusal if it never does.
528
+ if (!registered) {
529
+ awaitingMount.add(candidate.worldId);
530
+ return;
531
+ }
532
+ awaitingMount.delete(candidate.worldId);
533
+ const epoch = (mountEpochs.get(candidate.worldId) ?? 0) + 1;
534
+ mountEpochs.set(candidate.worldId, epoch);
535
+ const layer = createLayerElement(container, candidate, BASE_Z_INDEX + i, !!presentation);
536
+ layers.set(candidate.worldId, layer);
537
+
538
+ const context: DesignTimeMountContext = {
539
+ projectRootPath,
540
+ store,
541
+ ...(presentation ? { view: presentation.canvasSceneView } : {}),
542
+ ...(activationDocumentId ? { activationDocumentId } : {}),
543
+ };
544
+
545
+ // Wrapped so a mount that throws SYNCHRONOUSLY (an implementation that
546
+ // is not itself `async`) still lands in the boundary path below rather
547
+ // than escaping this call.
548
+ const mountPromise: Promise<LayerMountResult> = (async () =>
549
+ registered.mount(candidate, layer, context))();
550
+
551
+ mountPromise
552
+ .then((result) => {
553
+ if (torndown || playTeardownDone || mountEpochs.get(candidate.worldId) !== epoch) {
554
+ result.dispose();
555
+ return;
556
+ }
557
+ if (result.adapter) {
558
+ // A descriptor with no composite child of its own (rootless UI or
559
+ // the Dev component board) is reported below and nowhere else:
560
+ // `replaceChild` would only warn and ignore, which is the right
561
+ // refusal for a real mis-routing and pure noise for a board that
562
+ // never had a child. For a real child, replace FIRST: publishing the
563
+ // mounted adapter into React before the composite owns its ids gives
564
+ // one render where selection belongs to neither surface. Besides a
565
+ // false unowned-id refusal, logging that refusal during render makes
566
+ // the console tray update while RootSelectionOverlay is rendering.
567
+ if (isCompositeChild(composite, candidate.worldId)) {
568
+ composite.replaceChild(candidate.worldId, result.adapter);
569
+ upgraded.add(candidate.worldId);
570
+ }
571
+ onAdapter?.(candidate.worldId, result.adapter);
572
+ } else if (result.pick) {
573
+ // Opaque/foreign Pixi roots can expose a stage pick without a
574
+ // source-authoring adapter. Re-wrap their Boundary with the pick and
575
+ // track it in `upgraded`: `suspendForPlay` must remove a pick that
576
+ // closes over a disposed Pixi stage.
577
+ const info: BoundaryRootInfo = {
578
+ id: candidate.worldId,
579
+ kind: candidate.kind,
580
+ entryOrScenePath: candidate.path,
581
+ zOrder: candidate.zOrder,
582
+ pausable: candidate.pausable,
583
+ };
584
+ composite.replaceChild(
585
+ candidate.worldId,
586
+ new BoundaryAuthoringAdapter(store, info, undefined, result.pick),
587
+ );
588
+ upgraded.add(candidate.worldId);
589
+ }
590
+ disposersByWorld.set(candidate.worldId, result.dispose);
591
+ // PD-1: this root is mounted — retract any failure recorded for it by
592
+ // an earlier attempt (per-world, never the whole list: a sibling root
593
+ // that is still down keeps its own report).
594
+ clearMountFailureReport(candidate.worldId);
595
+ store.notifyIngestEdit();
596
+ onSettled?.();
597
+ })
598
+ .catch((err: unknown) => {
599
+ if (mountEpochs.get(candidate.worldId) !== epoch) return;
600
+ onSettled?.();
601
+ layer.remove();
602
+ if (layers.get(candidate.worldId) === layer) layers.delete(candidate.worldId);
603
+ if (torndown || playTeardownDone) return;
604
+ const message = formatMountFailureMessage(err);
605
+ editorConsole.error(
606
+ `[design-time-layers] world "${candidate.worldId}" (${candidate.kind}) failed to ` +
607
+ `mount: ${message}`,
608
+ 'authoring',
609
+ );
610
+ const info: BoundaryRootInfo = {
611
+ id: candidate.worldId,
612
+ kind: candidate.kind,
613
+ entryOrScenePath: candidate.path,
614
+ zOrder: candidate.zOrder,
615
+ pausable: candidate.pausable,
616
+ };
617
+ onAdapter?.(candidate.worldId, null);
618
+ if (isCompositeChild(composite, candidate.worldId)) {
619
+ composite.replaceChild(
620
+ candidate.worldId,
621
+ new BoundaryAuthoringAdapter(store, info, message),
622
+ );
623
+ }
624
+ addMountFailureReport({
625
+ worldId: candidate.worldId,
626
+ kind: candidate.kind,
627
+ identity: registered.identity,
628
+ message,
629
+ });
630
+ store.notifyIngestEdit();
631
+ });
632
+ }
633
+ orderedCandidates.forEach((candidate, i) => {
634
+ mountCandidate(candidate, i);
635
+ });
636
+
637
+ /**
638
+ * Dispose one world's mount and mount it again from CURRENT source —
639
+ * the canvas lane's absorb-by-remount unit (storage watch below).
640
+ */
641
+ /**
642
+ * A remount that keeps the OLD layer on screen until the new one has
643
+ * settled. `remountCandidate` tears the layer down first, so every
644
+ * re-projection was a visible flash — "it flickers like a reload, then the
645
+ * change is applied" (runhuman pass 144). The new layer is created on top;
646
+ * when its mount settles the previous one is disposed and removed.
647
+ */
648
+ function reprojectCandidate(candidate: DesignTimeRootDescriptor, i: number): void {
649
+ const prevDispose = disposersByWorld.get(candidate.worldId);
650
+ disposersByWorld.delete(candidate.worldId);
651
+ const prevLayer = layers.get(candidate.worldId);
652
+ layers.delete(candidate.worldId);
653
+ mountCandidate(candidate, i, () => {
654
+ // The new layer mounted hidden — its frames fill in over a second and
655
+ // an empty board on top of the old one IS the flash (runhuman pass
656
+ // 146 still saw it with the old layer kept). Show it only now.
657
+ const next = layers.get(candidate.worldId);
658
+ if (next) next.style.visibility = '';
659
+ if (prevDispose) {
660
+ try {
661
+ prevDispose();
662
+ } catch (err) {
663
+ editorConsole.error(
664
+ `[design-time-layers] re-projection dispose failed: ${err}`,
665
+ 'authoring',
666
+ );
667
+ }
668
+ }
669
+ prevLayer?.remove();
670
+ });
671
+ const next = layers.get(candidate.worldId);
672
+ if (next && next !== prevLayer) next.style.visibility = 'hidden';
673
+ }
674
+
675
+ function remountCandidate(candidate: DesignTimeRootDescriptor, i: number): void {
676
+ const prev = disposersByWorld.get(candidate.worldId);
677
+ disposersByWorld.delete(candidate.worldId);
678
+ if (prev) {
679
+ try {
680
+ prev();
681
+ } catch (err) {
682
+ editorConsole.error(`[design-time-layers] remount dispose failed: ${err}`, 'authoring');
683
+ }
684
+ }
685
+ const oldLayer = layers.get(candidate.worldId);
686
+ if (oldLayer) {
687
+ oldLayer.remove();
688
+ layers.delete(candidate.worldId);
689
+ }
690
+ mountCandidate(candidate, i);
691
+ }
692
+
693
+ // WHEN A MOUNTED LAYER GOES STALE IS THE MOUNT'S STATEMENT, NOT THIS
694
+ // MODULE'S. Two rules used to be hard-coded here, each bought with a
695
+ // measured screen. A canvas layer REMOUNTS when a write lands with no HMR
696
+ // channel to re-execute it: without that a structural write landed in
697
+ // source while the live board kept showing the old world until F5 (runhuman
698
+ // pass 61: "when I duplicated, nothing happened... when you refresh, now it
699
+ // shows"). A dom board RE-PROJECTS when its story registry publishes: same absence of
700
+ // HMR, and a dragged crosshair stayed where the live preview left it while
701
+ // an UNDO changed nothing on screen until F5 — "Ctrl+Z just blinked" for
702
+ // every Windows tester and, measured on the instrument, for macOS too
703
+ // (runhuman passes 135–142; screen read 2026-09-03). Both are facts about a
704
+ // MEDIUM, so both travel with the mount now.
705
+ //
706
+ // WHAT STAYS HERE is the pair of ACTIONS and the one question this module
707
+ // can answer — whether a stale layer may be replaced right now (not while
708
+ // play is running, not after teardown). REMOUNT tears the old layer down
709
+ // first; RE-PROJECT mounts the new one on top and disposes the previous
710
+ // only once it has settled, because an empty board over the old one IS the
711
+ // flash (runhuman pass 146). A mount asks for the one its medium affords.
712
+ // The three world is neither: it runs its own design session with its own
713
+ // absorb protocol (`r3f-design-session.ts`).
714
+ const invalidationStops = new Map<DesignTimeMount, () => void>();
715
+ function bindInvalidations(): void {
716
+ for (const [mount, stop] of invalidationStops) {
717
+ if (designTimeMountFor(mount.kind) === mount) continue;
718
+ // Replaced (an HMR re-evaluation) or removed: its hooks are stale.
719
+ stop();
720
+ invalidationStops.delete(mount);
721
+ }
722
+ for (const mount of designTimeMounts()) {
723
+ if (invalidationStops.has(mount)) continue;
724
+ if (designTimeMountFor(mount.kind) !== mount) continue;
725
+ if (!orderedCandidates.some((c) => c.kind === mount.kind)) continue;
726
+ const stops: Array<() => void> = [];
727
+ const invalidate =
728
+ (apply: (candidate: DesignTimeRootDescriptor, index: number) => void) => (): void => {
729
+ if (torndown || playTeardownDone || store.playState !== 'stopped') return;
730
+ orderedCandidates.forEach((candidate, index) => {
731
+ if (candidate.kind === mount.kind) apply(candidate, index);
732
+ });
733
+ };
734
+ if (mount.remountWhen) stops.push(mount.remountWhen(invalidate(remountCandidate)));
735
+ if (mount.reprojectWhen) stops.push(mount.reprojectWhen(invalidate(reprojectCandidate)));
736
+ invalidationStops.set(mount, () => {
737
+ for (const stop of stops) stop();
738
+ });
739
+ }
740
+ }
741
+ bindInvalidations();
742
+
743
+ // A PACKAGE THAT ARRIVES LATE STILL GETS ITS LAYER. The contribution pass
744
+ // that loads the surface packages is deferred behind the first viewport
745
+ // frame (`project-tool-discovery.ts`), so on a cold open this stack is
746
+ // routinely installed before any of them has registered. Every candidate it
747
+ // could not mount is waiting in `awaitingMount`; a registration is the
748
+ // moment to mount it, and the moment to bind that medium's staleness hooks.
749
+ const unsubMounts = subscribeDesignTimeMounts(() => {
750
+ if (torndown || playTeardownDone || store.playState !== 'stopped') return;
751
+ orderedCandidates.forEach((candidate, index) => {
752
+ if (!awaitingMount.has(candidate.worldId)) return;
753
+ if (!designTimeMountFor(candidate.kind)) return;
754
+ refusedMount.delete(candidate.worldId);
755
+ clearMountFailureReport(candidate.worldId);
756
+ mountCandidate(candidate, index);
757
+ });
758
+ bindInvalidations();
759
+ });
760
+
761
+ // ...AND A MEDIUM NO PACKAGE OWNS SAYS SO. Once a contribution pass has
762
+ // installed, "nothing registered" has stopped being "not yet" and become
763
+ // the answer — the honest emptiness a build whose package list carries no
764
+ // mount for this medium gives. It is a Boundary disclosure, deliberately
765
+ // NOT an `editorConsole.error` and not a mount-failure report: a build
766
+ // shipping no such package is a configuration fact, not a failure, and a
767
+ // later registration above still upgrades it.
768
+ const unsubContributions = subscribeToolContributions(() => {
769
+ if (torndown || playTeardownDone) return;
770
+ let refused = false;
771
+ for (const candidate of orderedCandidates) {
772
+ if (!awaitingMount.has(candidate.worldId)) continue;
773
+ if (refusedMount.has(candidate.worldId)) continue;
774
+ if (designTimeMountFor(candidate.kind)) continue;
775
+ refusedMount.add(candidate.worldId);
776
+ if (!isCompositeChild(composite, candidate.worldId)) continue;
777
+ const info: BoundaryRootInfo = {
778
+ id: candidate.worldId,
779
+ kind: candidate.kind,
780
+ entryOrScenePath: candidate.path,
781
+ zOrder: candidate.zOrder,
782
+ pausable: candidate.pausable,
783
+ };
784
+ composite.replaceChild(
785
+ candidate.worldId,
786
+ new BoundaryAuthoringAdapter(
787
+ store,
788
+ info,
789
+ `No package in this editor registered a design-time mount for a "${candidate.kind}" ` +
790
+ 'world, so there is nothing here to author it with. A build ships the packages its ' +
791
+ 'own entry in `builds/` lists.',
792
+ ),
793
+ );
794
+ refused = true;
795
+ }
796
+ if (refused) store.notifyIngestEdit();
797
+ });
798
+
799
+ // D4 (spec27 §8 "space-pan" row) — the lockstep half of the pan feature:
800
+ // re-apply the current shared transform to every currently-mounted layer
801
+ // the instant it changes (`RootSelectionOverlay`'s own outer wrapper binds
802
+ // the SAME `subscribeRootPan`/`panTransformValue` pair independently — see
803
+ // `world-pan-state.ts`'s doc comment for why applying the identical value
804
+ // at both mount points is what keeps `rect(id)` pan-invariant). A cheap
805
+ // style write, never a remount.
806
+ const unsubPan = subscribeRootPan(() => {
807
+ for (const layer of layers.values()) applyPanTransform(layer);
808
+ });
809
+ const unsubViewport = subscribeRootCanvasViewport(() => {
810
+ for (const layer of layers.values()) applyPanTransform(layer);
811
+ });
812
+
813
+ const unsubStore = store.subscribe(() => {
814
+ if (!playTeardownDone && store.playState !== 'stopped') {
815
+ playTeardownDone = true;
816
+ suspendForPlay();
817
+ return;
818
+ }
819
+ if (playTeardownDone && !rebuildRequested && store.playState === 'stopped' && !torndown) {
820
+ rebuildRequested = true;
821
+ // Reuse the world root's stage's one authoritative reinstall path. The event
822
+ // causes this mount's disposer to run before the replacement composite
823
+ // and layers are installed, so no React root or adapter survives across
824
+ // the Play -> Stop boundary.
825
+ if (!torndown) queueEditModeRebuild();
826
+ return;
827
+ }
828
+ // Session toggles (eye/interactive) change via `store.notifyIngestEdit()`
829
+ // (`GameHierarchy.tsx`'s row affordances) — this is a cheap style
830
+ // re-apply, never a remount.
831
+ if (!playTeardownDone) {
832
+ for (const [worldId, layer] of layers) {
833
+ applySessionStyle(layer, worldId, candidateByRoot.get(worldId)?.kind ?? 'dom');
834
+ }
835
+ }
836
+ });
837
+
838
+ return () => {
839
+ torndown = true;
840
+ for (const stop of invalidationStops.values()) stop();
841
+ invalidationStops.clear();
842
+ unsubMounts();
843
+ unsubContributions();
844
+ unsubStore();
845
+ unsubPan();
846
+ unsubViewport();
847
+ disposeEverything();
848
+ };
849
+ }