@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,794 @@
1
+ /**
2
+ * Asset DOCUMENTS (W2 — inventory rows R2–R7):
3
+ * model/image/audio/input-map/JSON viewers open as CENTER workspace documents
4
+ * through `workspace-document-registry.ts`, replacing the former `RightPanel`
5
+ * asset-tab mechanism (`editor-store.ts`'s `AssetTab` list, deleted in this
6
+ * wave). The Inspector is never displaced by a viewer again (§10 acceptance
7
+ * #3).
8
+ *
9
+ * Identity (§7.1 stable ids): document ids keep the exact key vocabulary the
10
+ * old store tabs used, so the control-API surface (`open-asset-tab` /
11
+ * `close-asset-tab` / `active-tab` in `command-listener.ts`, the editor's
12
+ * `open-asset`) keeps its meaning unchanged:
13
+ *
14
+ * - project asset → the serving path itself (`/textures/crate.png`)
15
+ * - Asset Editor → `asset-editor:entity:<entityId>`
16
+ * - online asset → `online:<source>:<id>`
17
+ *
18
+ * Titles are source-owned file names as authored (§8 — `crate.png`, never a
19
+ * technology bucket); the online document keeps the catalog's own asset name.
20
+ *
21
+ * Per-document RUNTIME state (an online asset resolving to a local path after
22
+ * download) lives in an HMR-stable module spec map in the house
23
+ * `useSyncExternalStore` shape — the registry's descriptors stay immutable,
24
+ * and `AssetDocumentContent` re-renders from the spec map. Specs are dropped
25
+ * on document dispose (close / project teardown via
26
+ * `closeAllWorkspaceDocuments`), so nothing leaks across sessions.
27
+ *
28
+ * T6.3 interplay: activating an asset document means neither the scene
29
+ * viewport nor the Game preview is the active center surface, so the
30
+ * descriptor's `onActivate` writes the store's `'scene'` tab — the play input
31
+ * gate (`playState === 'playing' && activeViewportTab === 'play'`) drops
32
+ * without stopping play, exactly like clicking the scene tab.
33
+ */
34
+
35
+ import { AssetViewerSlot } from '@volter/sdk/kit/asset-viewers';
36
+ import { faCloudArrowDown, faFileLines } from '@fortawesome/free-solid-svg-icons';
37
+ import { bg, danger, text } from '@volter/sdk/widgets';
38
+ import type { AuthoringAssetSubject } from '@volter/project/adapter';
39
+ import { lazy, type ReactNode, Suspense, useEffect, useSyncExternalStore } from 'react';
40
+ import { clearSelectedAsset } from '../asset-selection';
41
+ import { assetCapabilities } from '@volter/sdk/kit/asset-capabilities';
42
+ import {
43
+ splitSpritesheetAssetPath,
44
+ spritesheetFrameTitle,
45
+ } from '@volter/sdk/kit/asset-workflow/pixi-spritesheet';
46
+ import { authoringAssetDataUrl } from '../authoring/authoring-asset-url';
47
+ import { awaitAnnouncedObject3DDocumentSession } from '../document-context-registry';
48
+ import { registerDocumentOpener } from '@volter/sdk/kit/document-open-registry';
49
+ import { projectFileExists } from '@volter/sdk/kit/editor-api';
50
+ import type { AssetKind, OnlineAssetInfo } from '../asset-selection';
51
+ import { type InspectionSection, PROPERTIES_SECTION_ORDER } from '@volter/sdk/kit/inspection-model';
52
+ import { inspectionNodeMedia } from '@volter/sdk/kit/inspection-node-media';
53
+ import { getActiveAuthoring } from '@volter/sdk/kit/authoring/active-adapter';
54
+ import { shellStoreForHost } from '@volter/sdk/kit/shell-store-door';
55
+ import { DOCUMENT_REGISTRATION_TIMEOUT_MS, waitUntil } from '../wait-until';
56
+ import {
57
+ activeWorkspaceDocumentId,
58
+ openWorkspaceDocument,
59
+ setWorkspaceDocumentTitle,
60
+ type WorkspaceDocumentContentProps,
61
+ type WorkspaceDocumentDescriptor,
62
+ workspaceDocumentSelection,
63
+ } from '@volter/sdk/kit/workspace-document-registry';
64
+ import { registerWorkspaceDocumentRestorer } from '@volter/sdk/kit/workspace-document-restore';
65
+
66
+ import { MediaProperties } from './MediaProperties';
67
+
68
+ const AssetEditorShell = lazy(async () => {
69
+ const module = await import('@volter/sdk/kit/components/AssetEditorShell');
70
+ return { default: module.AssetEditorShell };
71
+ });
72
+ const AudioViewer = lazy(async () => {
73
+ const module = await import('./asset-viewers/AudioViewer');
74
+ return { default: module.AudioViewer };
75
+ });
76
+ const ImageViewer = lazy(async () => {
77
+ const module = await import('./asset-viewers/ImageViewer');
78
+ return { default: module.ImageViewer };
79
+ });
80
+ const VideoViewer = lazy(async () => {
81
+ const module = await import('./asset-viewers/VideoViewer');
82
+ return { default: module.VideoViewer };
83
+ });
84
+ const JsonAssetDocument = lazy(async () => {
85
+ const module = await import('./asset-viewers/JsonAssetDocument');
86
+ return { default: module.JsonAssetDocument };
87
+ });
88
+ const OnlineAssetDetail = lazy(async () => {
89
+ const module = await import('./asset-viewers/OnlineAssetDetail');
90
+ return { default: module.OnlineAssetDetail };
91
+ });
92
+ const SourceAssetViewer = lazy(async () => {
93
+ const module = await import('./asset-viewers/SourceAssetViewer');
94
+ return { default: module.SourceAssetViewer };
95
+ });
96
+
97
+ /** What one open asset document is showing — the former `AssetTab` shape,
98
+ * now keyed by workspace-document id. */
99
+ export interface AssetDocumentSpec {
100
+ readonly assetPath: string;
101
+ readonly kind: AssetKind;
102
+ /** Entity source for a session-only Asset Editor document. Never serialized. */
103
+ readonly entityId?: string;
104
+ /** Present while this document shows an online (not-yet-downloaded) asset. */
105
+ readonly online?: OnlineAssetInfo;
106
+ readonly unavailableReason?: string;
107
+ /** Incremented when the file bytes change at the same path. */
108
+ readonly revision?: number;
109
+ /** Display metadata for an adapter-provided in-memory asset (for example inline SVG). */
110
+ readonly displayName?: string;
111
+ readonly sourcePath?: string;
112
+ }
113
+
114
+ export interface AssetDocumentMutation {
115
+ type: 'move' | 'delete' | 'change';
116
+ oldPath?: string;
117
+ newPath?: string;
118
+ paths?: readonly string[];
119
+ }
120
+
121
+ // Vite's HMR context when a dev server serves this module; the SDK carries no bundler types.
122
+ const hotData = (import.meta as ImportMeta & { hot?: { data: Record<string, unknown> } }).hot?.data;
123
+ const _specs =
124
+ (hotData?.['volter:asset-document-specs'] as Map<string, AssetDocumentSpec> | undefined) ??
125
+ new Map<string, AssetDocumentSpec>();
126
+ const _pathAliases =
127
+ (hotData?.['volter:asset-document-path-aliases'] as Map<string, string> | undefined) ??
128
+ new Map<string, string>();
129
+ const _focusReturnTargets = new Map<string, HTMLElement>();
130
+ if (hotData) {
131
+ // Workspace descriptors intentionally survive compatible Fast Refreshes.
132
+ // Their specs must survive with them or the next persistence snapshot drops
133
+ // the open document and leaves a titled-but-empty tab on reload.
134
+ hotData['volter:asset-document-specs'] = _specs;
135
+ hotData['volter:asset-document-path-aliases'] = _pathAliases;
136
+ }
137
+ let _version = 0;
138
+ const _listeners = new Set<() => void>();
139
+
140
+ function notifyChanged(): void {
141
+ _version++;
142
+ for (const fn of _listeners) fn();
143
+ }
144
+
145
+ /** Subscribe to spec changes (online-asset resolution). Returns unsubscribe. */
146
+ export function subscribeAssetDocuments(fn: () => void): () => void {
147
+ _listeners.add(fn);
148
+ return () => {
149
+ _listeners.delete(fn);
150
+ };
151
+ }
152
+
153
+ /** Monotonic change counter — the `getSnapshot` for `useSyncExternalStore`. */
154
+ export function assetDocumentsVersion(): number {
155
+ return _version;
156
+ }
157
+
158
+ /** The spec behind an open asset document id, or `undefined`. */
159
+ export function assetDocumentSpec(id: string): AssetDocumentSpec | undefined {
160
+ return _specs.get(id);
161
+ }
162
+
163
+ /** Whether `id` is one of this module's asset documents (used by
164
+ * `command-listener.ts` to report the legacy `activeTabKey` facet). */
165
+ export function isAssetDocumentId(id: string): boolean {
166
+ return _specs.has(id);
167
+ }
168
+
169
+ /** Shallow equality over the union of both specs' keys — the specs are flat
170
+ * records of primitives plus two small objects, and the only question asked
171
+ * of them is "did the request ask for something different". */
172
+ function sameSpec(a: AssetDocumentSpec, b: AssetDocumentSpec): boolean {
173
+ const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
174
+ for (const key of keys) {
175
+ const left = (a as unknown as Record<string, unknown>)[key];
176
+ const right = (b as unknown as Record<string, unknown>)[key];
177
+ if (left === right) continue;
178
+ if (JSON.stringify(left) !== JSON.stringify(right)) return false;
179
+ }
180
+ return true;
181
+ }
182
+
183
+ function openSpecDocument(
184
+ id: string,
185
+ title: string,
186
+ spec: AssetDocumentSpec,
187
+ provenance?: WorkspaceDocumentDescriptor['provenance'],
188
+ options: { activate?: boolean } = {},
189
+ ): string {
190
+ if (typeof document !== 'undefined' && document.activeElement instanceof HTMLElement) {
191
+ _focusReturnTargets.set(id, document.activeElement);
192
+ }
193
+ const existing = _specs.get(id);
194
+ if (!existing) {
195
+ _specs.set(id, spec);
196
+ notifyChanged();
197
+ } else {
198
+ // FIRST SPEC NO LONGER WINS. Re-opening the same path with a DIFFERENT
199
+ // kind used to be a silent no-op: the id IS the path, the spec was written
200
+ // once, and every later request was dropped on the floor while the call
201
+ // still acked. `editor.openAsset('/x.json', 'source')` therefore handed
202
+ // back the JSON viewer and reported success.
203
+ //
204
+ // The honest resolution is UPDATE, not refuse, because of what the two
205
+ // halves of this identity are: the PATH is the document's identity (the id
206
+ // is literally the path) and the KIND is a VIEW of that file. A second
207
+ // view of the same file is an ordinary request, not a collision — refusing
208
+ // it would mean a file already open can only be re-viewed by closing it
209
+ // first, a modal rule the workspace has nowhere to show. Layout
210
+ // persistence agrees: it serializes whatever the spec CURRENTLY says
211
+ // (`{path, assetKind}`) and restores by calling this function against an
212
+ // empty registry, so an updated spec round-trips exactly and refusal would
213
+ // never fire on that path anyway.
214
+ //
215
+ // The request wins key-by-key; fields the request does not speak for
216
+ // (`revision`, `unavailableReason` — facts about the FILE at that path,
217
+ // maintained by `applyAssetDocumentMutation`) survive.
218
+ const merged = { ...existing, ...spec };
219
+ if (!sameSpec(existing, merged)) {
220
+ _specs.set(id, merged);
221
+ notifyChanged();
222
+ }
223
+ }
224
+ const descriptor: WorkspaceDocumentDescriptor = {
225
+ id,
226
+ title,
227
+ kind: 'asset',
228
+ workspaceRole: 'authored-subject',
229
+ ...(provenance ? { provenance } : {}),
230
+ Content: AssetDocumentContent,
231
+ closeable: true,
232
+ presentation: () => {
233
+ const current = _specs.get(id);
234
+ if (!current) return null;
235
+ if (current.entityId)
236
+ return { kind: 'asset', entityId: current.entityId, assetKind: 'model' };
237
+ if (!current.assetPath || current.online) return null;
238
+ return {
239
+ kind: 'asset',
240
+ path: current.assetPath,
241
+ assetKind: current.kind,
242
+ };
243
+ },
244
+ // Only a real FILE-BACKED view restores: a live-entity Asset Editor
245
+ // session and an unresolved online asset are not truthfully re-resolvable
246
+ // from a cold boot, so they return nothing and the host stores nothing.
247
+ persist: () => {
248
+ const current = _specs.get(id);
249
+ if (!current?.assetPath || current.online || current.entityId !== undefined) return undefined;
250
+ return { path: current.assetPath, assetKind: current.kind };
251
+ },
252
+ onActivate: () => {
253
+ if (spec.kind === 'image' || spec.kind === 'video' || spec.kind === 'audio')
254
+ clearSelectedAsset();
255
+ },
256
+ onDispose: (documentId) => {
257
+ if (_specs.delete(documentId)) notifyChanged();
258
+ const focusTarget = _focusReturnTargets.get(documentId);
259
+ _focusReturnTargets.delete(documentId);
260
+ if (focusTarget?.isConnected) setTimeout(() => focusTarget.focus(), 0);
261
+ },
262
+ };
263
+ return openWorkspaceDocument(descriptor, options);
264
+ }
265
+
266
+ /**
267
+ * Open a project asset as a center document (double-click/Enter in the asset
268
+ * browser, `open-asset-tab` control command). Re-opening an already-open
269
+ * path activates the existing document (registry dedupe) — and re-opening it
270
+ * with a DIFFERENT kind re-points that document's spec at the requested view
271
+ * rather than silently keeping the first one (see `openSpecDocument`).
272
+ */
273
+ export function openAssetDocument(
274
+ assetPath: string,
275
+ kind: AssetKind,
276
+ options: { activate?: boolean } = {},
277
+ ): string {
278
+ const title = spritesheetFrameTitle(assetPath);
279
+ const { frameName } = splitSpritesheetAssetPath(assetPath);
280
+ const id = _pathAliases.get(normalizeAssetPath(assetPath)) ?? assetPath;
281
+ return openSpecDocument(id,
282
+ title,
283
+ frameName ? { assetPath, kind, displayName: title } : { assetPath, kind },
284
+ { sourcePath: assetPath },
285
+ options,
286
+ );
287
+ }
288
+
289
+ /**
290
+ * Reopen a persisted asset document. Verified with an EXISTENCE-ONLY probe
291
+ * (`projectFileExists` — a backend stat in browser-hosted projects, an HTTP
292
+ * HEAD on the local server): a deleted asset resolves false and the document
293
+ * is dropped. Deliberately not `readProjectTextFile`, which downloads the
294
+ * whole asset body just to throw it away here.
295
+ */
296
+ registerWorkspaceDocumentRestorer({
297
+ kind: 'asset',
298
+ owner: 'asset-documents',
299
+ restore: async ({ state }) => {
300
+ const record = state as { path?: unknown; assetKind?: unknown } | null | undefined;
301
+ const path = typeof record?.path === 'string' ? record.path : null;
302
+ const assetKind = typeof record?.assetKind === 'string' ? record.assetKind : null;
303
+ if (!path || !assetKind) return false;
304
+ if (!(await projectFileExists(path))) return false;
305
+ openAssetDocument(path, assetKind as AssetKind);
306
+ return true;
307
+ },
308
+ });
309
+
310
+ /**
311
+ * THE `asset` ADDRESS (`document-open-registry.ts`) — an asset at a path, or
312
+ * the Asset Editor over a live scene entity. Registered at module load, the
313
+ * shape `packages/editor-game/src/three/story-documents/three-story-documents.tsx:239`
314
+ * uses; what an extension means, and what `@volter/sdk`'s published view
315
+ * kinds map onto internally, are this module's rules and the presenter no
316
+ * longer holds either.
317
+ *
318
+ * `ready` (the post-open half of `settle`) is why the address exists as more
319
+ * than a call: an asset document answers its id the moment its tab is
320
+ * registered, while `present` must not return until the Inspector has mounted
321
+ * — and for a MODEL, until its live Object3D adapter has replaced the identity
322
+ * floor, or an immediate inspect/setField sees a mounted tab whose graph is
323
+ * not ready. That rule is the asset family's; the host used to await it by
324
+ * hand because it knew the rule.
325
+ */
326
+ /**
327
+ * THE ASSET DOCUMENT'S READINESS — this family's rule, and a DOOR a caller
328
+ * outside the view protocol holds besides the `ready` below
329
+ * (`command-listener.ts`'s `open-asset-tab`). It lived in `editor-view-presentation.ts` while the presenter did
330
+ * the waiting by hand; the presenter holds no kind's rule any more, so the
331
+ * rule lives beside the documents it describes.
332
+ */
333
+ export async function waitForAssetDocumentInspector(
334
+ documentId: string,
335
+ kind: AssetKind,
336
+ ): Promise<boolean> {
337
+ return waitUntil(() => {
338
+ if (activeWorkspaceDocumentId() !== documentId) return false;
339
+ const selection = workspaceDocumentSelection(documentId);
340
+ if (!selection) return false;
341
+ // Every asset shell publishes its own identity once mounted. A model has
342
+ // a stronger readiness bar: its live Object3D adapter must have replaced
343
+ // that identity floor, otherwise an immediate inspect/setField would see
344
+ // a mounted tab whose graph and project authoring contribution are not
345
+ // ready yet.
346
+ return kind !== 'model' || selection.adapter !== null;
347
+ }, DOCUMENT_REGISTRATION_TIMEOUT_MS);
348
+ }
349
+
350
+ /**
351
+ * THE `asset-editor:entity` ADDRESS — the Asset Editor document over a live entity a medium has
352
+ * already resolved (the Three integration's inspector jump names the entity and its title). The
353
+ * `asset` address's entity form asks the medium; this is what the medium asks back.
354
+ */
355
+ registerDocumentOpener<{ readonly entityId: string; readonly title: string }>({
356
+ id: 'asset-editor:entity',
357
+ owner: 'asset-documents',
358
+ open: (_store, { entityId, title }) =>
359
+ openSpecDocument(
360
+ `asset-editor:entity:${entityId}`,
361
+ title,
362
+ { assetPath: '', kind: 'model', entityId },
363
+ { origin: `entity:${entityId}` },
364
+ ),
365
+ });
366
+
367
+ registerDocumentOpener<{
368
+ readonly path?: string;
369
+ readonly assetKind?: string;
370
+ readonly entityId?: string;
371
+ }>({
372
+ id: 'asset',
373
+ owner: 'asset-documents',
374
+ open: (_store, request) => {
375
+ if (request.entityId) {
376
+ // A live entity's Asset Editor document is the medium's to open: the one
377
+ // that draws it answers whether it has a model to show
378
+ // (`@volter/sdk/kit/inspection-node-media`).
379
+ const shell = shellStoreForHost();
380
+ if (!shell) return null;
381
+ const id = inspectionNodeMedia(getActiveAuthoring(shell), request.entityId)?.assetDocument?.open() ?? null;
382
+ if (!id) throw new Error(`Scene entity is not available: ${request.entityId}`);
383
+ return id;
384
+ }
385
+ const path = request.path;
386
+ if (path === undefined) return null;
387
+ return openAssetDocument(path, requestedAssetKind(request.assetKind, path));
388
+ },
389
+ ready: async (documentId, request) => {
390
+ const kind: AssetKind = request.entityId
391
+ ? 'model'
392
+ : requestedAssetKind(request.assetKind, request.path ?? '');
393
+ if (!(await waitForAssetDocumentInspector(documentId, kind)))
394
+ throw new Error(`Asset document did not finish mounting its Inspector: ${documentId}`);
395
+ // AND ITS STAGE, where it mounted one. The KIND does not decide that and
396
+ // this module must not pretend it does: a `model` always mounts one, a
397
+ // `source` asset only when its module is a model builder
398
+ // (`asset-viewers/SourceAssetViewer.tsx:256`) and a `json` only when its
399
+ // content is a three.quarks system (`asset-viewers/JsonAssetDocument.tsx:64`),
400
+ // so the stage's own announcement is what answers. The Inspector wait above
401
+ // is what makes an absent announcement mean "no stage" rather than "not
402
+ // yet": the shell that would have rendered one has already mounted.
403
+ await awaitAnnouncedObject3DDocumentSession(documentId);
404
+ },
405
+ });
406
+
407
+ /**
408
+ * The internal view kind an address asks for. `@volter/sdk`'s public
409
+ * view-link contract still carries 'animation' and 'prefab' kinds that have no
410
+ * internal counterpart; exactly those remap to their closest surviving
411
+ * internal kinds. Unnamed falls back to what the extension says.
412
+ */
413
+ function requestedAssetKind(requested: string | undefined, path: string): AssetKind {
414
+ if (requested === 'animation') return 'source';
415
+ if (requested === 'prefab') return 'json';
416
+ return (requested as AssetKind | undefined) ?? inferAssetKind(path);
417
+ }
418
+
419
+ /** What an extension means. The asset browser's own vocabulary, which is why
420
+ * it lives beside the documents it opens rather than in the presenter. */
421
+ function inferAssetKind(path: string): AssetKind {
422
+ const ext = path.slice(path.lastIndexOf('.') + 1).toLowerCase();
423
+ if (ext === 'glb' || ext === 'gltf') return 'model';
424
+ if (['png', 'jpg', 'jpeg', 'webp', 'gif', 'svg'].includes(ext)) return 'image';
425
+ if (['mp3', 'ogg', 'wav', 'flac'].includes(ext)) return 'audio';
426
+ if (['glsl', 'vert', 'frag', 'ts', 'tsx', 'js', 'jsx', 'css', 'html', 'md', 'txt'].includes(ext))
427
+ return 'source';
428
+ return 'json';
429
+ }
430
+
431
+
432
+ /** Open an adapter-owned atomic asset that is embedded in a source document. */
433
+ export function openAuthoringAssetDocument(
434
+ entityId: string,
435
+ subject: AuthoringAssetSubject,
436
+ ): string {
437
+ const assetPath = authoringAssetDataUrl(subject);
438
+ return openSpecDocument(`asset-editor:subject:${entityId}`,
439
+ subject.name,
440
+ {
441
+ assetPath,
442
+ kind: subject.kind,
443
+ displayName: subject.name,
444
+ ...(subject.sourcePath ? { sourcePath: subject.sourcePath } : {}),
445
+ },
446
+ subject.sourcePath ? { sourcePath: subject.sourcePath } : { origin: `entity:${entityId}` },
447
+ );
448
+ }
449
+
450
+ /** Open an online-library asset's detail/download document. */
451
+ export function openOnlineAssetDocument(online: OnlineAssetInfo): void {
452
+ const kind: AssetKind =
453
+ online.type === 'model'
454
+ ? 'model'
455
+ : online.type === 'animation' || online.type === 'source'
456
+ ? 'source'
457
+ : 'image';
458
+ openSpecDocument(`online:${online.source}:${online.id}`,
459
+ online.name,
460
+ { assetPath: '', kind, online },
461
+ { origin: `online:${online.source}:${online.id}` },
462
+ );
463
+ }
464
+
465
+ /** Point an online asset document at its downloaded local file — the local
466
+ * viewer takes over in place (same document id/title). */
467
+ export function resolveOnlineAssetDocument(id: string, localPath: string): void {
468
+ const spec = _specs.get(id);
469
+ if (!spec) return;
470
+ const { online: _dropped, ...rest } = spec;
471
+ _specs.set(id, { ...rest, assetPath: localPath });
472
+ notifyChanged();
473
+ }
474
+
475
+ /** Test-only reset — drops all specs without touching the document registry
476
+ * (pair it with `__resetWorkspaceDocumentRegistryForTest`). */
477
+ export function __resetAssetDocumentsForTest(): void {
478
+ _specs.clear();
479
+ _pathAliases.clear();
480
+ _focusReturnTargets.clear();
481
+ notifyChanged();
482
+ }
483
+
484
+ /** Keep a live editor bound to asset identity while its source path changes. */
485
+ export function applyAssetDocumentMutation(
486
+ documentId: string,
487
+ detail: AssetDocumentMutation,
488
+ ): void {
489
+ const spec = _specs.get(documentId);
490
+ if (!spec?.assetPath) return;
491
+ const current = normalizeAssetPath(spec.assetPath);
492
+ if (
493
+ detail.type === 'move' &&
494
+ detail.oldPath &&
495
+ detail.newPath &&
496
+ current === normalizeAssetPath(detail.oldPath)
497
+ ) {
498
+ const newPath = `/${normalizeAssetPath(detail.newPath)}`;
499
+ _specs.set(documentId, { ...spec, assetPath: newPath });
500
+ _pathAliases.set(normalizeAssetPath(newPath), documentId);
501
+ setWorkspaceDocumentTitle(documentId, newPath.split('/').pop() ?? newPath);
502
+ notifyChanged();
503
+ } else if (
504
+ detail.type === 'delete' &&
505
+ detail.paths?.some((path) => normalizeAssetPath(path) === current)
506
+ ) {
507
+ _specs.set(documentId, {
508
+ ...spec,
509
+ unavailableReason: `The source ${spec.assetPath} was deleted. Undo the deletion or close this editor.`,
510
+ });
511
+ notifyChanged();
512
+ } else if (
513
+ detail.type === 'change' &&
514
+ detail.paths?.some((path) => normalizeAssetPath(path) === current)
515
+ ) {
516
+ _specs.set(documentId, { ...spec, revision: (spec.revision ?? 0) + 1 });
517
+ notifyChanged();
518
+ }
519
+ }
520
+
521
+ export type AssetDocumentViewerRoute =
522
+ | 'online'
523
+ | 'model'
524
+ | 'environment'
525
+ | 'image'
526
+ | 'video'
527
+ | 'audio'
528
+ | 'source'
529
+ | 'json'
530
+ | 'unsupported';
531
+
532
+ /** Pure routing decision, exported so every recognized asset kind is test-pinned. */
533
+ export function assetDocumentViewerRoute(spec: AssetDocumentSpec): AssetDocumentViewerRoute {
534
+ if (spec.online) return 'online';
535
+ if (assetCapabilities(spec.assetPath).editor === 'environment') return 'environment';
536
+ if (spec.kind === 'json')
537
+ return 'json';
538
+ if (['model', 'image', 'video', 'audio', 'source'].includes(spec.kind))
539
+ return spec.kind as AssetDocumentViewerRoute;
540
+ return 'unsupported';
541
+ }
542
+
543
+ /** Where this asset CAME FROM — the document's own provenance block, one of
544
+ * the sections it publishes as its subject
545
+ * (`components/AssetEditorShell.tsx`). */
546
+ function sourceSection(spec: AssetDocumentSpec): InspectionSection {
547
+ return {
548
+ id: 'source',
549
+ title: 'Source',
550
+ icon: faFileLines,
551
+ order: PROPERTIES_SECTION_ORDER,
552
+ body: {
553
+ kind: 'custom',
554
+ render: () => <div style={{ padding: 4 }}>{spec.sourcePath ?? spec.assetPath}</div>,
555
+ },
556
+ };
557
+ }
558
+
559
+ /** Routes a spec to its center viewer/editor. */
560
+ function AssetViewerBody({
561
+ id,
562
+ spec,
563
+ active,
564
+ }: {
565
+ id: string;
566
+ spec: AssetDocumentSpec;
567
+ active: boolean;
568
+ }) {
569
+ if (spec.unavailableReason) {
570
+ return (
571
+ <AssetEditorShell
572
+ documentId={id}
573
+ active={active}
574
+ type={spec.kind}
575
+ title={spec.assetPath.split('/').pop() || 'Deleted asset'}
576
+ status="Source unavailable"
577
+ >
578
+ <div style={{ padding: 20, color: danger }}>{spec.unavailableReason}</div>
579
+ </AssetEditorShell>
580
+ );
581
+ }
582
+ const route = assetDocumentViewerRoute(spec);
583
+ if (route === 'online' && spec.online) {
584
+ const online = spec.online;
585
+ return (
586
+ <AssetEditorShell
587
+ documentId={id}
588
+ active={active}
589
+ type="online"
590
+ title={online.name}
591
+ sections={[
592
+ {
593
+ id: 'online-asset',
594
+ title: 'Asset',
595
+ icon: faCloudArrowDown,
596
+ order: PROPERTIES_SECTION_ORDER,
597
+ body: {
598
+ kind: 'custom',
599
+ render: () => (
600
+ <OnlineAssetDetail
601
+ online={online}
602
+ mode="inspector"
603
+ onResolved={(localPath) => resolveOnlineAssetDocument(id, localPath)}
604
+ />
605
+ ),
606
+ },
607
+ },
608
+ ]}
609
+ status={`${online.type} · ${online.source}`}
610
+ >
611
+ <OnlineAssetDetail online={online} mode="preview" />
612
+ </AssetEditorShell>
613
+ );
614
+ }
615
+ const title = (spec.displayName ?? spec.assetPath.split('/').pop()) || spec.entityId || 'Asset';
616
+ const viewerKey = `${id}:${spec.revision ?? 0}`;
617
+ // A model and an environment map are shown by the media integration that
618
+ // renders them (`@volter/sdk/kit/asset-viewers`).
619
+ const noViewer = (
620
+ <div style={{ padding: 20, color: danger }}>No editor package in this project renders {title}.</div>
621
+ );
622
+ if (route === 'model')
623
+ if (spec.assetPath)
624
+ return (
625
+ <AssetViewerSlot
626
+ route="model"
627
+ key={viewerKey}
628
+ documentId={id}
629
+ assetPath={spec.assetPath}
630
+ displayName={title}
631
+ active={active}
632
+ whenUnregistered={noViewer}
633
+ />
634
+ );
635
+ else if (spec.entityId)
636
+ return (
637
+ <AssetViewerSlot
638
+ route="entity-model"
639
+ key={viewerKey}
640
+ documentId={id}
641
+ assetPath=""
642
+ entityId={spec.entityId}
643
+ displayName={title}
644
+ active={active}
645
+ whenUnregistered={noViewer}
646
+ />
647
+ );
648
+ else return <div style={{ padding: 20, color: danger }}>Model source is unavailable.</div>;
649
+ if (route === 'environment')
650
+ return (
651
+ <AssetViewerSlot
652
+ route="environment"
653
+ key={viewerKey}
654
+ documentId={id}
655
+ assetPath={spec.assetPath}
656
+ displayName={title}
657
+ active={active}
658
+ whenUnregistered={noViewer}
659
+ />
660
+ );
661
+ if (route === 'source')
662
+ return (
663
+ <SourceAssetViewer
664
+ key={viewerKey}
665
+ documentId={id}
666
+ assetPath={spec.assetPath}
667
+ active={active}
668
+ />
669
+ );
670
+ // A .json is content-routed: three.quarks names no extension of its own, so
671
+ // only the file's own structure can say whether this is a particle document
672
+ // or ordinary JSON. `JsonAssetDocument` owns the shell for both answers.
673
+ if (route === 'json')
674
+ return (
675
+ <JsonAssetDocument
676
+ key={viewerKey}
677
+ documentId={id}
678
+ assetPath={spec.assetPath}
679
+ displayName={title}
680
+ active={active}
681
+ sections={[sourceSection(spec)]}
682
+ status={`json · ${spec.sourcePath ?? spec.displayName ?? spec.assetPath}`}
683
+ />
684
+ );
685
+ let viewer: ReactNode;
686
+ switch (route) {
687
+ case 'image':
688
+ viewer = (
689
+ <ImageViewer
690
+ key={viewerKey}
691
+ assetPath={spec.assetPath}
692
+ {...(spec.displayName ? { displayName: spec.displayName } : {})}
693
+ />
694
+ );
695
+ break;
696
+ case 'video':
697
+ viewer = <VideoViewer key={viewerKey} assetPath={spec.assetPath} />;
698
+ break;
699
+ case 'audio':
700
+ viewer = <AudioViewer key={viewerKey} assetPath={spec.assetPath} />;
701
+ break;
702
+ default:
703
+ viewer = (
704
+ <div style={{ padding: 16, color: text[2], fontSize: 12 }}>
705
+ No viewer available for this asset type.
706
+ </div>
707
+ );
708
+ }
709
+ return (
710
+ <AssetEditorShell
711
+ documentId={id}
712
+ active={active}
713
+ type={route}
714
+ title={title}
715
+ fill={route === 'image' || route === 'video'}
716
+ sections={
717
+ (route === 'image' && !spec.assetPath.includes('#')) ||
718
+ route === 'video' ||
719
+ route === 'audio'
720
+ ? [
721
+ {
722
+ id: 'media-properties',
723
+ title: 'Properties',
724
+ icon: faFileLines,
725
+ order: PROPERTIES_SECTION_ORDER,
726
+ body: {
727
+ kind: 'custom',
728
+ render: () => (
729
+ <MediaProperties
730
+ key={viewerKey}
731
+ assetPath={spec.assetPath}
732
+ kind={route as 'image' | 'video' | 'audio'}
733
+ />
734
+ ),
735
+ },
736
+ },
737
+ ...(spec.sourcePath ? [sourceSection(spec)] : []),
738
+ ]
739
+ : [sourceSection(spec)]
740
+ }
741
+ status={`${route} · ${spec.sourcePath ?? spec.displayName ?? spec.assetPath}`}
742
+ >
743
+ {viewer}
744
+ </AssetEditorShell>
745
+ );
746
+ }
747
+
748
+ /**
749
+ * Full-size type-specific Asset Editor document host.
750
+ */
751
+ export function AssetDocumentContent({ documentId, active }: WorkspaceDocumentContentProps) {
752
+ useSyncExternalStore(subscribeAssetDocuments, assetDocumentsVersion);
753
+ useEffect(() => {
754
+ const handleMutation = (rawEvent: Event) => {
755
+ applyAssetDocumentMutation(
756
+ documentId,
757
+ (rawEvent as CustomEvent<AssetDocumentMutation>).detail,
758
+ );
759
+ };
760
+ window.addEventListener('editor:asset-mutation', handleMutation);
761
+ return () => window.removeEventListener('editor:asset-mutation', handleMutation);
762
+ }, [documentId]);
763
+ const spec = _specs.get(documentId);
764
+ return (
765
+ <div
766
+ data-testid={`asset-document:${documentId}`}
767
+ style={{
768
+ flex: 1,
769
+ minHeight: 0,
770
+ overflow: 'hidden',
771
+ pointerEvents: 'auto',
772
+ background: bg[1],
773
+ }}
774
+ >
775
+ {spec ? (
776
+ <Suspense
777
+ fallback={
778
+ <div style={{ padding: 16, color: text[2], fontSize: 12 }}>Loading asset viewer…</div>
779
+ }
780
+ >
781
+ <AssetViewerBody id={documentId} spec={spec} active={active} />
782
+ </Suspense>
783
+ ) : (
784
+ <div style={{ padding: 16, color: text[2], fontSize: 12 }}>
785
+ This asset document is no longer available.
786
+ </div>
787
+ )}
788
+ </div>
789
+ );
790
+ }
791
+
792
+ function normalizeAssetPath(path: string): string {
793
+ return path.split(/[?#]/, 1)[0]!.replace(/^\/+/, '');
794
+ }