@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,260 @@
1
+ /**
2
+ * THE `story` ADDRESS — one registered opener for every gesture that opens a
3
+ * portable story (`@editor/document-open-registry`).
4
+ *
5
+ * The host used to reach four different ways: `ProjectLayout` branched on
6
+ * `requested.kind === 'story'` and pre-loaded the module itself,
7
+ * `scene-documents` resolved a composed id against the registry,
8
+ * `GameHierarchy` called `openStoryDocumentByStoryId`, and
9
+ * `editor-view-presentation` polled the registry, derived the title and chose
10
+ * the medium. All four were the host knowing what CSF is. They are now ONE
11
+ * address, `story`, and the host asks for it exactly as it asks for
12
+ * `generation` (WORK.md §Stories leave the host, edges 4–7).
13
+ *
14
+ * TWO WAYS TO NAME A STORY, because the gestures genuinely differ: a module
15
+ * path plus an export name (a layout address, a view address, a package that
16
+ * just WROTE the module), or the composed Storybook id (a hierarchy row's
17
+ * node id, a scene-table plan). Both resolve here; neither is the host's
18
+ * problem.
19
+ *
20
+ * WHICH SURFACE it lands on is still the declared medium
21
+ * (`story-declared-medium.ts`) delegated to `story:three` / `story:isolated`,
22
+ * and `mode: 'docs'` to `story:docs`. Those three are `@vgai/game`'s
23
+ * documents: a build without that package has no opener for them and this one
24
+ * answers `null`, which is the same honest answer an undeclared medium gives.
25
+ *
26
+ * SETTLING. `open` is synchronous and a story address routinely arrives before
27
+ * discovery has published the module — on a layout restore, and immediately
28
+ * after a package writes a story module. `document-open-registry.ts` already
29
+ * has the shape for that: try once, let the kind SETTLE, try again. What each
30
+ * former call site did by hand is what `settle` does here, and which one it
31
+ * does depends on what the failed attempt NAMED — a module path settles by
32
+ * loading exactly that module (`ProjectLayout`'s `ensureProjectStoryModule`,
33
+ * whose own loop re-runs when a concurrent discovery pass supersedes it, and
34
+ * which is also what a package that just WROTE a story module needs), an
35
+ * id-only address by waiting for discovery to PUBLISH
36
+ * (`whenProjectStoriesReady`, never a refresh of its own — a later pass
37
+ * supersedes that and it returns over an emptied registry, measured in unit
38
+ * 4). `tierSourceWriteBackend()` comes first on both, because a restored
39
+ * address can arrive before the source backend the discovery scan reads
40
+ * exists — that is the await `ProjectLayout` held.
41
+ */
42
+
43
+ import { object3DDocumentSession } from '../authoring/object3d-document-session-registry';
44
+ import { openRegisteredDocument, registerDocumentOpener } from '../document-open-registry';
45
+ import { editorConsole } from '../editor-console';
46
+ import { getCurrentProject } from '../project-manager';
47
+ import { tierSourceWriteBackend } from '../ui-source/tier-source-write-backend';
48
+ import { DOCUMENT_REGISTRATION_TIMEOUT_MS, waitUntil } from '../wait-until';
49
+ import type { WorkspaceStateStore } from '../workspace-document-restore';
50
+ import { getProjectStoryRegions } from './project-story-regions';
51
+ import { declaredStoryMedium, reportUndeclaredStoryMedium } from './story-declared-medium';
52
+ import {
53
+ ISOLATED_STORY_DOCUMENT_OPENER,
54
+ STORY_DOCS_DOCUMENT_OPENER,
55
+ THREE_STORY_DOCUMENT_OPENER,
56
+ } from './story-document-openers';
57
+ import { deriveStoryGroupPath } from './story-grouping';
58
+ import {
59
+ ensureProjectStoryModule,
60
+ getProjectStoryModules,
61
+ projectStoriesReady,
62
+ whenProjectStoriesReady,
63
+ } from './story-registry';
64
+
65
+ /** The address a story opens under. */
66
+ export const STORY_DOCUMENT_OPENER = 'story';
67
+
68
+ export interface StoryOpenRequest {
69
+ /** Project-relative CSF module path. With {@link StoryOpenRequest.storyName}
70
+ * this is the whole address. */
71
+ readonly modulePath?: string;
72
+ /** The story's authored export name. */
73
+ readonly storyName?: string;
74
+ /** The composed Storybook id — the other way to name the same story (a
75
+ * hierarchy story row's node id IS this id). */
76
+ readonly storyId?: string;
77
+ /** Tab title; derived from the story's own group path when absent. */
78
+ readonly title?: string;
79
+ /** Whether opening also FOCUSES the tab. A click-through opens and focuses;
80
+ * the Edit tab row offers a standing tab instead and passes `false`. */
81
+ readonly activate?: boolean;
82
+ /** `docs` opens the Storybook Docs companion instead of the preview. */
83
+ readonly mode?: 'preview' | 'docs';
84
+ }
85
+
86
+ /**
87
+ * What a failed attempt NAMED, waiting for `settle` — which takes no request,
88
+ * so this is where the pair meets. Sets rather than one slot each, because
89
+ * several addresses can be in flight at once (a restored layout opening three
90
+ * story tabs) and settling one must not drop another's.
91
+ */
92
+ const unsettledModules = new Set<string>();
93
+ const unsettledIds = new Set<string>();
94
+
95
+ interface ResolvedStory {
96
+ readonly modulePath: string;
97
+ readonly storyName: string;
98
+ readonly title: string;
99
+ }
100
+
101
+ /** The title a story gives itself: its group path's leaf, and its own label
102
+ * when that says something the leaf does not. */
103
+ function derivedTitle(story: {
104
+ readonly modulePath: string;
105
+ readonly title?: string | undefined;
106
+ readonly label: string;
107
+ }): string {
108
+ const subject = deriveStoryGroupPath({ modulePath: story.modulePath, title: story.title }).leaf;
109
+ return subject === story.label ? subject : `${subject} · ${story.label}`;
110
+ }
111
+
112
+ /** Resolve an address against the live registry, or `null` when nothing
113
+ * composed answers to it yet. */
114
+ function resolve(request: StoryOpenRequest): ResolvedStory | null {
115
+ for (const module_ of getProjectStoryModules()) {
116
+ if (!module_.ok) continue;
117
+ for (const story of module_.stories) {
118
+ const matches =
119
+ request.storyId === undefined
120
+ ? module_.modulePath === request.modulePath && story.name === request.storyName
121
+ : story.id === request.storyId;
122
+ if (!matches) continue;
123
+ return {
124
+ modulePath: module_.modulePath,
125
+ storyName: story.name,
126
+ title:
127
+ request.title ??
128
+ derivedTitle({ modulePath: module_.modulePath, title: story.title, label: story.label }),
129
+ };
130
+ }
131
+ }
132
+ return null;
133
+ }
134
+
135
+ function openResolved(
136
+ store: WorkspaceStateStore,
137
+ request: StoryOpenRequest,
138
+ story: ResolvedStory,
139
+ ): string | null {
140
+ const base = {
141
+ modulePath: story.modulePath,
142
+ storyName: story.storyName,
143
+ title: story.title,
144
+ ...(request.activate === undefined ? {} : { activate: request.activate }),
145
+ };
146
+ if (request.mode === 'docs') {
147
+ return openRegisteredDocument(STORY_DOCS_DOCUMENT_OPENER, store, base);
148
+ }
149
+ const declared = declaredStoryMedium({
150
+ modulePath: story.modulePath,
151
+ regions: getProjectStoryRegions(),
152
+ });
153
+ if (declared.medium === undefined) {
154
+ // An undeclared story is a NAMED gap, never a candidate for a fallback
155
+ // renderer — and never a reason to settle and try again, so it reports
156
+ // here and the address answers "opened nothing" rather than `null`'s
157
+ // "ask me again".
158
+ reportUndeclaredStoryMedium(story.modulePath, declared.reason);
159
+ return null;
160
+ }
161
+ return openRegisteredDocument(
162
+ declared.medium === 'three' ? THREE_STORY_DOCUMENT_OPENER : ISOLATED_STORY_DOCUMENT_OPENER,
163
+ store,
164
+ base,
165
+ );
166
+ }
167
+
168
+ /** An id no composed story carries. The caller's own refusal can only say
169
+ * "nothing opened it": the two ways an address fails — an unknown id, and a
170
+ * module with no declared medium — are both this package's to tell apart. */
171
+ function reportUnresolvedStoryId(storyId: string): void {
172
+ editorConsole.warn(
173
+ `No composed portable story carries the id ${JSON.stringify(storyId)}, so nothing opened it.`,
174
+ 'editor',
175
+ );
176
+ }
177
+
178
+ export function registerStoryOpener(): () => void {
179
+ return registerDocumentOpener<StoryOpenRequest>({
180
+ id: STORY_DOCUMENT_OPENER,
181
+ owner: './story-opener',
182
+ open: (store, request) => {
183
+ const story = resolve(request);
184
+ if (story) return openResolved(store, request, story);
185
+ if (request.modulePath) unsettledModules.add(request.modulePath);
186
+ else if (request.storyId !== undefined) {
187
+ unsettledIds.add(request.storyId);
188
+ // Only once discovery has PUBLISHED is an unknown id a real answer;
189
+ // before that an empty registry is boot in flight, and a synchronous
190
+ // caller (the Edit tab row, the `open` verb) is the one that needs the
191
+ // name — an async caller settles and asks again first.
192
+ if (projectStoriesReady()) reportUnresolvedStoryId(request.storyId);
193
+ }
194
+ return null;
195
+ },
196
+ settle: async () => {
197
+ await tierSourceWriteBackend();
198
+ const modules = [...unsettledModules];
199
+ const ids = [...unsettledIds];
200
+ unsettledModules.clear();
201
+ unsettledIds.clear();
202
+ // A MODULE-named address settles by loading exactly that module, whose
203
+ // own loop re-runs when a concurrent discovery pass supersedes it. This
204
+ // is also what a package that just WROTE a story needs: the module is
205
+ // new, so nothing published it and the targeted load is the refresh.
206
+ await Promise.all(
207
+ modules.map((modulePath) => ensureProjectStoryModule(getCurrentProject(), modulePath)),
208
+ );
209
+ if (ids.length === 0) return;
210
+ // An ID-only address settles by waiting for discovery to PUBLISH — never
211
+ // by awaiting a refresh of its own, which a later pass supersedes and
212
+ // which then returns over an emptied registry (measured, unit 4).
213
+ await whenProjectStoriesReady();
214
+ for (const storyId of ids) {
215
+ if (!resolve({ storyId })) reportUnresolvedStoryId(storyId);
216
+ }
217
+ },
218
+ /**
219
+ * THIS ADDRESS'S OWN READINESS: a story the presenter just opened is not
220
+ * ready to be DRIVEN until the document it opened into has mounted its
221
+ * stage. The presenter used to hold this rule by spelling the three-story
222
+ * document's id prefix (`editor-view-presentation.ts`'s
223
+ * `documentId.startsWith('three-story:')`); it is this package's, because
224
+ * this package is what chose the medium (`openResolved` above) and
225
+ * therefore what knows a `three` story opens onto a turntable stage while
226
+ * an `isolated` one and a `docs` one do not.
227
+ *
228
+ * The MEDIUM is the declaration here, not the stage's own announcement:
229
+ * the story document constructs its viewport source asynchronously
230
+ * (`@vgai/game`'s `three-story-documents.tsx`), so at this instant nothing
231
+ * has rendered yet and an announcement could only read absent. A `three`
232
+ * medium ALWAYS mounts one, which is what makes the wait unconditional and
233
+ * the refusal honest.
234
+ *
235
+ * The three DIRECT callers of `story:three`
236
+ * (`three-board/ThreeBoardDocument.tsx:189`,
237
+ * `../src/component-states-source.ts`, and `openResolved` above) go
238
+ * through the SYNCHRONOUS door, which by the registry's own contract gets
239
+ * the id and waits on the kind's doors itself.
240
+ */
241
+ ready: async (documentId, request) => {
242
+ if (request.mode === 'docs') return;
243
+ const story = resolve(request);
244
+ if (!story) return;
245
+ const declared = declaredStoryMedium({
246
+ modulePath: story.modulePath,
247
+ regions: getProjectStoryRegions(),
248
+ });
249
+ if (declared.medium !== 'three') return;
250
+ if (
251
+ await waitUntil(
252
+ () => object3DDocumentSession(documentId) !== null,
253
+ DOCUMENT_REGISTRATION_TIMEOUT_MS,
254
+ )
255
+ )
256
+ return;
257
+ throw new Error(`The requested Object3D view did not mount for document: ${documentId}`);
258
+ },
259
+ });
260
+ }
@@ -0,0 +1,408 @@
1
+ /**
2
+ * Off-screen CANVAS/Pixi story preview — mount a composed CSF story that
3
+ * produces Pixi content and hand back the `PIXI.Container` it rendered into,
4
+ * plus the `Application` that owns its clock.
5
+ *
6
+ * The canvas-surface sibling of `story-three-preview.ts`, and it exists for the
7
+ * same reason: portable CSF is a prefab's explicit "here is how this renders",
8
+ * and a surface that wants to SHOW a prefab (the canvas board, and the
9
+ * qualification both boards key membership on) needs that render as an object,
10
+ * not as a picture.
11
+ *
12
+ * ## Why the mount goes through react-dom, not through a Pixi root
13
+ *
14
+ * A 2D prefab story mounts its OWN `@pixi/react` `<Application>` (see any 2D prefab's colocated
15
+ * story under an example project's `prefabs` directory): unlike the three lane, where the
16
+ * board supplies the scene and the story renders bare JSX into it, nothing
17
+ * supplies a canvas-surface stage, so the story carries one and renders
18
+ * anywhere react-dom renders. That is the story's own declaration and this
19
+ * module does not second-guess it — it mounts the story exactly as written, in
20
+ * a detached DOM host, and then ADOPTS what came out.
21
+ *
22
+ * ## What comes out, and what the caller may do with it
23
+ *
24
+ * `host` is the detached element the story rendered into and `stage` is the
25
+ * Application's own root container — the REAL objects, with the story's real
26
+ * textures on them. A caller that wants to DISPLAY the story reparents the
27
+ * host into its own DOM (the `2D` board does exactly this, one host per
28
+ * frame); nothing here rasterizes, copies, or re-creates anything, which is
29
+ * what keeps the composed story render the sole visual authority (the
30
+ * anti-shim rule) and what makes a `scaleMode: 'nearest'` sheet magnify
31
+ * nearest wherever it ends up — the filtering belongs to the texture's own
32
+ * source, and adopting the real render carries it along.
33
+ *
34
+ * The mount arrives STOPPED: `Application.init` defaults to `autoStart: true`,
35
+ * so the story's own `requestAnimationFrame` loop would run on wall time in a
36
+ * detached host nobody is looking at. {@link MountedStoryPixi.ticker} is that
37
+ * clock, handed to the caller so a surface that wants the cycle to PLAY drives
38
+ * it from its own frame instead — one clock per surface, none on wall time.
39
+ *
40
+ * ## Mounts are SERIAL, and one bad story never takes the rest down
41
+ *
42
+ * Same physics as the three lane, same queue (`story-mount-turn.ts`): a story's
43
+ * loaders touch process-global state (Pixi's `Assets` cache and the
44
+ * `@pixi/react` component catalogue are both module-global), so overlapping two
45
+ * mounts interleaves their setup. A story that crashes while rendering is
46
+ * caught by {@link CrashNullBoundary} and rejects in milliseconds with its own
47
+ * error rather than burning the ceiling below — the exact measurement the three
48
+ * lane records, and the reason a board over a project of non-canvas stories
49
+ * finishes at all.
50
+ */
51
+
52
+ import type { Application, Container } from 'pixi.js';
53
+ import { createElement } from 'react';
54
+ import type { Root } from 'react-dom/client';
55
+ import { withApplicationCollector } from '../canvas-preview-frames';
56
+ import { CrashNullBoundary } from '../crash-null-boundary';
57
+ import { mountedStoryHasPixiContent } from './pixi-story-model';
58
+ import { resolveStoryDomRuntime } from './story-dom-runtime';
59
+ import { runInStoryMountTurn } from './story-mount-turn';
60
+ import type { StoryPreviewComponent } from './story-three-preview';
61
+
62
+ /**
63
+ * How long the mount waits for the story's `Application` to finish initializing
64
+ * AND for its stage to hold content. Both are legitimately slow: `init()`
65
+ * creates a real renderer, and a prefab story commonly gates its children on
66
+ * its atlas (`SpritesReady`), so the content arrives when the sheet does.
67
+ * Matches the three lane's own first-commit ceiling.
68
+ */
69
+ const PIXI_MOUNT_TIMEOUT_MS = 10_000;
70
+
71
+ /**
72
+ * Event-loop turns the mount waits for the story's FIRST DOM commit, and for a
73
+ * `<canvas>` to appear in it. This is the cheap NO: a story that renders DOM
74
+ * and no canvas is not a canvas story, and it must answer in a few turns rather
75
+ * than at the ceiling above — otherwise every board build over a UI-only
76
+ * project would pay ten seconds per story.
77
+ */
78
+ const COMMIT_TURNS = 12;
79
+
80
+ /** Where the detached host sits: far off-screen, contained, inert. `contain:
81
+ * strict` makes the host the containing block for `position: fixed`
82
+ * descendants, so a story that pins itself cannot flash across the editor —
83
+ * the same containment `story-capture.ts` documents for its own host. */
84
+ const HOST_STYLE =
85
+ 'position:fixed;left:-20000px;top:0;width:512px;height:512px;' +
86
+ 'overflow:hidden;contain:strict;pointer-events:none;visibility:hidden;';
87
+
88
+ /** One mounted canvas story. */
89
+ export interface MountedStoryPixi {
90
+ /**
91
+ * The detached element the story rendered into, with its `<canvas>` inside.
92
+ *
93
+ * A DOM surface that wants to SHOW this story reparents THIS element — never
94
+ * the canvas alone. React owns the host's children: moving a react-rendered
95
+ * node out from under its own root leaves `root.unmount()` removing a child
96
+ * of a parent it no longer has, which throws. The host itself is ours, so it
97
+ * travels freely and {@link dispose} still removes it from wherever it ended
98
+ * up. Restyle it on adoption; {@link HOST_STYLE} only parks it off-screen.
99
+ */
100
+ readonly host: HTMLElement;
101
+ /** The Application's own root container — the story's real display tree.
102
+ * Reparent it; never copy it. */
103
+ readonly stage: Container;
104
+ /** The story's clock, already STOPPED. A surface that wants the story's
105
+ * cycle to play calls `ticker.update(elapsedMs)` from its own frame. */
106
+ readonly ticker: Application['ticker'];
107
+ /**
108
+ * The Application the story built — its own renderer included.
109
+ *
110
+ * A displaying surface keeps that renderer (it is what draws the adopted
111
+ * host's canvas) and therefore keeps its WebGL context. That is the real
112
+ * ceiling on how many stories one surface may show at once: a browser allows
113
+ * on the order of sixteen contexts per page and drops the OLDEST when that
114
+ * runs out. `app.screen` is the mounting canvas the story's own
115
+ * `<Application>` declared; presentation surfaces may crop it to stage
116
+ * content unless the story explicitly requests a fullscreen layout.
117
+ */
118
+ readonly app: Application;
119
+ /** Tear the isolated react root down and destroy the story's display tree and
120
+ * clock. Runs the story's own effect cleanups. Idempotent. */
121
+ dispose(): void;
122
+ }
123
+
124
+ /** A mount function valid inside an already-acquired story turn — the canvas
125
+ * sibling of `story-three-preview.ts`'s `StoryMountInTurn`. */
126
+ export type PixiStoryMountInTurn = (
127
+ Component: StoryPreviewComponent,
128
+ props?: Record<string, unknown>,
129
+ ) => Promise<MountedStoryPixi>;
130
+
131
+ /** Run one compound canvas-story operation (a whole board build) with no other
132
+ * story mount, of any medium, interleaving it. */
133
+ export function withPixiStoryMountTurn<T>(
134
+ task: (mount: PixiStoryMountInTurn) => Promise<T>,
135
+ ): Promise<T> {
136
+ return runInStoryMountTurn(() => task(mountStoryPixiInTurn));
137
+ }
138
+
139
+ /** Mount one canvas story off-screen, taking a turn of its own. */
140
+ export function mountStoryPixi(
141
+ Component: StoryPreviewComponent,
142
+ props: Record<string, unknown> = {},
143
+ ): Promise<MountedStoryPixi> {
144
+ return withPixiStoryMountTurn((mount) => mount(Component, props));
145
+ }
146
+
147
+ /** Dispose a standalone mount in the same shared turn domain. Compound callers
148
+ * already holding a turn dispose their own mounts inside it. */
149
+ export function disposeStoryPixi(mounted: MountedStoryPixi): Promise<void> {
150
+ return runInStoryMountTurn(async () => mounted.dispose());
151
+ }
152
+
153
+ export interface PixiStoryThumbnailOptions {
154
+ readonly width?: number;
155
+ readonly height?: number;
156
+ readonly props?: Record<string, unknown>;
157
+ /** Storybook's standard layout policy. Only `fullscreen` makes the
158
+ * mounting Application's declared canvas part of the preview. */
159
+ readonly layout?: 'centered' | 'padded' | 'fullscreen';
160
+ }
161
+
162
+ /**
163
+ * Photograph a canvas prefab from its portable CSF story.
164
+ *
165
+ * This is the Pixi sibling of `captureStoryComponentThumbnail`: the story is
166
+ * mounted through its own real `<Application>`, its paused stage is rendered
167
+ * once without advancing the ticker, and Pixi's extraction API reads the
168
+ * result back. The requested rectangle is presentation only; it never becomes
169
+ * a second authored size or a component-specific thumbnail asset.
170
+ */
171
+ export async function capturePixiStoryThumbnail(
172
+ Component: StoryPreviewComponent,
173
+ options: PixiStoryThumbnailOptions = {},
174
+ ): Promise<string> {
175
+ const mounted = await mountStoryPixi(Component, options.props ?? {});
176
+ try {
177
+ const width = options.width ?? 128;
178
+ const height = options.height ?? 96;
179
+ mounted.app.renderer.render({ container: mounted.stage });
180
+
181
+ const bounds = mounted.stage.getBounds();
182
+ const fullscreen = options.layout === 'fullscreen';
183
+ const padding = 8;
184
+ // Both frames are the STORY's own `Rectangle` — `screen.clone()` is the
185
+ // app's class, whatever graph the story mounted in. A story's
186
+ // `<Application>` comes from its own module graph (the project's under the
187
+ // packaged runtime), and this rectangle is handed straight back to that
188
+ // app's extractor; constructing the shell's class here was the one value
189
+ // in this path that crossed.
190
+ const frame = mounted.app.screen.clone();
191
+ if (!fullscreen) {
192
+ frame.x = bounds.x - padding;
193
+ frame.y = bounds.y - padding;
194
+ frame.width = Math.max(1, bounds.width + padding * 2);
195
+ frame.height = Math.max(1, bounds.height + padding * 2);
196
+ }
197
+ // Match the target aspect by adding transparent breathing room. Never
198
+ // stretch a sprite to make a rectangular thumbnail.
199
+ const targetAspect = width / height;
200
+ if (!fullscreen && frame.width / frame.height < targetAspect) {
201
+ const nextWidth = frame.height * targetAspect;
202
+ frame.x -= (nextWidth - frame.width) / 2;
203
+ frame.width = nextWidth;
204
+ } else if (!fullscreen && frame.width / frame.height > targetAspect) {
205
+ const nextHeight = frame.width / targetAspect;
206
+ frame.y -= (nextHeight - frame.height) / 2;
207
+ frame.height = nextHeight;
208
+ }
209
+ const background = mounted.app.renderer.background;
210
+ const source = (await mounted.app.renderer.extract.canvas({
211
+ target: mounted.stage,
212
+ frame,
213
+ ...(fullscreen && background.alpha > 0 ? { clearColor: background.colorRgba } : {}),
214
+ })) as HTMLCanvasElement;
215
+ const output = document.createElement('canvas');
216
+ output.width = width;
217
+ output.height = height;
218
+ const context = output.getContext('2d');
219
+ if (!context) throw new Error('Canvas story preview could not create a 2D readback context.');
220
+
221
+ const scale = Math.min(width / source.width, height / source.height);
222
+ const drawWidth = source.width * scale;
223
+ const drawHeight = source.height * scale;
224
+ context.drawImage(
225
+ source,
226
+ (width - drawWidth) / 2,
227
+ (height - drawHeight) / 2,
228
+ drawWidth,
229
+ drawHeight,
230
+ );
231
+ return output.toDataURL('image/png');
232
+ } finally {
233
+ await disposeStoryPixi(mounted);
234
+ }
235
+ }
236
+
237
+ const nextTurn = (): Promise<void> => new Promise((resolve) => setTimeout(resolve, 0));
238
+
239
+ /** The mount body for callers that already own the story turn. */
240
+ async function mountStoryPixiInTurn(
241
+ StoryComponent: StoryPreviewComponent,
242
+ props: Record<string, unknown> = {},
243
+ ): Promise<MountedStoryPixi> {
244
+ // Loaders first (Storybook 9's portable-story contract): a story's `loaded`
245
+ // data is empty until `.load()` has run, and a canvas prefab's loader is
246
+ // where it fetches its atlas.
247
+ if (typeof StoryComponent.load === 'function') await StoryComponent.load();
248
+
249
+ // In the packaged editor the CSF component and @pixi/react both belong to
250
+ // the project's Vite graph. Mount them with that graph's react-dom too;
251
+ // using the prebuilt editor bundle's createRoot crosses React dispatchers.
252
+ const storyRuntime = await resolveStoryDomRuntime();
253
+
254
+ const host = document.createElement('div');
255
+ host.setAttribute('style', HOST_STYLE);
256
+ document.body.appendChild(host);
257
+
258
+ let root: Root | null = null;
259
+ const teardown = (): void => {
260
+ root?.unmount();
261
+ root = null;
262
+ host.remove();
263
+ };
264
+
265
+ try {
266
+ return await withApplicationCollector(async (created) => {
267
+ let renderError: unknown = null;
268
+ root = storyRuntime.createRoot(host);
269
+ root.render(
270
+ createElement(
271
+ CrashNullBoundary,
272
+ {
273
+ onCaught: (error: unknown) => {
274
+ renderError ??= error;
275
+ },
276
+ },
277
+ createElement(StoryComponent, props),
278
+ ),
279
+ );
280
+
281
+ // The cheap NO: a committed story with no canvas in it is not a canvas
282
+ // story, and says so in a few turns.
283
+ for (let turn = 0; turn < COMMIT_TURNS; turn++) {
284
+ if (renderError) break;
285
+ if (host.querySelector('canvas')) break;
286
+ await nextTurn();
287
+ }
288
+ if (renderError) throw describeMountFailure(renderError);
289
+ if (!host.querySelector('canvas')) {
290
+ throw new Error('The story rendered no canvas — it does not mount Pixi content.');
291
+ }
292
+
293
+ // A canvas is present, so an `Application.init()` is in flight (or has
294
+ // already landed). From here the ceiling is the real one: init creates a
295
+ // renderer, and the story's own content may be gated on its atlas.
296
+ const deadline = Date.now() + PIXI_MOUNT_TIMEOUT_MS;
297
+ let app: Application | undefined;
298
+ while (Date.now() < deadline) {
299
+ if (renderError) throw describeMountFailure(renderError);
300
+ app ??= created[0];
301
+ if (app && mountedStoryHasPixiContent(app.stage)) break;
302
+ await nextTurn();
303
+ }
304
+ if (!app) {
305
+ throw new Error(
306
+ 'The story rendered a canvas but never initialized a Pixi Application within ' +
307
+ `${PIXI_MOUNT_TIMEOUT_MS / 1000}s — it is not a canvas story, or its renderer ` +
308
+ 'could not be created here.',
309
+ );
310
+ }
311
+
312
+ // Content time does not run on wall time at design time. The story's own
313
+ // rAF loop stops here and the clock is handed to whoever presents it.
314
+ app.stop();
315
+ app.ticker.lastTime = 0;
316
+
317
+ let disposed = false;
318
+ return {
319
+ host,
320
+ stage: app.stage,
321
+ ticker: app.ticker,
322
+ app,
323
+ dispose(): void {
324
+ if (disposed) return;
325
+ disposed = true;
326
+ // React, and react ONLY. Unmounting the story's `<Application>` is
327
+ // what tears the Application down: `@pixi/react`'s own unmount path
328
+ // (`helpers/unmountRoot`) renders `null` into the stage — running
329
+ // every `useTick` cleanup in order — and only then destroys the app
330
+ // and forgets its canvas. Destroying anything here as well is a
331
+ // double free of a ticker its cleanups are still using.
332
+ teardown();
333
+ },
334
+ } satisfies MountedStoryPixi;
335
+ });
336
+ } catch (error) {
337
+ teardown();
338
+ throw error;
339
+ }
340
+ }
341
+
342
+ // ------------------------------------------------- the capture lane's frame
343
+
344
+ /** How long {@link awaitPixiStoryFrame} waits. Same ceiling, same reasons, as
345
+ * the mount above: a real renderer is created and a prefab's content commonly
346
+ * waits on its atlas. */
347
+ const FIRST_FRAME_TIMEOUT_MS = PIXI_MOUNT_TIMEOUT_MS;
348
+
349
+ /**
350
+ * Wait until every collected `Application` has PRESENTED a frame with content
351
+ * in it — the readiness test a capture of a canvas story owes itself.
352
+ *
353
+ * MEASURED DEFECT (this is why the function exists): `vgai screenshot
354
+ * <file>.stories.tsx` photographed a `@pixi/react` story blank. The capture
355
+ * lane's own readiness test is "the container has committed child nodes", and
356
+ * `<Application>` commits its `<canvas>` on the FIRST commit — before
357
+ * `Application.init()` has even created a renderer, and long before an atlas
358
+ * the story gates its children on has resolved. So the sheet was rasterized
359
+ * from an empty canvas and looked like a perfectly plausible blank frame, which
360
+ * is the worst failure a look verb has.
361
+ *
362
+ * Two conditions, because either alone still lies: a renderer that has drawn an
363
+ * empty stage has presented a frame, and a stage with children whose renderer
364
+ * has not run yet has no pixels.
365
+ *
366
+ * Resolves (rather than throwing) at the ceiling: a story that genuinely never
367
+ * draws must still be photographed, and an honestly blank cell is the truth
368
+ * about it.
369
+ */
370
+ export async function awaitPixiStoryFrame(
371
+ apps: readonly Application[],
372
+ now: () => number = Date.now,
373
+ ): Promise<void> {
374
+ if (apps.length === 0) return;
375
+ const presented = new Set<Application>();
376
+ const subscriptions = apps.map((app) => {
377
+ const observer = {
378
+ postrender: () => {
379
+ presented.add(app);
380
+ },
381
+ };
382
+ app.renderer?.runners.postrender.add(observer);
383
+ return { app, observer };
384
+ });
385
+ try {
386
+ const deadline = now() + FIRST_FRAME_TIMEOUT_MS;
387
+ while (now() < deadline) {
388
+ const ready = apps.every(
389
+ (app) =>
390
+ app.stage !== undefined && mountedStoryHasPixiContent(app.stage) && presented.has(app),
391
+ );
392
+ if (ready) return;
393
+ await nextTurn();
394
+ }
395
+ } finally {
396
+ for (const { app, observer } of subscriptions) {
397
+ app.renderer?.runners.postrender.remove(observer);
398
+ }
399
+ }
400
+ }
401
+
402
+ function describeMountFailure(error: unknown): Error {
403
+ return new Error(
404
+ 'mountStoryPixi: the story threw while rendering — it does not mount Pixi content, or its ' +
405
+ `component crashed. Original error: ${error instanceof Error ? error.message : String(error)}`,
406
+ { cause: error },
407
+ );
408
+ }