@volter/sdk 0.0.0-stage → 0.5.204

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 (523) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +20 -0
  3. package/README.md +38 -3
  4. package/package.json +510 -4
  5. package/src/account.ts +210 -0
  6. package/src/chrome.ts +88 -0
  7. package/src/client.ts +1646 -0
  8. package/src/commands.ts +66 -0
  9. package/src/contributions.ts +619 -0
  10. package/src/css-numeric-style.ts +97 -0
  11. package/src/document-probe.ts +282 -0
  12. package/src/editor-view.ts +225 -0
  13. package/src/extension.ts +40 -0
  14. package/src/generations.ts +178 -0
  15. package/src/host.ts +1157 -0
  16. package/src/http-transport.browser.ts +14 -0
  17. package/src/http-transport.node.ts +19 -0
  18. package/src/index.ts +131 -0
  19. package/src/kit/CapabilityCoverageSection.tsx +185 -0
  20. package/src/kit/account-client.ts +333 -0
  21. package/src/kit/action-registry.ts +317 -0
  22. package/src/kit/active-product.ts +76 -0
  23. package/src/kit/active-project.ts +155 -0
  24. package/src/kit/adapter-editor-config.ts +25 -0
  25. package/src/kit/adapter-module.ts +7 -0
  26. package/src/kit/adapter-observation.ts +49 -0
  27. package/src/kit/animation/animation-clock.ts +479 -0
  28. package/src/kit/animation/stage-transport.ts +385 -0
  29. package/src/kit/api/assets.ts +365 -0
  30. package/src/kit/api/project-open.ts +355 -0
  31. package/src/kit/api/project-source.ts +180 -0
  32. package/src/kit/api/project-state.ts +110 -0
  33. package/src/kit/api/relay.ts +270 -0
  34. package/src/kit/api/themes.ts +45 -0
  35. package/src/kit/api-asset-library-wire.ts +45 -0
  36. package/src/kit/api-base.ts +10 -0
  37. package/src/kit/api-build.ts +99 -0
  38. package/src/kit/api-git-wire.ts +56 -0
  39. package/src/kit/api-logs.ts +92 -0
  40. package/src/kit/api-project-identity.ts +74 -0
  41. package/src/kit/api-settings.ts +36 -0
  42. package/src/kit/api-worktrees.ts +205 -0
  43. package/src/kit/asset-capabilities.ts +344 -0
  44. package/src/kit/asset-compare-core.ts +171 -0
  45. package/src/kit/asset-editor-context.tsx +101 -0
  46. package/src/kit/asset-events.ts +96 -0
  47. package/src/kit/asset-inspector-actions.ts +87 -0
  48. package/src/kit/asset-selection-viewer-registry.ts +113 -0
  49. package/src/kit/asset-selection.ts +146 -0
  50. package/src/kit/asset-thumbnails.ts +25 -0
  51. package/src/kit/asset-viewers.ts +115 -0
  52. package/src/kit/asset-workflow/asset-import-jobs.ts +106 -0
  53. package/src/kit/asset-workflow/asset-ledger-backend.ts +126 -0
  54. package/src/kit/asset-workflow/asset-ledger.ts +156 -0
  55. package/src/kit/asset-workflow/asset-materialization-report.ts +140 -0
  56. package/src/kit/asset-workflow/asset-pack-manifest.ts +320 -0
  57. package/src/kit/asset-workflow/asset-types.ts +142 -0
  58. package/src/kit/asset-workflow/audio-preview-player.ts +193 -0
  59. package/src/kit/asset-workflow/audio-waveform.ts +22 -0
  60. package/src/kit/asset-workflow/cloud-asset-client.ts +263 -0
  61. package/src/kit/asset-workflow/hosted-asset-materialization.ts +236 -0
  62. package/src/kit/asset-workflow/image-view-scale.ts +31 -0
  63. package/src/kit/asset-workflow/import-contract.ts +124 -0
  64. package/src/kit/asset-workflow/ledger-write-lock.ts +244 -0
  65. package/src/kit/asset-workflow/pixi-spritesheet.ts +197 -0
  66. package/src/kit/asset-workflow/preview-resource-lifetime.ts +44 -0
  67. package/src/kit/asset-workflow/project-asset-commands.ts +20 -0
  68. package/src/kit/asset-workflow/project-content.ts +288 -0
  69. package/src/kit/asset-workflow/project-source-index.ts +545 -0
  70. package/src/kit/asset-workflow/thumbnail-system.ts +256 -0
  71. package/src/kit/authoring/active-adapter.ts +200 -0
  72. package/src/kit/authoring/active-systems.ts +422 -0
  73. package/src/kit/authoring/adapter-key.ts +18 -0
  74. package/src/kit/authoring/authoring-asset-url.ts +27 -0
  75. package/src/kit/authoring/bootstrap-state.ts +49 -0
  76. package/src/kit/authoring/boundary-authoring-adapter.ts +189 -0
  77. package/src/kit/authoring/canvas-scene-guides.ts +84 -0
  78. package/src/kit/authoring/composite-authoring-adapter.ts +2109 -0
  79. package/src/kit/authoring/consumer-actions.ts +531 -0
  80. package/src/kit/authoring/design-time-layers.ts +852 -0
  81. package/src/kit/authoring/design-time-mount-registry.ts +244 -0
  82. package/src/kit/authoring/edit-mode-authoring.ts +637 -0
  83. package/src/kit/authoring/empty-project-authoring.ts +22 -0
  84. package/src/kit/authoring/instance-source-menu.ts +135 -0
  85. package/src/kit/authoring/layered-pick.ts +185 -0
  86. package/src/kit/authoring/mounted-root-subjects.ts +146 -0
  87. package/src/kit/authoring/no-authoring-adapter.ts +59 -0
  88. package/src/kit/authoring/object3d-document-persistence.ts +122 -0
  89. package/src/kit/authoring/panel-authoring.ts +121 -0
  90. package/src/kit/authoring/project-authoring-session.ts +105 -0
  91. package/src/kit/authoring/provenance.ts +99 -0
  92. package/src/kit/authoring/react-canvas-navigation.ts +259 -0
  93. package/src/kit/authoring/react-design-canvas-style.ts +20 -0
  94. package/src/kit/authoring/react-story-board.ts +917 -0
  95. package/src/kit/authoring/selection-scope.ts +195 -0
  96. package/src/kit/authoring/shell-document-ops.ts +169 -0
  97. package/src/kit/authoring/story-board-chrome-fit.ts +107 -0
  98. package/src/kit/authoring/story-board-presentation.ts +111 -0
  99. package/src/kit/authoring/three-root.ts +67 -0
  100. package/src/kit/authoring/viewport-tool-context.ts +73 -0
  101. package/src/kit/authoring/viewport-tool-owner.ts +38 -0
  102. package/src/kit/authoring/world-session-state.ts +101 -0
  103. package/src/kit/authoring-seam-evidence.ts +300 -0
  104. package/src/kit/availability-tick.ts +66 -0
  105. package/src/kit/bitmap-label.ts +120 -0
  106. package/src/kit/boot-routing.ts +392 -0
  107. package/src/kit/breakpoint-state.ts +43 -0
  108. package/src/kit/build-identity.ts +16 -0
  109. package/src/kit/bytes-codec.ts +62 -0
  110. package/src/kit/cancellation-reason.ts +58 -0
  111. package/src/kit/canvas-frames.ts +88 -0
  112. package/src/kit/capture-camera-pose.ts +77 -0
  113. package/src/kit/capture-size.ts +88 -0
  114. package/src/kit/chrome-registry.ts +159 -0
  115. package/src/kit/chrome-slot-registry.ts +91 -0
  116. package/src/kit/collaboration-client.ts +264 -0
  117. package/src/kit/collaboration-presence.ts +41 -0
  118. package/src/kit/command-dispatch.ts +19 -0
  119. package/src/kit/command-listener.ts +2182 -0
  120. package/src/kit/command-registry.ts +71 -0
  121. package/src/kit/component-board-registry.ts +205 -0
  122. package/src/kit/component-states-registry.ts +199 -0
  123. package/src/kit/components/AlignToolbar.tsx +204 -0
  124. package/src/kit/components/ApplicationMenus.tsx +372 -0
  125. package/src/kit/components/AssetEditorShell.tsx +216 -0
  126. package/src/kit/components/AssetInspectorToolSection.tsx +124 -0
  127. package/src/kit/components/BoardRulers.tsx +354 -0
  128. package/src/kit/components/CanvasAddNodeDialogs.tsx +529 -0
  129. package/src/kit/components/CanvasSceneViewport.tsx +1195 -0
  130. package/src/kit/components/ChromeSlot.tsx +20 -0
  131. package/src/kit/components/CodeView.tsx +470 -0
  132. package/src/kit/components/CompactInspectorShell.tsx +39 -0
  133. package/src/kit/components/ConsolePanel.tsx +273 -0
  134. package/src/kit/components/GameplaySessionTimeline.tsx +295 -0
  135. package/src/kit/components/InspectionProjection.tsx +932 -0
  136. package/src/kit/components/Inspector.tsx +270 -0
  137. package/src/kit/components/InspectorCanvasPreview.tsx +35 -0
  138. package/src/kit/components/InspectorFieldsSection.tsx +290 -0
  139. package/src/kit/components/InspectorStoriesSection.tsx +92 -0
  140. package/src/kit/components/InspectorToolSection.tsx +96 -0
  141. package/src/kit/components/InspectorTransformSection.tsx +245 -0
  142. package/src/kit/components/LightExplorerPanel.tsx +433 -0
  143. package/src/kit/components/MediaProperties.tsx +145 -0
  144. package/src/kit/components/ProjectHeader.tsx +328 -0
  145. package/src/kit/components/ReactCanvasControls.tsx +358 -0
  146. package/src/kit/components/RootSelectionOverlay.tsx +3688 -0
  147. package/src/kit/components/RootTextEditor.tsx +79 -0
  148. package/src/kit/components/SaveStatus.tsx +70 -0
  149. package/src/kit/components/SurfaceCrashBoundary.tsx +105 -0
  150. package/src/kit/components/SurfaceStateOverlay.tsx +24 -0
  151. package/src/kit/components/ToolContributionSurfaces.tsx +49 -0
  152. package/src/kit/components/ToolHost.tsx +380 -0
  153. package/src/kit/components/Toolbar.tsx +811 -0
  154. package/src/kit/components/TransientHint.tsx +44 -0
  155. package/src/kit/components/VersionControlSection.tsx +470 -0
  156. package/src/kit/components/ViewportOverlaysMenu.tsx +177 -0
  157. package/src/kit/components/VolterLogo.tsx +18 -0
  158. package/src/kit/components/WorktreeSwitcher.tsx +712 -0
  159. package/src/kit/components/account-documents.tsx +1162 -0
  160. package/src/kit/components/asset-documents.tsx +794 -0
  161. package/src/kit/components/asset-editor-persistence.ts +216 -0
  162. package/src/kit/components/asset-selection-section.tsx +545 -0
  163. package/src/kit/components/asset-thumbnails.tsx +307 -0
  164. package/src/kit/components/asset-viewers/AudioViewer.tsx +201 -0
  165. package/src/kit/components/asset-viewers/GenericJsonViewer.tsx +102 -0
  166. package/src/kit/components/asset-viewers/ImageViewer.tsx +300 -0
  167. package/src/kit/components/asset-viewers/JsonAssetDocument.tsx +98 -0
  168. package/src/kit/components/asset-viewers/OnlineAssetDetail.tsx +426 -0
  169. package/src/kit/components/asset-viewers/SourceAssetViewer.tsx +356 -0
  170. package/src/kit/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
  171. package/src/kit/components/asset-viewers/VideoViewer.tsx +101 -0
  172. package/src/kit/components/asset-viewers/shader-source.ts +144 -0
  173. package/src/kit/components/board-guides.ts +150 -0
  174. package/src/kit/components/canvas-scene-hotkeys.ts +37 -0
  175. package/src/kit/components/canvas-temporary-pivot.ts +34 -0
  176. package/src/kit/components/core-utilities.tsx +94 -0
  177. package/src/kit/components/inspector-preview-section.tsx +223 -0
  178. package/src/kit/components/inspector-revert-label.ts +20 -0
  179. package/src/kit/components/inspector-selection.ts +42 -0
  180. package/src/kit/components/inspector-stories-gating.ts +171 -0
  181. package/src/kit/components/inspector-transform-subject.ts +11 -0
  182. package/src/kit/components/inspector-transform.ts +88 -0
  183. package/src/kit/components/kind-documents.tsx +544 -0
  184. package/src/kit/components/primitives/DraftColorInput.tsx +74 -0
  185. package/src/kit/components/project-tool-documents.tsx +402 -0
  186. package/src/kit/components/scene-documents.tsx +221 -0
  187. package/src/kit/components/status-contributions.tsx +407 -0
  188. package/src/kit/components/tool-documents.tsx +302 -0
  189. package/src/kit/components/tool-schema-form.tsx +262 -0
  190. package/src/kit/components/use-after-paint.ts +41 -0
  191. package/src/kit/components/use-project-image-assets.ts +86 -0
  192. package/src/kit/components/workspace-history.ts +32 -0
  193. package/src/kit/components/world-documents.tsx +570 -0
  194. package/src/kit/components/world-overlay-gestures.ts +1939 -0
  195. package/src/kit/composite-screenshot.ts +2238 -0
  196. package/src/kit/content-entry-source-registry.ts +184 -0
  197. package/src/kit/contribution-surfaces.ts +48 -0
  198. package/src/kit/coverage/canvas-reveal.ts +192 -0
  199. package/src/kit/coverage/design-time-surfaces.ts +101 -0
  200. package/src/kit/coverage/ontology-invariants.ts +466 -0
  201. package/src/kit/coverage/session-vitals.ts +503 -0
  202. package/src/kit/crash-null-boundary.ts +36 -0
  203. package/src/kit/creation-site-edit.ts +1491 -0
  204. package/src/kit/creation-site-registry.ts +160 -0
  205. package/src/kit/delegate-harness-registry.ts +134 -0
  206. package/src/kit/document-areas.ts +70 -0
  207. package/src/kit/document-context-registry.ts +193 -0
  208. package/src/kit/document-open-registry.ts +200 -0
  209. package/src/kit/document-play-extension.ts +221 -0
  210. package/src/kit/document-preview-source.ts +20 -0
  211. package/src/kit/document-renderer-session.ts +138 -0
  212. package/src/kit/document-stage-sessions.ts +26 -0
  213. package/src/kit/document-viewports.ts +120 -0
  214. package/src/kit/editor-api.ts +46 -0
  215. package/src/kit/editor-chrome-capture.ts +136 -0
  216. package/src/kit/editor-commands.ts +176 -0
  217. package/src/kit/editor-console.ts +580 -0
  218. package/src/kit/editor-current-view.ts +56 -0
  219. package/src/kit/editor-document-probe.ts +1168 -0
  220. package/src/kit/editor-git-client.ts +115 -0
  221. package/src/kit/editor-hotkeys.ts +728 -0
  222. package/src/kit/editor-lease-view.ts +39 -0
  223. package/src/kit/editor-lease.ts +415 -0
  224. package/src/kit/editor-mode.ts +19 -0
  225. package/src/kit/editor-notifications.ts +140 -0
  226. package/src/kit/editor-presence.ts +563 -0
  227. package/src/kit/editor-presentation-activity.ts +58 -0
  228. package/src/kit/editor-presentation-notice.ts +42 -0
  229. package/src/kit/editor-runtime.tsx +147 -0
  230. package/src/kit/editor-server-response.ts +86 -0
  231. package/src/kit/editor-session-attribution.ts +85 -0
  232. package/src/kit/editor-session-mode.ts +54 -0
  233. package/src/kit/editor-state-facets.ts +74 -0
  234. package/src/kit/editor-view-presentation.ts +777 -0
  235. package/src/kit/environment-images.ts +58 -0
  236. package/src/kit/eyedropper-session.ts +60 -0
  237. package/src/kit/files/file-provider.ts +62 -0
  238. package/src/kit/files/project-files.ts +270 -0
  239. package/src/kit/finders/index.ts +137 -0
  240. package/src/kit/finders/scenes-from-entrypoint-selection.ts +387 -0
  241. package/src/kit/frame/frame-parts.ts +30 -0
  242. package/src/kit/framed-document-capture.ts +34 -0
  243. package/src/kit/game-globals-prelude.ts +143 -0
  244. package/src/kit/game-surface-defaults.ts +33 -0
  245. package/src/kit/gameplay-dom-recording.ts +318 -0
  246. package/src/kit/gameplay-export-state.ts +14 -0
  247. package/src/kit/gameplay-replay.ts +417 -0
  248. package/src/kit/gameplay-session-time.ts +9 -0
  249. package/src/kit/gameplay-sessions.ts +204 -0
  250. package/src/kit/hierarchy-component-marks.ts +298 -0
  251. package/src/kit/hierarchy-internals.ts +197 -0
  252. package/src/kit/hierarchy-kind-icon.ts +217 -0
  253. package/src/kit/hierarchy-menu-registry.ts +67 -0
  254. package/src/kit/hierarchy-node-rows.ts +307 -0
  255. package/src/kit/hierarchy-panel-view.ts +280 -0
  256. package/src/kit/hierarchy-projection.ts +76 -0
  257. package/src/kit/hierarchy-row-media.ts +45 -0
  258. package/src/kit/hierarchy-row-model.ts +308 -0
  259. package/src/kit/hierarchy-rows.ts +11 -0
  260. package/src/kit/hierarchy-walk.ts +86 -0
  261. package/src/kit/history/editor-session.ts +25 -0
  262. package/src/kit/history/history-commands.ts +147 -0
  263. package/src/kit/history/history-delegate.ts +187 -0
  264. package/src/kit/history/history-limit-notices.ts +43 -0
  265. package/src/kit/history/history-service.ts +1189 -0
  266. package/src/kit/history/persistence-coordinator.ts +35 -0
  267. package/src/kit/history/project-file-history.ts +386 -0
  268. package/src/kit/history/project-root-history-backends.ts +139 -0
  269. package/src/kit/history/resource-registry.ts +209 -0
  270. package/src/kit/history/snapshot-store.ts +103 -0
  271. package/src/kit/history/source-history-backend.ts +546 -0
  272. package/src/kit/history-types.ts +124 -0
  273. package/src/kit/hmr-registration-group.ts +67 -0
  274. package/src/kit/hmr-stable-react-context.ts +23 -0
  275. package/src/kit/hotkeys.ts +190 -0
  276. package/src/kit/inference-diagnostics.ts +69 -0
  277. package/src/kit/initial-project.ts +80 -0
  278. package/src/kit/inspection/active-subject.ts +571 -0
  279. package/src/kit/inspection/active-surface.ts +142 -0
  280. package/src/kit/inspection/compose-subject.ts +1055 -0
  281. package/src/kit/inspection/compose.ts +7 -0
  282. package/src/kit/inspection/display.ts +171 -0
  283. package/src/kit/inspection/document-subject.ts +109 -0
  284. package/src/kit/inspection/game-subject.ts +85 -0
  285. package/src/kit/inspection/null-subject.ts +119 -0
  286. package/src/kit/inspection/serialize.ts +357 -0
  287. package/src/kit/inspection/use-active-inspection.ts +180 -0
  288. package/src/kit/inspection-model.ts +542 -0
  289. package/src/kit/inspection-node-media.ts +58 -0
  290. package/src/kit/inspector-presentation.ts +203 -0
  291. package/src/kit/inspector-property-grouping.ts +64 -0
  292. package/src/kit/inspector-section-registry.ts +221 -0
  293. package/src/kit/instance-source-actions.ts +163 -0
  294. package/src/kit/js-heap.ts +71 -0
  295. package/src/kit/key-actions.ts +91 -0
  296. package/src/kit/keymap-presets.ts +428 -0
  297. package/src/kit/layout-policy.ts +31 -0
  298. package/src/kit/light-explorer-model.ts +134 -0
  299. package/src/kit/live-canvas-frame.ts +55 -0
  300. package/src/kit/live-document.ts +296 -0
  301. package/src/kit/live-gesture-lock.ts +50 -0
  302. package/src/kit/live-seam-evidence.ts +11 -0
  303. package/src/kit/live-session-registry.ts +220 -0
  304. package/src/kit/live-transition.ts +391 -0
  305. package/src/kit/manifest-project.ts +107 -0
  306. package/src/kit/module-fetch-diagnosis.ts +192 -0
  307. package/src/kit/mount-failure-report.ts +154 -0
  308. package/src/kit/native-selection-style.ts +497 -0
  309. package/src/kit/object3d-document-write-policy.ts +137 -0
  310. package/src/kit/packaged-runtime.ts +108 -0
  311. package/src/kit/palettes/maya.palette.json +57 -0
  312. package/src/kit/palettes/substance.palette.json +57 -0
  313. package/src/kit/performance-profiler.ts +367 -0
  314. package/src/kit/performance-sources.ts +69 -0
  315. package/src/kit/photograph-notice.ts +141 -0
  316. package/src/kit/play-boot-phase.ts +166 -0
  317. package/src/kit/play-camera-flight.ts +35 -0
  318. package/src/kit/png-encode.worker.ts +26 -0
  319. package/src/kit/presentation-surface.ts +248 -0
  320. package/src/kit/product-command.ts +90 -0
  321. package/src/kit/project-adapter.ts +1140 -0
  322. package/src/kit/project-asset-refresh.ts +23 -0
  323. package/src/kit/project-asset-roots.ts +68 -0
  324. package/src/kit/project-local-state.ts +151 -0
  325. package/src/kit/project-manager.ts +243 -0
  326. package/src/kit/project-module-changes.ts +201 -0
  327. package/src/kit/project-module-split.ts +270 -0
  328. package/src/kit/project-play-layers.ts +25 -0
  329. package/src/kit/project-provenance.ts +115 -0
  330. package/src/kit/project-ready.ts +42 -0
  331. package/src/kit/project-shape.ts +68 -0
  332. package/src/kit/project-tools.ts +107 -0
  333. package/src/kit/projection-types.ts +44 -0
  334. package/src/kit/readiness.ts +113 -0
  335. package/src/kit/renderer-resource-counts.ts +27 -0
  336. package/src/kit/reported-play-state.ts +90 -0
  337. package/src/kit/resolve-contributed-command.ts +14 -0
  338. package/src/kit/resolve-relative-specifier.ts +33 -0
  339. package/src/kit/retained-document-states.ts +91 -0
  340. package/src/kit/scene-document-plan.ts +320 -0
  341. package/src/kit/scene-live-open.ts +210 -0
  342. package/src/kit/scoped-game-css.ts +152 -0
  343. package/src/kit/served-url.ts +5 -0
  344. package/src/kit/session-close.ts +17 -0
  345. package/src/kit/session-tombstone.ts +127 -0
  346. package/src/kit/settings/settings-provider.ts +82 -0
  347. package/src/kit/settings-store.ts +348 -0
  348. package/src/kit/shell-document-state.ts +27 -0
  349. package/src/kit/shell-store-door.ts +45 -0
  350. package/src/kit/shell-store.ts +722 -0
  351. package/src/kit/source-conflict.ts +122 -0
  352. package/src/kit/stage-context.ts +377 -0
  353. package/src/kit/stage-invalidation.ts +25 -0
  354. package/src/kit/stage-store-registry.ts +69 -0
  355. package/src/kit/startup-failure.ts +80 -0
  356. package/src/kit/state-report-deferral.ts +73 -0
  357. package/src/kit/storage/host-files-storage.ts +97 -0
  358. package/src/kit/storage/http-storage.ts +174 -0
  359. package/src/kit/storage/index.ts +75 -0
  360. package/src/kit/storage/mem-storage.ts +158 -0
  361. package/src/kit/storage/path-lock.ts +44 -0
  362. package/src/kit/storage/paths.ts +26 -0
  363. package/src/kit/storage-types.ts +127 -0
  364. package/src/kit/stories/StoryPreviewMount.tsx +306 -0
  365. package/src/kit/stories/compose-project-stories.ts +255 -0
  366. package/src/kit/stories/prefabs-finder.ts +54 -0
  367. package/src/kit/stories/prefabs-from-stories.ts +182 -0
  368. package/src/kit/stories/project-story-regions.ts +24 -0
  369. package/src/kit/stories/story-capture.ts +579 -0
  370. package/src/kit/stories/story-declared-medium.ts +126 -0
  371. package/src/kit/stories/story-discovery.ts +176 -0
  372. package/src/kit/stories/story-dom-runtime.ts +78 -0
  373. package/src/kit/stories/story-grouping.ts +111 -0
  374. package/src/kit/stories/story-mount-turn.ts +27 -0
  375. package/src/kit/stories/story-presentation.ts +215 -0
  376. package/src/kit/stories/story-preview-component.ts +7 -0
  377. package/src/kit/stories/story-registry.ts +530 -0
  378. package/src/kit/stories-scope.ts +35 -0
  379. package/src/kit/story-document-openers.ts +36 -0
  380. package/src/kit/story-thumbnails.ts +47 -0
  381. package/src/kit/surface-keyboard.ts +101 -0
  382. package/src/kit/surface-state.ts +135 -0
  383. package/src/kit/system-seam-evidence.ts +72 -0
  384. package/src/kit/tab-census.ts +202 -0
  385. package/src/kit/tab-lifecycle-client.ts +227 -0
  386. package/src/kit/theme-library.ts +897 -0
  387. package/src/kit/theme-preference.ts +429 -0
  388. package/src/kit/three-viewport-presentation.ts +23 -0
  389. package/src/kit/tool-contribution-play.ts +74 -0
  390. package/src/kit/tool-loader.ts +1918 -0
  391. package/src/kit/transform-mode-request.ts +66 -0
  392. package/src/kit/transient-hint.ts +78 -0
  393. package/src/kit/transport-strip.tsx +174 -0
  394. package/src/kit/ui-source/adapter-region-includes.ts +238 -0
  395. package/src/kit/ui-source/file-region-resolver.ts +302 -0
  396. package/src/kit/ui-source/inspect.ts +775 -0
  397. package/src/kit/ui-source/source-write-backend.ts +605 -0
  398. package/src/kit/ui-source/tier-source-write-backend.ts +279 -0
  399. package/src/kit/user-local-state.ts +105 -0
  400. package/src/kit/viewport-activation-timings.ts +840 -0
  401. package/src/kit/viewport-editor-controls.ts +22 -0
  402. package/src/kit/viewport-presentation.ts +668 -0
  403. package/src/kit/viewport-surface-status.tsx +55 -0
  404. package/src/kit/wait-until.ts +37 -0
  405. package/src/kit/worker-call-metrics.ts +166 -0
  406. package/src/kit/workspace-areas.ts +191 -0
  407. package/src/kit/workspace-aux-commands.ts +11 -0
  408. package/src/kit/workspace-available-documents.ts +142 -0
  409. package/src/kit/workspace-core-utilities.ts +31 -0
  410. package/src/kit/workspace-document-ids.ts +59 -0
  411. package/src/kit/workspace-document-registry.ts +624 -0
  412. package/src/kit/workspace-document-restore.ts +146 -0
  413. package/src/kit/workspace-host-commands.ts +141 -0
  414. package/src/kit/workspace-persistence-gate.ts +40 -0
  415. package/src/kit/workspace-play-utilities.ts +44 -0
  416. package/src/kit/workspace-presets.ts +446 -0
  417. package/src/kit/workspace-regions.ts +276 -0
  418. package/src/kit/workspace-static-panels.ts +73 -0
  419. package/src/kit/workspace-status-registry.ts +121 -0
  420. package/src/kit/workspace-storage.ts +35 -0
  421. package/src/kit/workspace-style.ts +226 -0
  422. package/src/kit/workspace-utility-commands.ts +74 -0
  423. package/src/kit/workspace-utility-registry.ts +263 -0
  424. package/src/kit/world-adoption-event.ts +23 -0
  425. package/src/kit/world-adoption.ts +115 -0
  426. package/src/kit/world-canvas-viewport-state.ts +35 -0
  427. package/src/kit/world-document-routing.ts +104 -0
  428. package/src/kit/world-pan-state.ts +198 -0
  429. package/src/kit/write-pipe.ts +173 -0
  430. package/src/layout-arrangements.ts +5 -0
  431. package/src/layouts.tsx +108 -0
  432. package/src/looks.ts +16 -0
  433. package/src/project/output-roots.ts +73 -0
  434. package/src/project/tab-census.ts +155 -0
  435. package/src/project-tool-catalog.ts +104 -0
  436. package/src/selection.tsx +107 -0
  437. package/src/services.ts +18 -0
  438. package/src/session/build-report.ts +22 -0
  439. package/src/session/collaboration-types.ts +262 -0
  440. package/src/session/command-table.ts +327 -0
  441. package/src/session/discovery.ts +100 -0
  442. package/src/session/editor-brand.ts +48 -0
  443. package/src/session/editor-compatibility.ts +329 -0
  444. package/src/session/editor-control-lifecycle.ts +68 -0
  445. package/src/session/editor-control-protocol.ts +5 -0
  446. package/src/session/entrypoint-selection-readers.ts +66 -0
  447. package/src/session/entrypoint-selection-source.ts +120 -0
  448. package/src/session/game-css-scope.ts +30 -0
  449. package/src/session/hosted-attachment.ts +225 -0
  450. package/src/session/limited-view.ts +82 -0
  451. package/src/session/product-create.ts +24 -0
  452. package/src/session/product-locator.ts +478 -0
  453. package/src/session/project-module-url.ts +242 -0
  454. package/src/session/project-serving.ts +164 -0
  455. package/src/session/project-upgrade.ts +669 -0
  456. package/src/session/registry-format.ts +210 -0
  457. package/src/session/relative-path-guard.ts +56 -0
  458. package/src/session/scoped-game-css.ts +461 -0
  459. package/src/session/source-glob.ts +15 -0
  460. package/src/session/tool-contribution-convention.ts +123 -0
  461. package/src/session/workbench-locator.ts +712 -0
  462. package/src/session.ts +41 -0
  463. package/src/share.ts +160 -0
  464. package/src/source-analysis.ts +28 -0
  465. package/src/source-authoring.ts +439 -0
  466. package/src/tools/errors.ts +91 -0
  467. package/src/tools/provider-execution.ts +70 -0
  468. package/src/tools/registry.ts +341 -0
  469. package/src/tools/types.ts +159 -0
  470. package/src/transport.ts +100 -0
  471. package/src/types.ts +1693 -0
  472. package/src/views.ts +164 -0
  473. package/src/widgets/design-system.ts +93 -0
  474. package/src/widgets/editor-appearance.ts +151 -0
  475. package/src/widgets/editor-material.ts +83 -0
  476. package/src/widgets/icon-set-registry.ts +105 -0
  477. package/src/widgets/index.ts +71 -0
  478. package/src/widgets/inspector-widgets/AlignmentGrid.tsx +182 -0
  479. package/src/widgets/inspector-widgets/AssetSlotPicker.tsx +123 -0
  480. package/src/widgets/inspector-widgets/BorderEditor.tsx +309 -0
  481. package/src/widgets/inspector-widgets/ColorPicker.tsx +549 -0
  482. package/src/widgets/inspector-widgets/CurveEditor.tsx +359 -0
  483. package/src/widgets/inspector-widgets/FilterEditor.tsx +108 -0
  484. package/src/widgets/inspector-widgets/FontPicker.tsx +191 -0
  485. package/src/widgets/inspector-widgets/GradientEditor.tsx +623 -0
  486. package/src/widgets/inspector-widgets/ScrubbableInput.tsx +180 -0
  487. package/src/widgets/inspector-widgets/ShadowEditor.tsx +319 -0
  488. package/src/widgets/inspector-widgets/color-utils.ts +201 -0
  489. package/src/widgets/inspector-widgets/curve-utils.ts +212 -0
  490. package/src/widgets/inspector-widgets/index.ts +25 -0
  491. package/src/widgets/inspector-widgets/shared.tsx +140 -0
  492. package/src/widgets/interactive-edit-scope.ts +33 -0
  493. package/src/widgets/patterns/Dialog.tsx +140 -0
  494. package/src/widgets/patterns/Fields.tsx +44 -0
  495. package/src/widgets/patterns/List.tsx +25 -0
  496. package/src/widgets/patterns/StateSurface.tsx +40 -0
  497. package/src/widgets/patterns/Surfaces.tsx +122 -0
  498. package/src/widgets/patterns/Tabs.tsx +80 -0
  499. package/src/widgets/patterns/Toolbar.tsx +72 -0
  500. package/src/widgets/patterns/Tree.tsx +72 -0
  501. package/src/widgets/primitives/AnchoredMenu.tsx +260 -0
  502. package/src/widgets/primitives/Button.tsx +62 -0
  503. package/src/widgets/primitives/ColorInput.tsx +78 -0
  504. package/src/widgets/primitives/DraftTextInput.tsx +63 -0
  505. package/src/widgets/primitives/EditorIcon.tsx +157 -0
  506. package/src/widgets/primitives/FormControls.tsx +88 -0
  507. package/src/widgets/primitives/HoverPreview.tsx +96 -0
  508. package/src/widgets/primitives/JsonInput.tsx +113 -0
  509. package/src/widgets/primitives/Layout.tsx +100 -0
  510. package/src/widgets/primitives/Menu.tsx +161 -0
  511. package/src/widgets/primitives/NumberInput.tsx +169 -0
  512. package/src/widgets/primitives/Panel.tsx +80 -0
  513. package/src/widgets/primitives/SectionHeader.tsx +77 -0
  514. package/src/widgets/primitives/Text.tsx +54 -0
  515. package/src/widgets/primitives/ThemeRootPortal.tsx +52 -0
  516. package/src/widgets/primitives/Tooltip.tsx +204 -0
  517. package/src/widgets/primitives/Vec3Input.tsx +70 -0
  518. package/src/widgets/primitives/banner-tones.ts +32 -0
  519. package/src/widgets/primitives/clamp-to-viewport.ts +44 -0
  520. package/src/widgets/primitives/editor-icons.ts +254 -0
  521. package/src/widgets/primitives/panel-header-styles.ts +42 -0
  522. package/src/widgets/theme.ts +2841 -0
  523. package/src/widgets/z-index.ts +25 -0
@@ -0,0 +1,579 @@
1
+ /**
2
+ * The STORY lane of the editor's `screenshot` command — a `.stories.tsx`/`.stories.ts` file's
3
+ * CSF exports rendered in the live session's DOM and captured as one variant
4
+ * sheet (ARCHITECTURE-CORE §Agent surface, the look-verb decision).
5
+ *
6
+ * Nothing here is a second story runtime. The editor already discovers CSF
7
+ * files (`stories/story-discovery.ts`), composes them through Storybook's own
8
+ * portable-story API (`stories/compose-project-stories.ts`) and mounts one
9
+ * into an isolated `react-dom/client` root (`stories/StoryPreviewMount.ts`'s
10
+ * `mountIsolatedStory`) for the Stories panel. This lane DRIVES that machinery
11
+ * headlessly and photographs the result — the CSF module on disk stays the
12
+ * only story definition that exists, and a story looks the same through the
13
+ * CLI as it does in the panel because it is literally the same mount.
14
+ *
15
+ * The pixels come from the same leg the GAME lane composites with
16
+ * (`capturePlayComposite`): a story is DOM, so it rides the `foreignObject`
17
+ * half of that function exactly as a React HUD does. One capture path, so a
18
+ * story sheet and a game screenshot can never disagree about how DOM is
19
+ * rasterized.
20
+ *
21
+ * A CANVAS story is DOM (its own `<Application>` renders into a `<canvas>` the
22
+ * DOM leg composites), so it rides that leg — but it needed two things the leg
23
+ * did not do, and until they were added every `@pixi/react` story photographed
24
+ * BLANK: the capture has to wait for the Pixi renderer's first presented frame
25
+ * (`<Application>` commits its canvas before it has a renderer at all), and it
26
+ * has to read the pixels back through Pixi's own extraction (a WebGL canvas
27
+ * created without `preserveDrawingBuffer` — which a story's own `<Application>`
28
+ * is — has nothing left to `drawImage` by the time the compositor gets to it).
29
+ * Both live in `canvas-preview-frames.ts` with the measurement.
30
+ *
31
+ * EXCEPT a `three` story, which is not DOM at all: a prefab's CSF renders R3F
32
+ * JSX (`<group>`, `<mesh>`), and a DOM root turns those into inert custom
33
+ * elements — every 3D prefab's sheet came out pure white, silently (found
34
+ * live: a project whose 120+ prefab stories all photographed blank). So each
35
+ * export is offered to the off-screen R3F mount FIRST (`mountStoryObject3D`
36
+ * via `captureStoryComponentThumbnail` — the same machinery the Content tab's
37
+ * thumbnails and the 3D board already trust, packaged-runtime-aware), and
38
+ * only a story the R3F reconciler refuses ("is not part of the THREE
39
+ * namespace" — the qualification-rejection class that module documents) falls
40
+ * through to the DOM leg below. The known cost: a DOM story's `loaders` run
41
+ * once in the refused three attempt and again in the DOM mount — accepted,
42
+ * because the alternative (guessing a story's kind from its source) is a
43
+ * second story format, and loaders on DOM stories are rare.
44
+ *
45
+ * Isolation and cleanup: every export gets its OWN container element, mounted,
46
+ * captured, unmounted and removed before the next one — and the offscreen host
47
+ * is removed in a `finally`, so a story that throws mid-sheet leaves nothing
48
+ * behind in `document.body`. Per-export containers are not fussiness:
49
+ * `mountIsolatedStory` keys its react root by container in a `WeakMap` and
50
+ * DELETES the entry on unmount, so re-mounting into an already-unmounted
51
+ * container would call `createRoot` twice on the same node — react-dom's own
52
+ * documented warning case.
53
+ */
54
+
55
+ import { reactStoryBoardLayout } from '@volter/sdk/kit/authoring/react-story-board';
56
+ import { drawBitmapLabel } from '@volter/sdk/kit/bitmap-label';
57
+ import { observeCanvasMount } from '@volter/sdk/kit/canvas-frames';
58
+ import { type CaptureOptions, capturePlayComposite } from '@volter/sdk/kit/composite-screenshot';
59
+ import { getCurrentProject } from '@volter/sdk/kit/project-manager';
60
+ import {
61
+ type ComposedProjectStory,
62
+ composeProjectStories,
63
+ ensureProjectAnnotations,
64
+ } from '@volter/sdk/kit/stories/compose-project-stories';
65
+ import { getProjectStoryRegions } from '@volter/sdk/kit/stories/project-story-regions';
66
+ import { mountIsolatedStory } from './StoryPreviewMount';
67
+ import { declaredStoryMedium } from '@volter/sdk/kit/stories/story-declared-medium';
68
+ import {
69
+ discoverProjectStories,
70
+ loadProjectPreviewAnnotations,
71
+ type StoryDiscoveryProject,
72
+ } from '@volter/sdk/kit/stories/story-discovery';
73
+ import { storyBoardPresentation } from '@volter/sdk/kit/stories/story-presentation';
74
+ import { getProjectPreviewStories, whenProjectStoriesReady } from '@volter/sdk/kit/stories/story-registry';
75
+ import { isStoryMediumMismatch, storyThumbnailCapture } from '@volter/sdk/kit/story-thumbnails';
76
+ import type { ProjectPreviewStory } from '@volter/sdk/kit/stories/story-registry';
77
+
78
+ type StoryPreviewComponent = ProjectPreviewStory['Component'];
79
+
80
+ /** One CSF export, rendered and photographed. */
81
+ export interface StoryVariantImage {
82
+ /** The CSF export name — what `--story` narrows on, and the sheet label. */
83
+ name: string;
84
+ /** Storybook's human-facing story name; the CLI prints it, files use `name`. */
85
+ label: string;
86
+ base64: string;
87
+ mimeType: 'image/png';
88
+ }
89
+
90
+ /** One story FILE's variant sheet — the unit this lane produces. */
91
+ export interface StoryVariantCapture {
92
+ /** Project-relative path of the CSF module that was photographed. */
93
+ modulePath: string;
94
+ /** Per-variant cell size in CSS pixels. */
95
+ width: number;
96
+ height: number;
97
+ variants: StoryVariantImage[];
98
+ contactSheet: { width: number; height: number; base64: string; mimeType: 'image/png' };
99
+ }
100
+
101
+ export interface StoryCaptureOptions {
102
+ /** Project-relative CSF path, exactly as `/__editor/story-files` reports it. */
103
+ modulePath: string;
104
+ /** Narrow the sheet to ONE export (`--story <export>`). */
105
+ story?: string;
106
+ width: number;
107
+ height: number;
108
+ /**
109
+ * The rasterizer. Defaults to the game lane's own composite capture; a test
110
+ * injects a stub because jsdom has no canvas, which is also what lets the
111
+ * mount/cleanup contract be proven without a browser. The options are the
112
+ * composite's own — the Pixi lane below hands it a `canvasFrame` seam.
113
+ */
114
+ capture?: (container: HTMLElement, options?: CaptureOptions) => Promise<{ base64: string }>;
115
+ /**
116
+ * The `three` leg (see the module doc comment). Defaults to the registered
117
+ * three.js story capture (`@volter/sdk/kit/story-thumbnails`), which a
118
+ * page without WebGL does not register, so under tests the default leg is
119
+ * simply absent unless a stub is injected. Returns a `data:` URL; ANY rejection routes
120
+ * the story to the DOM leg.
121
+ */
122
+ captureThree?: (
123
+ component: StoryPreviewComponent,
124
+ size: { width: number; height: number },
125
+ ) => Promise<string>;
126
+ /** Free capture camera for THREE stories (`--azimuth/--elevation/
127
+ * --distance`), forwarded to the three leg's `captureObjectAssetPreview`.
128
+ * A selected story that lands on the DOM leg REFUSES it by name — a
129
+ * camera silently ignored is a wrong photograph presented as evidence. */
130
+ camera?: import('@volter/sdk').AssetPreviewCameraChoice;
131
+ /** Clip pose for THREE stories (`--clip/--time`); DOM-leg stories refuse
132
+ * it by the same rule. */
133
+ pose?: import('@volter/sdk').AssetPreviewPose;
134
+ }
135
+
136
+ /** Capture the UI Canvas as a composition of every declared DOM artboard.
137
+ * This uses the same portable-story mount and composite rasterizer as the
138
+ * live board, without opening or activating that document. */
139
+ export async function captureDomStoryBoardPreview(width: number, height: number): Promise<string> {
140
+ await whenProjectStoriesReady();
141
+ const regions = getProjectStoryRegions();
142
+ const stories = getProjectPreviewStories().filter(
143
+ (story) => declaredStoryMedium({ modulePath: story.modulePath, regions }).medium === 'dom',
144
+ );
145
+ const output = document.createElement('canvas');
146
+ output.width = width;
147
+ output.height = height;
148
+ const context = output.getContext('2d');
149
+ if (!context) throw new Error('UI Canvas preview could not create a 2D output context.');
150
+ if (stories.length === 0) return output.toDataURL('image/png');
151
+ const presentation = storyBoardPresentation(stories);
152
+ const resolution = getCurrentProject()?.config.resolution;
153
+ const fallback = { width: resolution?.width ?? 640, height: resolution?.height ?? 400 };
154
+ const images = new Map<string, HTMLImageElement>();
155
+ const sizes = new Map<string, { width: number; height: number }>();
156
+ for (const story of stories) {
157
+ const frame = presentation.resolve(story, fallback);
158
+ const [variant] = await renderStoryVariants(story.modulePath, [story], {
159
+ width: frame.width,
160
+ height: frame.height,
161
+ capture: capturePlayComposite,
162
+ cropToContent: frame.layout !== 'fullscreen',
163
+ layout: frame.layout,
164
+ transparent: true,
165
+ });
166
+ if (!variant) throw new Error(`No preview rendered for ${story.id}.`);
167
+ const image = new Image();
168
+ image.src = `data:${variant.mimeType};base64,${variant.base64}`;
169
+ await image.decode();
170
+ images.set(story.id, image);
171
+ sizes.set(story.id, { width: image.naturalWidth, height: image.naturalHeight });
172
+ }
173
+ const board = reactStoryBoardLayout(stories, presentation.index, (story) => sizes.get(story.id)!);
174
+ const scale = Math.min(width / board.width, height / board.height);
175
+ const left = (width - board.width * scale) / 2;
176
+ const top = (height - board.height * scale) / 2;
177
+ for (const [id, frame] of board.layouts) {
178
+ context.drawImage(
179
+ images.get(id)!,
180
+ left + frame.x * scale,
181
+ top + frame.y * scale,
182
+ frame.width * scale,
183
+ frame.height * scale,
184
+ );
185
+ }
186
+ return output.toDataURL('image/png');
187
+ }
188
+
189
+ /**
190
+ * A neutral, OPAQUE backdrop behind each story.
191
+ *
192
+ * Storybook's own default backdrop is white and most CSF files that care paint
193
+ * their own (a decorator with `position:absolute; inset:0`), so this only shows
194
+ * through where a story declares nothing. Opaque rather than transparent for a
195
+ * mechanical reason as well: `capturePlayComposite` treats a canvas-less
196
+ * capture that comes back mostly transparent as a torn `foreignObject` frame
197
+ * and pays for two bounded retries before returning it.
198
+ */
199
+ const STORY_BACKDROP = '#ffffff';
200
+
201
+ /** The sheet's gutter/letterbox colour — dark, so a white story cell reads as
202
+ * a cell rather than bleeding into the sheet. */
203
+ const SHEET_BACKDROP = '#14171c';
204
+
205
+ /**
206
+ * Load one project CSF module and photograph each of its stories.
207
+ *
208
+ * Every refusal names the FILE, because the caller typed a path: a module the
209
+ * project's own story scan does not know, a module that fails to import, a
210
+ * module with no renderable stories, and a `--story` that names no export all
211
+ * fail loudly rather than producing a plausible empty sheet.
212
+ */
213
+ export async function captureProjectStoryVariants(
214
+ project: StoryDiscoveryProject | null,
215
+ options: StoryCaptureOptions,
216
+ ): Promise<StoryVariantCapture> {
217
+ const { modulePath, width, height } = options;
218
+ if (!project) {
219
+ throw new Error(
220
+ `No project is open, so '${modulePath}' cannot be loaded. Open the project first.`,
221
+ );
222
+ }
223
+
224
+ const discovered = await discoverProjectStories(project);
225
+ const found = discovered.find((candidate) => candidate.modulePath === modulePath);
226
+ if (!found) {
227
+ throw new Error(
228
+ `'${modulePath}' is not a story file of this project. ` +
229
+ (discovered.length === 0
230
+ ? 'This project has no *.stories.tsx / *.stories.ts under src/.'
231
+ : `This project's story files are: ${discovered.map((s) => s.modulePath).join(', ')}.`),
232
+ );
233
+ }
234
+
235
+ // The Stories panel applies the project's `.storybook/preview` annotations
236
+ // through this same once-per-session latch before composing anything. Doing
237
+ // it here too means a capture taken before the panel has ever opened is
238
+ // composed identically to one taken after — not almost identically.
239
+ await ensureProjectAnnotations(await loadProjectPreviewAnnotations(project));
240
+
241
+ let mod: unknown;
242
+ try {
243
+ mod = await found.load();
244
+ } catch (err) {
245
+ throw new Error(
246
+ `'${modulePath}' failed to import — ${err instanceof Error ? err.message : String(err)}`,
247
+ );
248
+ }
249
+
250
+ const composed = await composeProjectStories(modulePath, mod);
251
+ if (!composed.ok) throw new Error(composed.error);
252
+
253
+ const selected = selectStories(modulePath, composed.stories, options.story);
254
+ const capture = options.capture ?? capturePlayComposite;
255
+ const threeCapture = storyThumbnailCapture('three');
256
+ const captureThree =
257
+ options.captureThree ??
258
+ (threeCapture
259
+ ? (component: StoryPreviewComponent, size: { width: number; height: number }) =>
260
+ threeCapture(component, {
261
+ ...size,
262
+ ...(options.camera === undefined ? {} : { camera: options.camera }),
263
+ ...(options.pose === undefined ? {} : { pose: options.pose }),
264
+ })
265
+ : undefined);
266
+ if ((options.camera || options.pose) && !captureThree) {
267
+ throw new Error(
268
+ `'${modulePath}': camera/clip parameters need the three capture leg, which this ` +
269
+ 'environment cannot provide (no WebGL).',
270
+ );
271
+ }
272
+ const variants = await renderStoryVariants(modulePath, selected, {
273
+ width,
274
+ height,
275
+ capture,
276
+ ...(captureThree ? { captureThree } : {}),
277
+ requireThree: Boolean(options.camera || options.pose),
278
+ });
279
+ const contactSheet = await composeVariantSheet(variants, width, height);
280
+ return { modulePath, width, height, variants, contactSheet };
281
+ }
282
+
283
+ /** `--story <export>`, or every story in CSF export order. */
284
+ function selectStories(
285
+ modulePath: string,
286
+ stories: ComposedProjectStory[],
287
+ story: string | undefined,
288
+ ): ComposedProjectStory[] {
289
+ if (story === undefined) return stories;
290
+ const match = stories.find((candidate) => candidate.name === story);
291
+ if (!match) {
292
+ throw new Error(
293
+ `'${modulePath}' exports no story named '${story}'. It exports: ` +
294
+ `${stories.map((candidate) => candidate.name).join(', ')}.`,
295
+ );
296
+ }
297
+ return [match];
298
+ }
299
+
300
+ /**
301
+ * Mount each story offscreen, one at a time, and capture it.
302
+ *
303
+ * The host is `position: fixed` far off-screen with `contain: strict` — the
304
+ * containment is load-bearing, not decoration: layout containment makes the
305
+ * host the containing block for `position: fixed` descendants, so a HUD story
306
+ * that pins itself to the viewport lands inside the capture box instead of
307
+ * flashing across the editor the user is watching.
308
+ */
309
+ export async function renderStoryVariants(
310
+ modulePath: string,
311
+ stories: readonly ComposedProjectStory[],
312
+ options: {
313
+ width: number;
314
+ height: number;
315
+ capture: (
316
+ container: HTMLElement,
317
+ captureOptions?: CaptureOptions,
318
+ ) => Promise<{
319
+ base64: string;
320
+ }>;
321
+ captureThree?: (
322
+ component: StoryPreviewComponent,
323
+ size: { width: number; height: number },
324
+ ) => Promise<string>;
325
+ /** Camera/clip parameters were requested: a story that cannot render on
326
+ * the three leg REFUSES by name instead of falling back to the DOM leg,
327
+ * which would silently ignore them. */
328
+ requireThree?: boolean;
329
+ /** Preserve the declared mount box for artboard/composition capture. */
330
+ cropToContent?: boolean;
331
+ layout?: string;
332
+ transparent?: boolean;
333
+ },
334
+ ): Promise<StoryVariantImage[]> {
335
+ const { width, height, capture, captureThree, requireThree, cropToContent = true } = options;
336
+ const host = document.createElement('div');
337
+ host.setAttribute(
338
+ 'style',
339
+ `position:fixed;left:-20000px;top:0;width:${width}px;height:${height}px;` +
340
+ // The neutral backdrop lives HERE, behind the per-story container it
341
+ // exactly covers — never inline on the container itself. That container
342
+ // is a game-CSS scope root (`mountIsolatedStory` marks it), and an
343
+ // inline background there outranks every rule in the game's own
344
+ // stylesheet: a dark-themed game would get its white HUD text painted
345
+ // onto the editor's white backdrop, correctly styled and unreadable.
346
+ // Behind the container the backdrop still shows through wherever the
347
+ // story paints nothing, which is all it was ever for.
348
+ `overflow:hidden;contain:strict;pointer-events:none;background:${options.transparent ? 'transparent' : STORY_BACKDROP};`,
349
+ );
350
+ document.body.appendChild(host);
351
+ const variants: StoryVariantImage[] = [];
352
+ try {
353
+ for (const story of stories) {
354
+ // The three leg first (see the module doc comment): a prefab story is
355
+ // R3F JSX that a DOM root can only render as a blank cell. ANY three
356
+ // failure — the reconciler's qualification rejection for a DOM story,
357
+ // or a genuine crash — falls through to the DOM leg, whose error
358
+ // boundary is the containment that names a broken story in its cell.
359
+ if (captureThree) {
360
+ const three = await captureThree(story.Component as StoryPreviewComponent, {
361
+ width,
362
+ height,
363
+ }).catch((error: unknown) => {
364
+ // The DOM-story classification refusal falls through silently by design; a THREE
365
+ // story that genuinely failed must be named here, or a broken prefab photographs
366
+ // as a blank cell with no trace (the whole-project-blank-sheet defect).
367
+ if (!isStoryMediumMismatch(error)) {
368
+ // biome-ignore lint/suspicious/noConsole: the loud half of the fall-through contract — this refusal used to be swallowed whole.
369
+ console.warn(
370
+ `[story-capture] three mount for '${story.name}' failed — falling back to the DOM leg:`,
371
+ error instanceof Error ? (error.stack ?? error.message) : String(error),
372
+ );
373
+ }
374
+ return null;
375
+ });
376
+ if (three === null && requireThree) {
377
+ throw new Error(
378
+ `story '${story.name}' did not render on the three leg — ` +
379
+ '--azimuth/--elevation/--distance/--clip/--time apply to three (R3F) stories ' +
380
+ 'only, and falling back to the DOM leg would silently ignore them. Narrow to a ' +
381
+ 'three story with --story <export>, or drop the camera/clip flags.',
382
+ );
383
+ }
384
+ if (three !== null) {
385
+ const comma = three.indexOf(',');
386
+ variants.push({
387
+ name: story.name,
388
+ label: story.label,
389
+ base64: comma >= 0 ? three.slice(comma + 1) : three,
390
+ mimeType: 'image/png',
391
+ });
392
+ continue;
393
+ }
394
+ }
395
+ // A FRESH container per export — see the module doc comment for why
396
+ // reusing one would trip react-dom's double-`createRoot` warning.
397
+ const container = document.createElement('div');
398
+ container.setAttribute(
399
+ 'style',
400
+ `position:relative;width:${width}px;height:${height}px;overflow:hidden;`,
401
+ );
402
+ if (options.layout === 'padded') container.style.padding = '16px';
403
+ if (options.layout === 'centered') {
404
+ container.style.display = 'flex';
405
+ container.style.alignItems = 'center';
406
+ container.style.justifyContent = 'center';
407
+ }
408
+ container.style.boxSizing = 'border-box';
409
+ host.replaceChildren(container);
410
+ // The medium that draws the story's canvases observes its mount, so the
411
+ // two things such a capture needs — a settled first frame, and a readback
412
+ // the compositor cannot clear — are keyed on the MOUNT rather than
413
+ // guessed from the story's source (`@volter/sdk/kit/canvas-frames`).
414
+ const observed = await observeCanvasMount(async () => {
415
+ const handle = await mountIsolatedStory(container, modulePath, story.name, story.Component);
416
+ await awaitStoryCommit(container);
417
+ return handle;
418
+ });
419
+ await observed.settled();
420
+ const mounted = observed.result;
421
+ try {
422
+ // Mount in the truth, frame the subject: the host is the full mount
423
+ // box (the screen the story's anchors resolve against), the photograph
424
+ // crops to what painted. A widget crops to the widget; a full screen's
425
+ // painted union spans the frame and stays effectively full-frame.
426
+ const shot = await capture(host, {
427
+ canvasFrame: observed.frame,
428
+ cropToContent,
429
+ ...(options.transparent ? { allowTransparent: true } : {}),
430
+ });
431
+ variants.push({
432
+ name: story.name,
433
+ label: story.label,
434
+ base64: shot.base64,
435
+ mimeType: 'image/png',
436
+ });
437
+ } finally {
438
+ mounted?.unmount();
439
+ // `unmount()` defers its real teardown to a microtask (React refuses a
440
+ // synchronous unmount from inside another root's commit); yield a full
441
+ // turn so it has landed before the container is detached.
442
+ await new Promise<void>((resolve) => setTimeout(resolve, 0));
443
+ container.remove();
444
+ }
445
+ }
446
+ } finally {
447
+ host.remove();
448
+ }
449
+ return variants;
450
+ }
451
+
452
+ /**
453
+ * Wait for the isolated root's render to COMMIT before photographing it.
454
+ *
455
+ * `mountIsolatedStory` returns as soon as it has called `root.render()`, and
456
+ * React schedules that work rather than performing it inline — so capturing on
457
+ * the very next line photographs an empty container. That is not a
458
+ * hypothetical — it has been observed in practice,
459
+ * and it is the worst possible failure for a look verb, because a blank cell
460
+ * rasterizes into a perfectly plausible sheet.
461
+ *
462
+ * Polls for committed child nodes rather than sleeping a guessed duration, so
463
+ * the common case costs one turn. The bound is what a story that legitimately
464
+ * renders `null` falls through — an empty capture is then the truth, not a
465
+ * race.
466
+ *
467
+ * The FIRST child is not the last word: a story whose content arrives through
468
+ * `lazy()` + `<Suspense>` commits its wrapper immediately and its screen when
469
+ * the chunk lands — a translated port's scene UI is the shipped case, and the
470
+ * capture used to photograph the fallback. So after the first commit this
471
+ * waits for the subtree to go QUIET (no mutations for a settle window),
472
+ * bounded — a story that animates forever is photographed mid-animation at the
473
+ * ceiling rather than never.
474
+ */
475
+ async function awaitStoryCommit(container: HTMLElement, maxTurns = 10): Promise<void> {
476
+ for (let turn = 0; turn < maxTurns; turn++) {
477
+ if (container.childNodes.length > 0) break;
478
+ await new Promise<void>((resolve) => setTimeout(resolve, 0));
479
+ }
480
+ if (container.childNodes.length === 0) return;
481
+ const QUIET_MS = 200;
482
+ const CEILING_MS = 5_000;
483
+ await new Promise<void>((resolve) => {
484
+ let settle: ReturnType<typeof setTimeout> | null = null;
485
+ let done = false;
486
+ const observer = new MutationObserver(() => {
487
+ if (settle !== null) clearTimeout(settle);
488
+ settle = setTimeout(finish, QUIET_MS);
489
+ });
490
+ const ceiling = setTimeout(finish, CEILING_MS);
491
+ function finish(): void {
492
+ if (done) return;
493
+ done = true;
494
+ observer.disconnect();
495
+ if (settle !== null) clearTimeout(settle);
496
+ clearTimeout(ceiling);
497
+ resolve();
498
+ }
499
+ observer.observe(container, {
500
+ subtree: true,
501
+ childList: true,
502
+ attributes: true,
503
+ characterData: true,
504
+ });
505
+ settle = setTimeout(finish, QUIET_MS);
506
+ });
507
+ }
508
+
509
+ /**
510
+ * Tile the captured variants into ONE labeled sheet — the same 2-column,
511
+ * bitmap-labeled geometry the Asset Lab's four-view contact sheet and its
512
+ * shot-set sheet already use (`asset-preview.ts`), so every sheet this repo
513
+ * hands an agent reads the same way. A single variant gets a single cell
514
+ * rather than a half-empty grid.
515
+ */
516
+ export async function composeVariantSheet(
517
+ variants: readonly StoryVariantImage[],
518
+ width: number,
519
+ height: number,
520
+ options: { readonly labels?: boolean; readonly columns?: number } = {},
521
+ ): Promise<{ width: number; height: number; base64: string; mimeType: 'image/png' }> {
522
+ const columns = Math.min(
523
+ Math.max(1, variants.length),
524
+ Math.max(1, Math.floor(options.columns ?? 2)),
525
+ );
526
+ const rows = Math.max(1, Math.ceil(variants.length / columns));
527
+ // Cells fit the variants, not the mount box: a cropped-to-content variant is
528
+ // its own size, so the cell is the largest variant (falling back to the
529
+ // mount box for an empty sheet) and each image draws centered at 1:1 —
530
+ // stretching a cropped widget to a screen-shaped cell would undo the crop.
531
+ const images: HTMLImageElement[] = [];
532
+ for (const variant of variants) {
533
+ const image = new Image();
534
+ image.src = `data:${variant.mimeType};base64,${variant.base64}`;
535
+ await image.decode();
536
+ images.push(image);
537
+ }
538
+ const cellWidth = Math.max(
539
+ 1,
540
+ ...images.map((one) => one.naturalWidth),
541
+ images.length ? 0 : width,
542
+ );
543
+ const cellHeight = Math.max(
544
+ 1,
545
+ ...images.map((one) => one.naturalHeight),
546
+ images.length ? 0 : height,
547
+ );
548
+ const sheetWidth = cellWidth * columns;
549
+ const sheetHeight = cellHeight * rows;
550
+ const canvas = document.createElement('canvas');
551
+ canvas.width = sheetWidth;
552
+ canvas.height = sheetHeight;
553
+ const context = canvas.getContext('2d');
554
+ if (!context) throw new Error('Unable to create the story variant sheet.');
555
+ context.fillStyle = SHEET_BACKDROP;
556
+ context.fillRect(0, 0, sheetWidth, sheetHeight);
557
+
558
+ for (const [index, variant] of variants.entries()) {
559
+ const x = (index % columns) * cellWidth;
560
+ const y = Math.floor(index / columns) * cellHeight;
561
+ const image = images[index]!;
562
+ context.drawImage(
563
+ image,
564
+ x + Math.floor((cellWidth - image.naturalWidth) / 2),
565
+ y + Math.floor((cellHeight - image.naturalHeight) / 2),
566
+ );
567
+ if (options.labels !== false)
568
+ drawBitmapLabel(context, variant.name.toUpperCase(), x, y, cellWidth, cellHeight);
569
+ }
570
+
571
+ const dataUrl = canvas.toDataURL('image/png');
572
+ const comma = dataUrl.indexOf(',');
573
+ return {
574
+ width: sheetWidth,
575
+ height: sheetHeight,
576
+ base64: comma >= 0 ? dataUrl.slice(comma + 1) : dataUrl,
577
+ mimeType: 'image/png',
578
+ };
579
+ }