@volter/editor-core 0.5.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (719) hide show
  1. package/BUNDLED_NOTICES +1527 -0
  2. package/LICENSE +686 -0
  3. package/LICENSE-APACHE +202 -0
  4. package/NOTICE +18 -0
  5. package/README.md +24 -0
  6. package/dist/build/chunk-JS4D4XZ3.js +175 -0
  7. package/dist/build/chunk-JS4D4XZ3.js.map +7 -0
  8. package/dist/build/vite-plugin-product-contributions.js +100 -0
  9. package/dist/build/vite-plugin-product-contributions.js.map +7 -0
  10. package/dist/build/vite-plugin-shared-react.js +25 -0
  11. package/dist/build/vite-plugin-shared-react.js.map +7 -0
  12. package/dist/build/vite-plugin-shared-three.js +86 -0
  13. package/dist/build/vite-plugin-shared-three.js.map +7 -0
  14. package/dist/server/chunk-KMJC7AGO.js +98 -0
  15. package/dist/server/chunk-KMJC7AGO.js.map +7 -0
  16. package/dist/server/chunk-PSILUMGS.js +46 -0
  17. package/dist/server/chunk-PSILUMGS.js.map +7 -0
  18. package/dist/server/frame-proxy.js +255 -0
  19. package/dist/server/frame-proxy.js.map +7 -0
  20. package/dist/server/open-browser.js +53 -0
  21. package/dist/server/open-browser.js.map +7 -0
  22. package/dist/server/process-shutdown.js +127 -0
  23. package/dist/server/process-shutdown.js.map +7 -0
  24. package/dist/server/session-registry.js +141 -0
  25. package/dist/server/session-registry.js.map +7 -0
  26. package/dist/server/spawn-opener.js +11 -0
  27. package/dist/server/spawn-opener.js.map +7 -0
  28. package/dist/server/worktree-identity.js +11 -0
  29. package/dist/server/worktree-identity.js.map +7 -0
  30. package/dist-server/packaged.mjs +88816 -0
  31. package/package.json +118 -0
  32. package/scripts/build-plugins.mjs +14 -0
  33. package/scripts/build-server.mjs +26 -0
  34. package/scripts/build-session.mjs +9 -0
  35. package/server/account-credentials.ts +60 -0
  36. package/server/account-service.ts +1434 -0
  37. package/server/adapter-region-includes.ts +47 -0
  38. package/server/asset-catalog-v2.ts +175 -0
  39. package/server/asset-history-snapshots.ts +196 -0
  40. package/server/asset-ledger-store.ts +195 -0
  41. package/server/asset-library-routes.ts +1595 -0
  42. package/server/blender-wasm-artifact.ts +430 -0
  43. package/server/boot-timings.ts +106 -0
  44. package/server/canonical-path.ts +50 -0
  45. package/server/checkout-workspace-preflight.ts +349 -0
  46. package/server/cloud-asset-catalog.ts +158 -0
  47. package/server/coding-inference-launch.ts +237 -0
  48. package/server/collaboration-account-client.ts +202 -0
  49. package/server/collaboration-attribution.ts +122 -0
  50. package/server/collaboration-session.ts +934 -0
  51. package/server/comfyui-bridge.ts +248 -0
  52. package/server/console-ledger.ts +499 -0
  53. package/server/creation-site-transform.ts +304 -0
  54. package/server/creation-site-write.ts +648 -0
  55. package/server/data-file-serialize.ts +158 -0
  56. package/server/editor-brand-html.ts +72 -0
  57. package/server/editor-control-socket.ts +366 -0
  58. package/server/editor-server-options.ts +190 -0
  59. package/server/editor-server.ts +1483 -0
  60. package/server/editor-sse.ts +352 -0
  61. package/server/engine-provenance.ts +169 -0
  62. package/server/engine-source-restart.ts +117 -0
  63. package/server/frame-bridge.ts +115 -0
  64. package/server/frame-proxy.ts +490 -0
  65. package/server/frame-workbench.ts +259 -0
  66. package/server/frontend-handoff.ts +229 -0
  67. package/server/game-globals-shadow.ts +113 -0
  68. package/server/gameplay-session-retention.ts +149 -0
  69. package/server/gameplay-sessions.ts +123 -0
  70. package/server/generation-jobs.ts +201 -0
  71. package/server/generation-reconciler.ts +137 -0
  72. package/server/generative-execution-context.ts +59 -0
  73. package/server/git-workflow.ts +612 -0
  74. package/server/harness-chat-caller.ts +36 -0
  75. package/server/harness-chat-service.ts +1681 -0
  76. package/server/idle-shutdown.ts +170 -0
  77. package/server/js-profiling-policy.ts +52 -0
  78. package/server/launcher-settings.ts +84 -0
  79. package/server/local-asset-catalog.ts +410 -0
  80. package/server/managed-account-defaults.ts +22 -0
  81. package/server/mock-control-plane.ts +540 -0
  82. package/server/model-import-conversion.ts +345 -0
  83. package/server/native-credential-store.ts +162 -0
  84. package/server/open-browser.ts +118 -0
  85. package/server/packaged.ts +1192 -0
  86. package/server/play-stall.ts +126 -0
  87. package/server/process-shutdown.ts +218 -0
  88. package/server/product-presets.ts +55 -0
  89. package/server/project-build-artifact.ts +95 -0
  90. package/server/project-components.ts +100 -0
  91. package/server/project-dependency-invalidation.ts +114 -0
  92. package/server/project-file-scan.ts +235 -0
  93. package/server/project-hmr-files.ts +215 -0
  94. package/server/project-install-roots.ts +210 -0
  95. package/server/project-kinds.ts +71 -0
  96. package/server/project-mcp-servers.ts +82 -0
  97. package/server/project-module-freshness.ts +109 -0
  98. package/server/project-module-instance.ts +375 -0
  99. package/server/project-optimize-deps-entries.ts +827 -0
  100. package/server/project-output-writer.ts +679 -0
  101. package/server/project-package-origin.ts +63 -0
  102. package/server/project-root-surface.ts +831 -0
  103. package/server/project-scratch-path.ts +47 -0
  104. package/server/project-script-hmr.ts +619 -0
  105. package/server/project-serving-plugins.ts +236 -0
  106. package/server/project-tools.ts +653 -0
  107. package/server/project-validation.ts +250 -0
  108. package/server/project-verbs.ts +127 -0
  109. package/server/project-view.ts +52 -0
  110. package/server/project-watch.ts +1134 -0
  111. package/server/project-work-coordinator.ts +288 -0
  112. package/server/provider-credentials.ts +331 -0
  113. package/server/recent-projects-store.ts +98 -0
  114. package/server/redact-secrets.ts +71 -0
  115. package/server/repository-presence.ts +267 -0
  116. package/server/routes/account.ts +357 -0
  117. package/server/routes/agents.ts +60 -0
  118. package/server/routes/assets.ts +154 -0
  119. package/server/routes/build.ts +13 -0
  120. package/server/routes/collaboration.ts +595 -0
  121. package/server/routes/configurations.ts +419 -0
  122. package/server/routes/context.ts +264 -0
  123. package/server/routes/control-plane.ts +1811 -0
  124. package/server/routes/logs.ts +272 -0
  125. package/server/routes/project-identity.ts +302 -0
  126. package/server/routes/project-open.ts +429 -0
  127. package/server/routes/project-source.ts +686 -0
  128. package/server/routes/project-state.ts +383 -0
  129. package/server/routes/relay.ts +1029 -0
  130. package/server/routes/route-helpers.ts +29 -0
  131. package/server/routes/served-modules.ts +138 -0
  132. package/server/routes/session-tabs.ts +196 -0
  133. package/server/routes/settings.ts +121 -0
  134. package/server/routes/share-control.ts +486 -0
  135. package/server/routes/themes.ts +116 -0
  136. package/server/routes/tools.ts +230 -0
  137. package/server/routes/worktrees.ts +595 -0
  138. package/server/scoped-game-css.ts +461 -0
  139. package/server/server-utils.ts +1194 -0
  140. package/server/session-product.ts +112 -0
  141. package/server/session-registry.ts +230 -0
  142. package/server/share-claims.ts +138 -0
  143. package/server/share-host.ts +526 -0
  144. package/server/share-session-gateway.ts +1362 -0
  145. package/server/share-tunnel.ts +123 -0
  146. package/server/spawn-opener.ts +74 -0
  147. package/server/support/play/log-format.ts +76 -0
  148. package/server/support/play/session-record.ts +199 -0
  149. package/server/support/project/build-discipline.ts +718 -0
  150. package/server/support/project/inspection-node.ts +119 -0
  151. package/server/support/project/log-naming.ts +54 -0
  152. package/server/support/project/provenance.ts +290 -0
  153. package/server/support/project/run-name.ts +18 -0
  154. package/server/support/project/session-journal.ts +830 -0
  155. package/server/support/project/shared.ts +327 -0
  156. package/server/tab-bootstrap.ts +62 -0
  157. package/server/tab-heartbeat.ts +477 -0
  158. package/server/tab-lifecycle.ts +550 -0
  159. package/server/tab-presence.ts +1508 -0
  160. package/server/team-agent-mirror.ts +95 -0
  161. package/server/twin-auth.ts +138 -0
  162. package/server/vendored-lock-recorder.ts +968 -0
  163. package/server/worktree-identity.ts +121 -0
  164. package/server/worktree-management.ts +398 -0
  165. package/server/worktree-retention.d.mts +13 -0
  166. package/server/worktree-retention.mjs +205 -0
  167. package/src/EditorContext.tsx +169 -0
  168. package/src/account.ts +333 -0
  169. package/src/action-registry.ts +532 -0
  170. package/src/active-product.ts +74 -0
  171. package/src/active-project.ts +155 -0
  172. package/src/adapter-editor-config.ts +25 -0
  173. package/src/adapter-observation.ts +49 -0
  174. package/src/animation/stage-transport.ts +379 -0
  175. package/src/animation/three-clips-subject.ts +172 -0
  176. package/src/api/asset-library-wire.ts +45 -0
  177. package/src/api/assets.ts +365 -0
  178. package/src/api/base.ts +10 -0
  179. package/src/api/build.ts +99 -0
  180. package/src/api/git-wire.ts +56 -0
  181. package/src/api/logs.ts +92 -0
  182. package/src/api/project-identity.ts +74 -0
  183. package/src/api/project-open.ts +355 -0
  184. package/src/api/project-source.ts +162 -0
  185. package/src/api/project-state.ts +107 -0
  186. package/src/api/relay.ts +268 -0
  187. package/src/api/settings.ts +36 -0
  188. package/src/api/themes.ts +45 -0
  189. package/src/api/worktrees.ts +257 -0
  190. package/src/asset-compare-core.ts +171 -0
  191. package/src/asset-compare.ts +294 -0
  192. package/src/asset-editor-context.tsx +100 -0
  193. package/src/asset-events.ts +97 -0
  194. package/src/asset-inspector-actions.ts +87 -0
  195. package/src/asset-preview-framing.ts +357 -0
  196. package/src/asset-preview.ts +2802 -0
  197. package/src/asset-selection-viewer-registry.ts +113 -0
  198. package/src/asset-selection.ts +133 -0
  199. package/src/asset-workflow/asset-capabilities.ts +344 -0
  200. package/src/asset-workflow/asset-import-jobs.ts +106 -0
  201. package/src/asset-workflow/asset-ledger-backend.ts +126 -0
  202. package/src/asset-workflow/asset-ledger.ts +156 -0
  203. package/src/asset-workflow/asset-materialization-report.ts +140 -0
  204. package/src/asset-workflow/asset-pack-manifest.ts +320 -0
  205. package/src/asset-workflow/asset-types.ts +142 -0
  206. package/src/asset-workflow/asset-workflow-quality.ts +61 -0
  207. package/src/asset-workflow/audio-preview-player.ts +193 -0
  208. package/src/asset-workflow/audio-waveform.ts +22 -0
  209. package/src/asset-workflow/cloud-asset-client.ts +263 -0
  210. package/src/asset-workflow/folder-preview.ts +345 -0
  211. package/src/asset-workflow/hosted-asset-materialization.ts +236 -0
  212. package/src/asset-workflow/image-view-scale.ts +32 -0
  213. package/src/asset-workflow/import-contract.ts +124 -0
  214. package/src/asset-workflow/ledger-write-lock.ts +244 -0
  215. package/src/asset-workflow/model-inspection.ts +818 -0
  216. package/src/asset-workflow/pixi-spritesheet.ts +197 -0
  217. package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
  218. package/src/asset-workflow/project-asset-commands.ts +23 -0
  219. package/src/asset-workflow/project-asset-health.ts +139 -0
  220. package/src/asset-workflow/project-asset-operations.ts +154 -0
  221. package/src/asset-workflow/project-asset-roots.ts +68 -0
  222. package/src/asset-workflow/project-content.ts +288 -0
  223. package/src/asset-workflow/project-source-index.ts +550 -0
  224. package/src/asset-workflow/thumbnail-system.ts +256 -0
  225. package/src/authoring/active-adapter.ts +199 -0
  226. package/src/authoring/active-systems.ts +422 -0
  227. package/src/authoring/adapter-key.ts +18 -0
  228. package/src/authoring/authoring-asset-url.ts +27 -0
  229. package/src/authoring/bootstrap-state.ts +49 -0
  230. package/src/authoring/boundary-authoring-adapter.ts +184 -0
  231. package/src/authoring/breakpoint-state.ts +43 -0
  232. package/src/authoring/canvas-scene-guides.ts +76 -0
  233. package/src/authoring/component-instance-root.ts +171 -0
  234. package/src/authoring/composite-authoring-adapter.ts +2110 -0
  235. package/src/authoring/consumer-actions.ts +520 -0
  236. package/src/authoring/css-numeric-style.ts +97 -0
  237. package/src/authoring/design-time-layers.ts +849 -0
  238. package/src/authoring/design-time-mount-registry.ts +235 -0
  239. package/src/authoring/design-time-settle.ts +343 -0
  240. package/src/authoring/edit-mode-authoring.ts +619 -0
  241. package/src/authoring/eyedropper-session.ts +60 -0
  242. package/src/authoring/instance-source-menu-register.ts +13 -0
  243. package/src/authoring/instance-source-menu.ts +135 -0
  244. package/src/authoring/layered-pick.ts +183 -0
  245. package/src/authoring/live-gesture-lock.ts +50 -0
  246. package/src/authoring/live-object-transform.ts +62 -0
  247. package/src/authoring/mount-failure-report.ts +154 -0
  248. package/src/authoring/mounted-root-subjects.ts +144 -0
  249. package/src/authoring/no-authoring-adapter.ts +55 -0
  250. package/src/authoring/null-inspection-subjects.tsx +75 -0
  251. package/src/authoring/object3d-document-persistence.ts +106 -0
  252. package/src/authoring/object3d-document-session-registry.ts +120 -0
  253. package/src/authoring/object3d-document-session.ts +1326 -0
  254. package/src/authoring/object3d-gesture-controller.ts +113 -0
  255. package/src/authoring/panel-authoring.ts +121 -0
  256. package/src/authoring/prefab-instance-inspector-section.tsx +237 -0
  257. package/src/authoring/prefab-instance-section-model.ts +55 -0
  258. package/src/authoring/project-authoring-session.ts +105 -0
  259. package/src/authoring/provenance.ts +99 -0
  260. package/src/authoring/quarks-particle-systems.ts +19 -0
  261. package/src/authoring/react-canvas-navigation.ts +255 -0
  262. package/src/authoring/react-design-canvas-style.ts +20 -0
  263. package/src/authoring/react-story-board.ts +937 -0
  264. package/src/authoring/selection-scope.ts +195 -0
  265. package/src/authoring/shell-document-ops.ts +169 -0
  266. package/src/authoring/shell-object3d-document-write-policy.ts +108 -0
  267. package/src/authoring/shell-viewport-policy.ts +48 -0
  268. package/src/authoring/source-object3d-authoring-adapter.ts +526 -0
  269. package/src/authoring/stories-scope.ts +35 -0
  270. package/src/authoring/story-board-chrome-fit.ts +107 -0
  271. package/src/authoring/story-board-presentation.ts +111 -0
  272. package/src/authoring/three-projection-core.ts +226 -0
  273. package/src/authoring/viewport-pick-context.ts +40 -0
  274. package/src/authoring/viewport-raycast.ts +240 -0
  275. package/src/authoring/viewport-tool-context.ts +73 -0
  276. package/src/authoring/world-canvas-viewport-state.ts +35 -0
  277. package/src/authoring/world-hidden-viewport.ts +152 -0
  278. package/src/authoring/world-pan-state.ts +198 -0
  279. package/src/authoring/world-session-state.ts +101 -0
  280. package/src/authoring/write-pipe.ts +173 -0
  281. package/src/availability-tick.ts +66 -0
  282. package/src/bitmap-label.ts +120 -0
  283. package/src/blender-tab-metrics.ts +161 -0
  284. package/src/board-open-actions.ts +20 -0
  285. package/src/boot-routing.ts +391 -0
  286. package/src/build-identity.ts +15 -0
  287. package/src/bytes-codec.ts +62 -0
  288. package/src/camera-authoring.ts +175 -0
  289. package/src/cancellation-reason.ts +58 -0
  290. package/src/canvas-preview-frames.ts +482 -0
  291. package/src/capture-camera-pose.ts +77 -0
  292. package/src/chrome-registry.ts +159 -0
  293. package/src/chrome-slot-registry.ts +91 -0
  294. package/src/collaboration-client.ts +264 -0
  295. package/src/collaboration-presence.ts +41 -0
  296. package/src/command-dispatch.ts +19 -0
  297. package/src/command-listener.ts +2743 -0
  298. package/src/command-registry.ts +70 -0
  299. package/src/component-board-registry.ts +205 -0
  300. package/src/component-states-registry.ts +200 -0
  301. package/src/components/AgentPresentationNotice.tsx +75 -0
  302. package/src/components/AlignToolbar.tsx +204 -0
  303. package/src/components/AppRoot.tsx +394 -0
  304. package/src/components/ApplicationMenus.tsx +415 -0
  305. package/src/components/AssetBrowser.tsx +2843 -0
  306. package/src/components/AssetEditorShell.tsx +216 -0
  307. package/src/components/AssetInspectorToolSection.tsx +124 -0
  308. package/src/components/BoardRulers.tsx +354 -0
  309. package/src/components/CameraInfo.tsx +81 -0
  310. package/src/components/CanvasSceneViewport.tsx +556 -0
  311. package/src/components/CapabilityCoverageSection.tsx +185 -0
  312. package/src/components/CenterDocuments.tsx +22 -0
  313. package/src/components/ChromeSlot.tsx +20 -0
  314. package/src/components/CodeView.tsx +470 -0
  315. package/src/components/CollaborationHeaderControl.css +74 -0
  316. package/src/components/CompactInspectorCard.tsx +190 -0
  317. package/src/components/CompactInspectorShell.tsx +39 -0
  318. package/src/components/ConsolePanel.css +55 -0
  319. package/src/components/ConsolePanel.tsx +270 -0
  320. package/src/components/DefaultEditorLayout.tsx +106 -0
  321. package/src/components/DocumentHeaderStrip.tsx +96 -0
  322. package/src/components/DocumentShelfRail.tsx +45 -0
  323. package/src/components/DocumentThumbnail.tsx +345 -0
  324. package/src/components/EditorLeaseGuard.tsx +372 -0
  325. package/src/components/ErrorBanner.tsx +123 -0
  326. package/src/components/FolderPreviewTile.tsx +159 -0
  327. package/src/components/GameHierarchy.tsx +3842 -0
  328. package/src/components/GameplaySessionTimeline.tsx +295 -0
  329. package/src/components/InspectionProjection.tsx +968 -0
  330. package/src/components/Inspector.tsx +233 -0
  331. package/src/components/InspectorCanvasPreview.tsx +35 -0
  332. package/src/components/InspectorFieldsSection.tsx +290 -0
  333. package/src/components/InspectorObjectPreview.tsx +57 -0
  334. package/src/components/InspectorStoriesSection.tsx +92 -0
  335. package/src/components/InspectorToolSection.tsx +96 -0
  336. package/src/components/InspectorTransformSection.tsx +245 -0
  337. package/src/components/LightExplorerPanel.tsx +433 -0
  338. package/src/components/MediaProperties.tsx +145 -0
  339. package/src/components/NonThreeAuthoringBootstrap.tsx +19 -0
  340. package/src/components/Object3DDocumentToolbar.css +100 -0
  341. package/src/components/Object3DDocumentToolbar.tsx +454 -0
  342. package/src/components/Object3DDocumentViewport.tsx +58 -0
  343. package/src/components/ProjectHeader.css +617 -0
  344. package/src/components/ProjectHeader.tsx +328 -0
  345. package/src/components/ProjectLayout.tsx +249 -0
  346. package/src/components/ReactCanvasControls.tsx +284 -0
  347. package/src/components/RootSelectionOverlay.tsx +3162 -0
  348. package/src/components/RootTextEditor.tsx +79 -0
  349. package/src/components/SaveStatus.tsx +70 -0
  350. package/src/components/StageHost.tsx +1954 -0
  351. package/src/components/StageOverlays.tsx +21 -0
  352. package/src/components/StartupErrorScreen.tsx +76 -0
  353. package/src/components/StartupLoadingScreen.tsx +65 -0
  354. package/src/components/StatsOverlay.tsx +78 -0
  355. package/src/components/SurfaceStateOverlay.tsx +24 -0
  356. package/src/components/ToolContributionSurfaces.tsx +65 -0
  357. package/src/components/ToolHost.tsx +370 -0
  358. package/src/components/ToolObject3DPreview.tsx +39 -0
  359. package/src/components/Toolbar.tsx +595 -0
  360. package/src/components/TransientHint.tsx +44 -0
  361. package/src/components/TransportStrip.tsx +174 -0
  362. package/src/components/VersionControlSection.tsx +470 -0
  363. package/src/components/VgaiLogo.css +83 -0
  364. package/src/components/VgaiLogo.tsx +35 -0
  365. package/src/components/ViewportControlsHint.tsx +60 -0
  366. package/src/components/ViewportFurniture.tsx +401 -0
  367. package/src/components/ViewportOverlay.tsx +145 -0
  368. package/src/components/ViewportOverlaysMenu.tsx +153 -0
  369. package/src/components/ViewportShadingMenu.tsx +300 -0
  370. package/src/components/ViewportViewMenu.tsx +101 -0
  371. package/src/components/WorkspaceDocumentSurface.tsx +204 -0
  372. package/src/components/WorkspaceUtilitySurface.tsx +22 -0
  373. package/src/components/WorktreeSwitcher.css +239 -0
  374. package/src/components/WorktreeSwitcher.tsx +890 -0
  375. package/src/components/account-documents.tsx +1162 -0
  376. package/src/components/asset-documents.tsx +812 -0
  377. package/src/components/asset-editor-persistence.ts +216 -0
  378. package/src/components/asset-selection-section.tsx +535 -0
  379. package/src/components/asset-thumbnails.tsx +312 -0
  380. package/src/components/asset-viewers/AudioViewer.tsx +201 -0
  381. package/src/components/asset-viewers/EntityModelDocument.tsx +122 -0
  382. package/src/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
  383. package/src/components/asset-viewers/GenericJsonViewer.tsx +102 -0
  384. package/src/components/asset-viewers/ImageViewer.tsx +300 -0
  385. package/src/components/asset-viewers/JsonAssetDocument.tsx +93 -0
  386. package/src/components/asset-viewers/LiveModuleDocument.tsx +417 -0
  387. package/src/components/asset-viewers/LutAssetDocument.tsx +444 -0
  388. package/src/components/asset-viewers/ModelAssetDocument.tsx +105 -0
  389. package/src/components/asset-viewers/Object3DPreview.tsx +359 -0
  390. package/src/components/asset-viewers/OnlineAssetDetail.tsx +421 -0
  391. package/src/components/asset-viewers/PasteboardModuleDocument.tsx +67 -0
  392. package/src/components/asset-viewers/QuarksAssetDocument.tsx +526 -0
  393. package/src/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
  394. package/src/components/asset-viewers/SourceAssetViewer.tsx +281 -0
  395. package/src/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
  396. package/src/components/asset-viewers/VideoViewer.tsx +101 -0
  397. package/src/components/asset-viewers/shader-source.ts +144 -0
  398. package/src/components/asset-workflow.css +731 -0
  399. package/src/components/board-guides.ts +150 -0
  400. package/src/components/compact-inspector.css +539 -0
  401. package/src/components/core-utilities.tsx +90 -0
  402. package/src/components/editor-notifications.css +40 -0
  403. package/src/components/engine-workspace.css +274 -0
  404. package/src/components/inspector-preview-section.tsx +238 -0
  405. package/src/components/inspector-property-grouping.ts +64 -0
  406. package/src/components/inspector-revert-label.ts +20 -0
  407. package/src/components/inspector-selection.ts +42 -0
  408. package/src/components/inspector-stories-gating.ts +171 -0
  409. package/src/components/inspector-transform-subject.ts +11 -0
  410. package/src/components/inspector-transform.ts +75 -0
  411. package/src/components/kind-documents.tsx +500 -0
  412. package/src/components/palette-action-publisher.tsx +140 -0
  413. package/src/components/primitives/DraftColorInput.tsx +74 -0
  414. package/src/components/product-shell.css +1829 -0
  415. package/src/components/project-tool-documents.tsx +410 -0
  416. package/src/components/scene-documents.tsx +223 -0
  417. package/src/components/stage-keyboard.tsx +37 -0
  418. package/src/components/stage-overlay-set.tsx +107 -0
  419. package/src/components/stage-presence-markers.ts +478 -0
  420. package/src/components/standard-viewport-dressing.ts +417 -0
  421. package/src/components/status-contributions.tsx +403 -0
  422. package/src/components/tool-documents.tsx +309 -0
  423. package/src/components/tool-schema-form.tsx +262 -0
  424. package/src/components/use-after-paint.ts +41 -0
  425. package/src/components/use-project-image-assets.ts +86 -0
  426. package/src/components/viewport-header-controls.css +90 -0
  427. package/src/components/viewport-surface-status.tsx +55 -0
  428. package/src/components/workspace-history.ts +32 -0
  429. package/src/components/workspace-static-panel-registry.tsx +152 -0
  430. package/src/components/workspace-surfaces.css +621 -0
  431. package/src/components/world-documents.tsx +586 -0
  432. package/src/components/world-overlay-gestures.ts +1694 -0
  433. package/src/composite-screenshot.ts +1641 -0
  434. package/src/console-sync.ts +131 -0
  435. package/src/constraint-helper.ts +338 -0
  436. package/src/content-entry-source-registry.ts +184 -0
  437. package/src/coverage/authoring-seam-evidence.ts +300 -0
  438. package/src/coverage/canvas-reveal.ts +192 -0
  439. package/src/coverage/design-time-surfaces.ts +101 -0
  440. package/src/coverage/live-seam-evidence.ts +11 -0
  441. package/src/coverage/ontology-invariants.ts +466 -0
  442. package/src/coverage/session-vitals.ts +501 -0
  443. package/src/coverage/system-seam-evidence.ts +72 -0
  444. package/src/crash-null-boundary.ts +36 -0
  445. package/src/creation-site-edit.ts +1479 -0
  446. package/src/creation-site-registry.ts +160 -0
  447. package/src/delegate-harness-registry.ts +143 -0
  448. package/src/document-context-registry.ts +177 -0
  449. package/src/document-open-registry.ts +200 -0
  450. package/src/document-preview-source.ts +20 -0
  451. package/src/document-renderer-session.ts +138 -0
  452. package/src/editor-api.ts +46 -0
  453. package/src/editor-chrome-capture.ts +139 -0
  454. package/src/editor-commands.ts +164 -0
  455. package/src/editor-console.ts +563 -0
  456. package/src/editor-current-view.ts +82 -0
  457. package/src/editor-document-probe.ts +881 -0
  458. package/src/editor-git-client.ts +115 -0
  459. package/src/editor-host-door.ts +462 -0
  460. package/src/editor-hotkeys.ts +842 -0
  461. package/src/editor-lease-view.ts +39 -0
  462. package/src/editor-lease.ts +415 -0
  463. package/src/editor-mode.ts +19 -0
  464. package/src/editor-notifications.ts +140 -0
  465. package/src/editor-presence.ts +563 -0
  466. package/src/editor-presentation-activity.ts +50 -0
  467. package/src/editor-presentation-notice.ts +42 -0
  468. package/src/editor-runtime.tsx +145 -0
  469. package/src/editor-server-response.ts +86 -0
  470. package/src/editor-session-attribution.ts +75 -0
  471. package/src/editor-session-mode.ts +54 -0
  472. package/src/editor-shell-store.ts +1304 -0
  473. package/src/editor-state-facets.ts +74 -0
  474. package/src/editor-styles.css +33 -0
  475. package/src/editor-view-presentation.ts +810 -0
  476. package/src/editor-viewport.ts +5302 -0
  477. package/src/entity-lod.ts +31 -0
  478. package/src/entity-object.ts +91 -0
  479. package/src/files/file-provider.ts +62 -0
  480. package/src/files/project-files.ts +264 -0
  481. package/src/finders/index.ts +136 -0
  482. package/src/finders/scenes-from-entrypoint-selection.ts +387 -0
  483. package/src/frame/bridge.tsx +1408 -0
  484. package/src/frame/product.ts +77 -0
  485. package/src/gameplay-dom-recording.ts +319 -0
  486. package/src/gameplay-export-state.ts +14 -0
  487. package/src/gameplay-replay.ts +417 -0
  488. package/src/gameplay-session-time.ts +9 -0
  489. package/src/gameplay-sessions.ts +204 -0
  490. package/src/harness-chat-types.ts +253 -0
  491. package/src/hierarchy-component-marks.ts +298 -0
  492. package/src/hierarchy-drop.ts +91 -0
  493. package/src/hierarchy-expansion-state.ts +80 -0
  494. package/src/hierarchy-header-slot.ts +52 -0
  495. package/src/hierarchy-internals.ts +197 -0
  496. package/src/hierarchy-kind-icon.ts +217 -0
  497. package/src/hierarchy-mark-reader.ts +73 -0
  498. package/src/hierarchy-menu-registry.ts +67 -0
  499. package/src/hierarchy-node-rows.ts +307 -0
  500. package/src/hierarchy-panel-view.ts +280 -0
  501. package/src/hierarchy-projection.ts +76 -0
  502. package/src/hierarchy-row-cache.ts +243 -0
  503. package/src/hierarchy-row-model.ts +308 -0
  504. package/src/hierarchy-rows.ts +11 -0
  505. package/src/hierarchy-walk.ts +86 -0
  506. package/src/history/editor-session.ts +25 -0
  507. package/src/history/history-commands.ts +147 -0
  508. package/src/history/history-delegate.ts +163 -0
  509. package/src/history/history-limit-notices.ts +43 -0
  510. package/src/history/history-service.ts +1173 -0
  511. package/src/history/persistence-coordinator.ts +35 -0
  512. package/src/history/project-file-history.ts +386 -0
  513. package/src/history/project-root-history-backends.ts +139 -0
  514. package/src/history/resource-registry.ts +209 -0
  515. package/src/history/snapshot-store.ts +103 -0
  516. package/src/history/source-history-backend.ts +546 -0
  517. package/src/history/types.ts +124 -0
  518. package/src/hmr-registration-group.ts +67 -0
  519. package/src/hmr-stable-react-context.ts +23 -0
  520. package/src/hotkeys.ts +188 -0
  521. package/src/inference-diagnostics.ts +69 -0
  522. package/src/initial-project.ts +80 -0
  523. package/src/inspection/active-subject.ts +578 -0
  524. package/src/inspection/active-surface.ts +142 -0
  525. package/src/inspection/compose.ts +1064 -0
  526. package/src/inspection/display.ts +170 -0
  527. package/src/inspection/document-subject.ts +109 -0
  528. package/src/inspection/game-subject.ts +85 -0
  529. package/src/inspection/model.ts +542 -0
  530. package/src/inspection/null-subject.ts +115 -0
  531. package/src/inspection/serialize.ts +357 -0
  532. package/src/inspection/use-active-inspection.ts +180 -0
  533. package/src/inspector-presentation.ts +201 -0
  534. package/src/inspector-section-registry.ts +221 -0
  535. package/src/instance-source-actions.ts +163 -0
  536. package/src/instanced-presentation.ts +164 -0
  537. package/src/js-heap.ts +71 -0
  538. package/src/key-actions.ts +91 -0
  539. package/src/keymap-presets.ts +401 -0
  540. package/src/layout-policy.ts +31 -0
  541. package/src/learn-links.ts +73 -0
  542. package/src/light-explorer-model.ts +134 -0
  543. package/src/live-canvas-frame.ts +55 -0
  544. package/src/live-document.ts +279 -0
  545. package/src/live-module-source.ts +230 -0
  546. package/src/live-session-registry.ts +220 -0
  547. package/src/live-transition.ts +633 -0
  548. package/src/manifest-project.ts +107 -0
  549. package/src/model-thumbnail.ts +523 -0
  550. package/src/native-selection-style.ts +202 -0
  551. package/src/object3d-document-write-policy.ts +137 -0
  552. package/src/packaged-runtime.ts +108 -0
  553. package/src/palettes/maya.palette.json +57 -0
  554. package/src/palettes/substance.palette.json +57 -0
  555. package/src/pasteboard-module.ts +81 -0
  556. package/src/performance-profiler.ts +367 -0
  557. package/src/performance-sources.ts +69 -0
  558. package/src/play-boot-phase.ts +145 -0
  559. package/src/presentation-surface.ts +248 -0
  560. package/src/project-adapter.ts +1143 -0
  561. package/src/project-asset-refresh.ts +26 -0
  562. package/src/project-declaration-refresh.ts +31 -0
  563. package/src/project-local-state.ts +118 -0
  564. package/src/project-manager.ts +243 -0
  565. package/src/project-module-changes.ts +163 -0
  566. package/src/project-module-split.ts +266 -0
  567. package/src/project-provenance.ts +115 -0
  568. package/src/project-ready.ts +42 -0
  569. package/src/project-session-reset.ts +67 -0
  570. package/src/project-shape.ts +68 -0
  571. package/src/project-tool-discovery.ts +66 -0
  572. package/src/project-tools.ts +107 -0
  573. package/src/project-work-types.ts +149 -0
  574. package/src/projection/three.ts +898 -0
  575. package/src/projection/types.ts +44 -0
  576. package/src/readiness.ts +113 -0
  577. package/src/reflection-probe-helper.ts +142 -0
  578. package/src/reported-play-state.ts +90 -0
  579. package/src/resolve-relative-specifier.ts +33 -0
  580. package/src/scene-document-plan.ts +315 -0
  581. package/src/scene-framing.ts +315 -0
  582. package/src/scene-live-open.ts +210 -0
  583. package/src/scene-view-fog.ts +89 -0
  584. package/src/scoped-game-css.ts +152 -0
  585. package/src/session-orphan-record.ts +193 -0
  586. package/src/session-tombstone.ts +126 -0
  587. package/src/settings/settings-provider.ts +82 -0
  588. package/src/settings-store.ts +345 -0
  589. package/src/shared-view-restore.ts +42 -0
  590. package/src/shell-store-door.ts +45 -0
  591. package/src/source-conflict.ts +122 -0
  592. package/src/spatial-handle-visuals.ts +332 -0
  593. package/src/stage-context.ts +341 -0
  594. package/src/stage-store-registry.ts +63 -0
  595. package/src/stale-chunk-recovery.ts +34 -0
  596. package/src/state-report-deferral.ts +73 -0
  597. package/src/storage/host-files-storage.ts +97 -0
  598. package/src/storage/http-storage.ts +174 -0
  599. package/src/storage/index.ts +75 -0
  600. package/src/storage/mem-storage.ts +149 -0
  601. package/src/storage/path-lock.ts +44 -0
  602. package/src/storage/paths.ts +26 -0
  603. package/src/storage/types.ts +116 -0
  604. package/src/stories/StoryComponentThumbnail.tsx +184 -0
  605. package/src/stories/StoryPreviewMount.tsx +306 -0
  606. package/src/stories/component-content-source.tsx +106 -0
  607. package/src/stories/component-states-source.ts +78 -0
  608. package/src/stories/compose-project-stories.ts +255 -0
  609. package/src/stories/pixi-story-model.ts +30 -0
  610. package/src/stories/prefabs-finder.ts +54 -0
  611. package/src/stories/prefabs-from-stories.ts +182 -0
  612. package/src/stories/project-story-discovery.ts +78 -0
  613. package/src/stories/project-story-regions.ts +24 -0
  614. package/src/stories/story-actions.ts +54 -0
  615. package/src/stories/story-capture-command.ts +124 -0
  616. package/src/stories/story-capture.ts +584 -0
  617. package/src/stories/story-declared-medium.ts +126 -0
  618. package/src/stories/story-discovery.ts +176 -0
  619. package/src/stories/story-document-openers.ts +36 -0
  620. package/src/stories/story-dom-runtime.ts +78 -0
  621. package/src/stories/story-grouping.ts +111 -0
  622. package/src/stories/story-lane.ts +88 -0
  623. package/src/stories/story-mount-turn.ts +27 -0
  624. package/src/stories/story-opener.ts +260 -0
  625. package/src/stories/story-pixi-preview.ts +408 -0
  626. package/src/stories/story-presentation.ts +215 -0
  627. package/src/stories/story-registry.ts +517 -0
  628. package/src/stories/story-three-preview.ts +807 -0
  629. package/src/stories/three-story-model.ts +96 -0
  630. package/src/story-three-preview-runtime.ts +56 -0
  631. package/src/surface-keyboard.ts +112 -0
  632. package/src/surface-state.ts +124 -0
  633. package/src/tab-bootstrap.js +628 -0
  634. package/src/tab-census.ts +198 -0
  635. package/src/tab-lifecycle-client.ts +196 -0
  636. package/src/theme-library.ts +844 -0
  637. package/src/theme-preference.ts +378 -0
  638. package/src/theme.css +3437 -0
  639. package/src/three-viewport/bone-selection-highlight.ts +119 -0
  640. package/src/three-viewport/camera-fit.ts +41 -0
  641. package/src/three-viewport/interactive-renderer.ts +132 -0
  642. package/src/three-viewport/selection-brackets.ts +310 -0
  643. package/src/three-viewport/selection-outline.ts +183 -0
  644. package/src/three-viewport/skeleton-helper.ts +61 -0
  645. package/src/three-viewport/source-color.ts +167 -0
  646. package/src/three-viewport/studio-environment.ts +96 -0
  647. package/src/three-viewport-presentation.ts +23 -0
  648. package/src/tool-contribution-play.ts +74 -0
  649. package/src/tool-loader.ts +1798 -0
  650. package/src/transform-mode-request.ts +57 -0
  651. package/src/transient-hint.ts +78 -0
  652. package/src/trigger-volume-helper.ts +116 -0
  653. package/src/ui-source/adapter-region-includes.ts +241 -0
  654. package/src/ui-source/ensure-import.ts +132 -0
  655. package/src/ui-source/file-region-resolver.ts +302 -0
  656. package/src/ui-source/inspect.ts +775 -0
  657. package/src/ui-source/oid-transform.ts +1845 -0
  658. package/src/ui-source/plan-csf-story.ts +196 -0
  659. package/src/ui-source/plan-extract-component.ts +421 -0
  660. package/src/ui-source/plan-fork-component.ts +621 -0
  661. package/src/ui-source/plan-named-style.ts +95 -0
  662. package/src/ui-source/plan-source-edit.ts +353 -0
  663. package/src/ui-source/r3f-contract-resolver.ts +327 -0
  664. package/src/ui-source/r3f-diagnostic-index.ts +165 -0
  665. package/src/ui-source/r3f-environment-binding.ts +133 -0
  666. package/src/ui-source/r3f-joint-binding.ts +251 -0
  667. package/src/ui-source/r3f-lod-binding.ts +91 -0
  668. package/src/ui-source/r3f-particle-binding.ts +235 -0
  669. package/src/ui-source/r3f-physics-binding.ts +483 -0
  670. package/src/ui-source/r3f-project-contracts.ts +444 -0
  671. package/src/ui-source/relative-import-specifier.ts +34 -0
  672. package/src/ui-source/reparent-guard.ts +544 -0
  673. package/src/ui-source/source-edit-request.ts +97 -0
  674. package/src/ui-source/source-write-backend.ts +618 -0
  675. package/src/ui-source/syntactic-prop-specs.ts +296 -0
  676. package/src/ui-source/tier-source-write-backend.ts +279 -0
  677. package/src/ui-source/ts-ast.ts +171 -0
  678. package/src/ui-source/utility-class-support.ts +169 -0
  679. package/src/ui-source/write-component-default.ts +133 -0
  680. package/src/ui-source/writer.ts +1989 -0
  681. package/src/viewport-activation-timings.ts +840 -0
  682. package/src/viewport-authoring-policy.ts +186 -0
  683. package/src/viewport-controls-hint.ts +57 -0
  684. package/src/viewport-door.ts +237 -0
  685. package/src/viewport-shading-boundary.ts +12 -0
  686. package/src/vite-error-surface.ts +141 -0
  687. package/src/wait-until.ts +37 -0
  688. package/src/workspace-areas.ts +156 -0
  689. package/src/workspace-aux-commands.ts +11 -0
  690. package/src/workspace-available-documents.ts +127 -0
  691. package/src/workspace-core-utilities.ts +31 -0
  692. package/src/workspace-document-ids.ts +59 -0
  693. package/src/workspace-document-registry.ts +612 -0
  694. package/src/workspace-document-restore.ts +145 -0
  695. package/src/workspace-host-commands.ts +141 -0
  696. package/src/workspace-persistence-gate.ts +40 -0
  697. package/src/workspace-play-utilities.ts +44 -0
  698. package/src/workspace-presets.ts +425 -0
  699. package/src/workspace-regions.ts +294 -0
  700. package/src/workspace-state-persistence.ts +536 -0
  701. package/src/workspace-static-panels.ts +73 -0
  702. package/src/workspace-status-registry.ts +121 -0
  703. package/src/workspace-style.ts +239 -0
  704. package/src/workspace-utility-commands.ts +74 -0
  705. package/src/workspace-utility-registry.ts +263 -0
  706. package/src/workspace-viewport-rect.ts +97 -0
  707. package/src/world-adoption.ts +115 -0
  708. package/src/world-document-routing.ts +104 -0
  709. package/vite-plugin-creation-site-write.ts +184 -0
  710. package/vite-plugin-creation-site.ts +80 -0
  711. package/vite-plugin-game-static.ts +303 -0
  712. package/vite-plugin-module-doorways.ts +336 -0
  713. package/vite-plugin-product-contributions.ts +197 -0
  714. package/vite-plugin-project-game-static.ts +125 -0
  715. package/vite-plugin-project-jsx-js.ts +65 -0
  716. package/vite-plugin-project-root-absolute-assets.ts +359 -0
  717. package/vite-plugin-shared-react.ts +420 -0
  718. package/vite-plugin-shared-three.ts +203 -0
  719. package/vite-plugin-ui-oid.ts +2106 -0
@@ -0,0 +1,812 @@
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`, `vgai
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 { faCloudArrowDown, faFileLines } from '@fortawesome/free-solid-svg-icons';
36
+ import { bg, danger, text } from '@volter/editor-sdk/widgets';
37
+ import type { AuthoringAssetSubject } from '@volter/editor-project/adapter';
38
+ import { lazy, type ReactNode, Suspense, useEffect, useSyncExternalStore } from 'react';
39
+ import type * as THREE from 'three';
40
+ import { clearSelectedAsset } from '../asset-selection';
41
+ import { assetCapabilities } from '../asset-workflow/asset-capabilities';
42
+ import {
43
+ splitSpritesheetAssetPath,
44
+ spritesheetFrameTitle,
45
+ } from '../asset-workflow/pixi-spritesheet';
46
+ import { authoringAssetDataUrl } from '../authoring/authoring-asset-url';
47
+ import { awaitAnnouncedObject3DDocumentSession } from '../document-context-registry';
48
+ import { registerDocumentOpener } from '../document-open-registry';
49
+ import { projectFileExists } from '../editor-api';
50
+ import type { AssetKind, OnlineAssetInfo, ViewportTab } from '../editor-shell-store';
51
+ import { type InspectionSection, PROPERTIES_SECTION_ORDER } from '../inspection/model';
52
+ import { shellStoreForHost } from '../shell-store-door';
53
+ import { DOCUMENT_REGISTRATION_TIMEOUT_MS, waitUntil } from '../wait-until';
54
+ import {
55
+ activeWorkspaceDocumentId,
56
+ openWorkspaceDocument,
57
+ setWorkspaceDocumentTitle,
58
+ type WorkspaceDocumentContentProps,
59
+ type WorkspaceDocumentDescriptor,
60
+ workspaceDocumentSelection,
61
+ } from '../workspace-document-registry';
62
+ import { registerWorkspaceDocumentRestorer } from '../workspace-document-restore';
63
+
64
+ import { MediaProperties } from './MediaProperties';
65
+
66
+ const AssetEditorShell = lazy(async () => {
67
+ const module = await import('./AssetEditorShell');
68
+ return { default: module.AssetEditorShell };
69
+ });
70
+ const AudioViewer = lazy(async () => {
71
+ const module = await import('./asset-viewers/AudioViewer');
72
+ return { default: module.AudioViewer };
73
+ });
74
+ const EntityModelDocument = lazy(async () => {
75
+ const module = await import('./asset-viewers/EntityModelDocument');
76
+ return { default: module.EntityModelDocument };
77
+ });
78
+ const EnvironmentAssetDocument = lazy(async () => {
79
+ const module = await import('./asset-viewers/EnvironmentAssetDocument');
80
+ return { default: module.EnvironmentAssetDocument };
81
+ });
82
+ const ImageViewer = lazy(async () => {
83
+ const module = await import('./asset-viewers/ImageViewer');
84
+ return { default: module.ImageViewer };
85
+ });
86
+ const VideoViewer = lazy(async () => {
87
+ const module = await import('./asset-viewers/VideoViewer');
88
+ return { default: module.VideoViewer };
89
+ });
90
+ const JsonAssetDocument = lazy(async () => {
91
+ const module = await import('./asset-viewers/JsonAssetDocument');
92
+ return { default: module.JsonAssetDocument };
93
+ });
94
+ const ModelAssetDocument = lazy(async () => {
95
+ const module = await import('./asset-viewers/ModelAssetDocument');
96
+ return { default: module.ModelAssetDocument };
97
+ });
98
+ const OnlineAssetDetail = lazy(async () => {
99
+ const module = await import('./asset-viewers/OnlineAssetDetail');
100
+ return { default: module.OnlineAssetDetail };
101
+ });
102
+ const SourceAssetViewer = lazy(async () => {
103
+ const module = await import('./asset-viewers/SourceAssetViewer');
104
+ return { default: module.SourceAssetViewer };
105
+ });
106
+
107
+ /** What one open asset document is showing — the former `AssetTab` shape,
108
+ * now keyed by workspace-document id. */
109
+ export interface AssetDocumentSpec {
110
+ readonly assetPath: string;
111
+ readonly kind: AssetKind;
112
+ /** Entity source for a session-only Asset Editor document. Never serialized. */
113
+ readonly entityId?: string;
114
+ /** Present while this document shows an online (not-yet-downloaded) asset. */
115
+ readonly online?: OnlineAssetInfo;
116
+ readonly unavailableReason?: string;
117
+ /** Incremented when the file bytes change at the same path. */
118
+ readonly revision?: number;
119
+ /** Display metadata for an adapter-provided in-memory asset (for example inline SVG). */
120
+ readonly displayName?: string;
121
+ readonly sourcePath?: string;
122
+ }
123
+
124
+ export interface AssetDocumentMutation {
125
+ type: 'move' | 'delete' | 'change';
126
+ oldPath?: string;
127
+ newPath?: string;
128
+ paths?: readonly string[];
129
+ }
130
+
131
+ const hotData = import.meta.hot?.data;
132
+ const _specs =
133
+ (hotData?.['vgai:asset-document-specs'] as Map<string, AssetDocumentSpec> | undefined) ??
134
+ new Map<string, AssetDocumentSpec>();
135
+ const _pathAliases =
136
+ (hotData?.['vgai:asset-document-path-aliases'] as Map<string, string> | undefined) ??
137
+ new Map<string, string>();
138
+ const _focusReturnTargets = new Map<string, HTMLElement>();
139
+ if (hotData) {
140
+ // Workspace descriptors intentionally survive compatible Fast Refreshes.
141
+ // Their specs must survive with them or the next persistence snapshot drops
142
+ // the open document and leaves a titled-but-empty tab on reload.
143
+ hotData['vgai:asset-document-specs'] = _specs;
144
+ hotData['vgai:asset-document-path-aliases'] = _pathAliases;
145
+ }
146
+ let _version = 0;
147
+ const _listeners = new Set<() => void>();
148
+
149
+ function notifyChanged(): void {
150
+ _version++;
151
+ for (const fn of _listeners) fn();
152
+ }
153
+
154
+ /** Subscribe to spec changes (online-asset resolution). Returns unsubscribe. */
155
+ export function subscribeAssetDocuments(fn: () => void): () => void {
156
+ _listeners.add(fn);
157
+ return () => {
158
+ _listeners.delete(fn);
159
+ };
160
+ }
161
+
162
+ /** Monotonic change counter — the `getSnapshot` for `useSyncExternalStore`. */
163
+ export function assetDocumentsVersion(): number {
164
+ return _version;
165
+ }
166
+
167
+ /** The spec behind an open asset document id, or `undefined`. */
168
+ export function assetDocumentSpec(id: string): AssetDocumentSpec | undefined {
169
+ return _specs.get(id);
170
+ }
171
+
172
+ /** Whether `id` is one of this module's asset documents (used by
173
+ * `command-listener.ts` to report the legacy `activeTabKey` facet). */
174
+ export function isAssetDocumentId(id: string): boolean {
175
+ return _specs.has(id);
176
+ }
177
+
178
+ /** The narrow store surface asset documents need (T6.3 gate hand-off). */
179
+ export interface AssetDocumentStore {
180
+ setActiveViewportTab(tab: ViewportTab): void;
181
+ }
182
+
183
+ /** The Asset Editor additionally needs the live object map for its §8 title. */
184
+ export interface EntityAssetDocumentStore extends AssetDocumentStore {
185
+ readonly objectMap: Map<string, THREE.Object3D>;
186
+ }
187
+
188
+ /** Shallow equality over the union of both specs' keys — the specs are flat
189
+ * records of primitives plus two small objects, and the only question asked
190
+ * of them is "did the request ask for something different". */
191
+ function sameSpec(a: AssetDocumentSpec, b: AssetDocumentSpec): boolean {
192
+ const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
193
+ for (const key of keys) {
194
+ const left = (a as unknown as Record<string, unknown>)[key];
195
+ const right = (b as unknown as Record<string, unknown>)[key];
196
+ if (left === right) continue;
197
+ if (JSON.stringify(left) !== JSON.stringify(right)) return false;
198
+ }
199
+ return true;
200
+ }
201
+
202
+ function openSpecDocument(
203
+ store: AssetDocumentStore,
204
+ id: string,
205
+ title: string,
206
+ spec: AssetDocumentSpec,
207
+ provenance?: WorkspaceDocumentDescriptor['provenance'],
208
+ options: { activate?: boolean } = {},
209
+ ): string {
210
+ if (typeof document !== 'undefined' && document.activeElement instanceof HTMLElement) {
211
+ _focusReturnTargets.set(id, document.activeElement);
212
+ }
213
+ const existing = _specs.get(id);
214
+ if (!existing) {
215
+ _specs.set(id, spec);
216
+ notifyChanged();
217
+ } else {
218
+ // FIRST SPEC NO LONGER WINS. Re-opening the same path with a DIFFERENT
219
+ // kind used to be a silent no-op: the id IS the path, the spec was written
220
+ // once, and every later request was dropped on the floor while the call
221
+ // still acked. `editor.openAsset('/x.json', 'source')` therefore handed
222
+ // back the JSON viewer and reported success.
223
+ //
224
+ // The honest resolution is UPDATE, not refuse, because of what the two
225
+ // halves of this identity are: the PATH is the document's identity (the id
226
+ // is literally the path) and the KIND is a VIEW of that file. A second
227
+ // view of the same file is an ordinary request, not a collision — refusing
228
+ // it would mean a file already open can only be re-viewed by closing it
229
+ // first, a modal rule the workspace has nowhere to show. Layout
230
+ // persistence agrees: it serializes whatever the spec CURRENTLY says
231
+ // (`{path, assetKind}`) and restores by calling this function against an
232
+ // empty registry, so an updated spec round-trips exactly and refusal would
233
+ // never fire on that path anyway.
234
+ //
235
+ // The request wins key-by-key; fields the request does not speak for
236
+ // (`revision`, `unavailableReason` — facts about the FILE at that path,
237
+ // maintained by `applyAssetDocumentMutation`) survive.
238
+ const merged = { ...existing, ...spec };
239
+ if (!sameSpec(existing, merged)) {
240
+ _specs.set(id, merged);
241
+ notifyChanged();
242
+ }
243
+ }
244
+ const descriptor: WorkspaceDocumentDescriptor = {
245
+ id,
246
+ title,
247
+ kind: 'asset',
248
+ workspaceRole: 'authored-subject',
249
+ ...(provenance ? { provenance } : {}),
250
+ Content: AssetDocumentContent,
251
+ closeable: true,
252
+ presentation: () => {
253
+ const current = _specs.get(id);
254
+ if (!current) return null;
255
+ if (current.entityId)
256
+ return { kind: 'asset', entityId: current.entityId, assetKind: 'model' };
257
+ if (!current.assetPath || current.online) return null;
258
+ return {
259
+ kind: 'asset',
260
+ path: current.assetPath,
261
+ assetKind: current.kind,
262
+ };
263
+ },
264
+ // Only a real FILE-BACKED view restores: a live-entity Asset Editor
265
+ // session and an unresolved online asset are not truthfully re-resolvable
266
+ // from a cold boot, so they return nothing and the host stores nothing.
267
+ persist: () => {
268
+ const current = _specs.get(id);
269
+ if (!current?.assetPath || current.online || current.entityId !== undefined) return undefined;
270
+ return { path: current.assetPath, assetKind: current.kind };
271
+ },
272
+ onActivate: () => {
273
+ store.setActiveViewportTab('edit');
274
+ if (spec.kind === 'image' || spec.kind === 'video' || spec.kind === 'audio')
275
+ clearSelectedAsset();
276
+ },
277
+ onDispose: (documentId) => {
278
+ if (_specs.delete(documentId)) notifyChanged();
279
+ const focusTarget = _focusReturnTargets.get(documentId);
280
+ _focusReturnTargets.delete(documentId);
281
+ if (focusTarget?.isConnected) setTimeout(() => focusTarget.focus(), 0);
282
+ },
283
+ };
284
+ return openWorkspaceDocument(descriptor, options);
285
+ }
286
+
287
+ /**
288
+ * Open a project asset as a center document (double-click/Enter in the asset
289
+ * browser, `open-asset-tab` control command). Re-opening an already-open
290
+ * path activates the existing document (registry dedupe) — and re-opening it
291
+ * with a DIFFERENT kind re-points that document's spec at the requested view
292
+ * rather than silently keeping the first one (see `openSpecDocument`).
293
+ */
294
+ export function openAssetDocument(
295
+ store: AssetDocumentStore,
296
+ assetPath: string,
297
+ kind: AssetKind,
298
+ options: { activate?: boolean } = {},
299
+ ): string {
300
+ const title = spritesheetFrameTitle(assetPath);
301
+ const { frameName } = splitSpritesheetAssetPath(assetPath);
302
+ const id = _pathAliases.get(normalizeAssetPath(assetPath)) ?? assetPath;
303
+ return openSpecDocument(
304
+ store,
305
+ id,
306
+ title,
307
+ frameName ? { assetPath, kind, displayName: title } : { assetPath, kind },
308
+ { sourcePath: assetPath },
309
+ options,
310
+ );
311
+ }
312
+
313
+ /**
314
+ * Reopen a persisted asset document. Verified with an EXISTENCE-ONLY probe
315
+ * (`projectFileExists` — a backend stat in browser-hosted projects, an HTTP
316
+ * HEAD on the local server): a deleted asset resolves false and the document
317
+ * is dropped. Deliberately not `readProjectTextFile`, which downloads the
318
+ * whole asset body just to throw it away here.
319
+ */
320
+ registerWorkspaceDocumentRestorer({
321
+ kind: 'asset',
322
+ owner: 'asset-documents',
323
+ restore: async ({ state, store }) => {
324
+ const record = state as { path?: unknown; assetKind?: unknown } | null | undefined;
325
+ const path = typeof record?.path === 'string' ? record.path : null;
326
+ const assetKind = typeof record?.assetKind === 'string' ? record.assetKind : null;
327
+ if (!path || !assetKind) return false;
328
+ if (!(await projectFileExists(path))) return false;
329
+ openAssetDocument(store, path, assetKind as AssetKind);
330
+ return true;
331
+ },
332
+ });
333
+
334
+ /**
335
+ * THE `asset` ADDRESS (`document-open-registry.ts`) — an asset at a path, or
336
+ * the Asset Editor over a live scene entity. Registered at module load, the
337
+ * shape `packages/game/src/story-documents/three-story-documents.tsx:239`
338
+ * uses; what an extension means, and what `@volter/editor-sdk`'s published view
339
+ * kinds map onto internally, are this module's rules and the presenter no
340
+ * longer holds either.
341
+ *
342
+ * `ready` (the post-open half of `settle`) is why the address exists as more
343
+ * than a call: an asset document answers its id the moment its tab is
344
+ * registered, while `present` must not return until the Inspector has mounted
345
+ * — and for a MODEL, until its live Object3D adapter has replaced the identity
346
+ * floor, or an immediate inspect/setField sees a mounted tab whose graph is
347
+ * not ready. That rule is the asset family's; the host used to await it by
348
+ * hand because it knew the rule.
349
+ */
350
+ /**
351
+ * THE ASSET DOCUMENT'S READINESS — this family's rule, and a DOOR two callers
352
+ * outside the view protocol hold besides the `ready` below
353
+ * (`command-listener.ts`'s `open-asset-tab` and `@vgai/game`'s ragdoll
354
+ * command). It lived in `editor-view-presentation.ts` while the presenter did
355
+ * the waiting by hand; the presenter holds no kind's rule any more, so the
356
+ * rule lives beside the documents it describes.
357
+ */
358
+ export async function waitForAssetDocumentInspector(
359
+ documentId: string,
360
+ kind: AssetKind,
361
+ ): Promise<boolean> {
362
+ return waitUntil(() => {
363
+ if (activeWorkspaceDocumentId() !== documentId) return false;
364
+ const selection = workspaceDocumentSelection(documentId);
365
+ if (!selection) return false;
366
+ // Every asset shell publishes its own identity once mounted. A model has
367
+ // a stronger readiness bar: its live Object3D adapter must have replaced
368
+ // that identity floor, otherwise an immediate inspect/setField would see
369
+ // a mounted tab whose graph and project authoring contribution are not
370
+ // ready yet.
371
+ return kind !== 'model' || selection.adapter !== null;
372
+ }, DOCUMENT_REGISTRATION_TIMEOUT_MS);
373
+ }
374
+
375
+ registerDocumentOpener<{
376
+ readonly path?: string;
377
+ readonly assetKind?: string;
378
+ readonly entityId?: string;
379
+ }>({
380
+ id: 'asset',
381
+ owner: 'asset-documents',
382
+ open: (store, request) => {
383
+ if (request.entityId) {
384
+ // The Asset Editor's §8 title comes from the LIVE object map, which the
385
+ // address seam's narrow store (`WorkspaceStateStore`) does not carry and
386
+ // should not: `shell-store-door.ts` is how a lane reaches the one shell
387
+ // store without the seam widening for a single family.
388
+ const shell = shellStoreForHost();
389
+ if (!shell) return null;
390
+ const id = openEntityAssetDocument(shell, request.entityId);
391
+ if (!id) throw new Error(`Scene entity is not available: ${request.entityId}`);
392
+ return id;
393
+ }
394
+ const path = request.path;
395
+ if (path === undefined) return null;
396
+ return openAssetDocument(store, path, requestedAssetKind(request.assetKind, path));
397
+ },
398
+ ready: async (documentId, request) => {
399
+ const kind: AssetKind = request.entityId
400
+ ? 'model'
401
+ : requestedAssetKind(request.assetKind, request.path ?? '');
402
+ if (!(await waitForAssetDocumentInspector(documentId, kind)))
403
+ throw new Error(`Asset document did not finish mounting its Inspector: ${documentId}`);
404
+ // AND ITS STAGE, where it mounted one. The KIND does not decide that and
405
+ // this module must not pretend it does: a `model` always mounts one, a
406
+ // `source` asset only when its module is a model builder
407
+ // (`asset-viewers/SourceAssetViewer.tsx:256`) and a `json` only when its
408
+ // content is a three.quarks system (`asset-viewers/JsonAssetDocument.tsx:64`),
409
+ // so the stage's own announcement is what answers. The Inspector wait above
410
+ // is what makes an absent announcement mean "no stage" rather than "not
411
+ // yet": the shell that would have rendered one has already mounted.
412
+ await awaitAnnouncedObject3DDocumentSession(documentId);
413
+ },
414
+ });
415
+
416
+ /**
417
+ * The internal view kind an address asks for. `@volter/editor-sdk`'s public
418
+ * view-link contract still carries 'animation' and 'prefab' kinds that have no
419
+ * internal counterpart; exactly those remap to their closest surviving
420
+ * internal kinds. Unnamed falls back to what the extension says.
421
+ */
422
+ function requestedAssetKind(requested: string | undefined, path: string): AssetKind {
423
+ if (requested === 'animation') return 'source';
424
+ if (requested === 'prefab') return 'json';
425
+ return (requested as AssetKind | undefined) ?? inferAssetKind(path);
426
+ }
427
+
428
+ /** What an extension means. The asset browser's own vocabulary, which is why
429
+ * it lives beside the documents it opens rather than in the presenter. */
430
+ function inferAssetKind(path: string): AssetKind {
431
+ const ext = path.slice(path.lastIndexOf('.') + 1).toLowerCase();
432
+ if (ext === 'glb' || ext === 'gltf') return 'model';
433
+ if (['png', 'jpg', 'jpeg', 'webp', 'gif', 'svg'].includes(ext)) return 'image';
434
+ if (['mp3', 'ogg', 'wav', 'flac'].includes(ext)) return 'audio';
435
+ if (['glsl', 'vert', 'frag', 'ts', 'tsx', 'js', 'jsx', 'css', 'html', 'md', 'txt'].includes(ext))
436
+ return 'source';
437
+ return 'json';
438
+ }
439
+
440
+ /** Open the Asset Editor document for a live entity. */
441
+ export function openEntityAssetDocument(
442
+ store: EntityAssetDocumentStore,
443
+ entityId: string,
444
+ ): string | null {
445
+ const object = store.objectMap.get(entityId);
446
+ if (!object) return null;
447
+ // The live `Object3D.name` is the only name there is.
448
+ const title = object.name || entityId;
449
+ return openSpecDocument(
450
+ store,
451
+ `asset-editor:entity:${entityId}`,
452
+ title,
453
+ { assetPath: '', kind: 'model', entityId },
454
+ { origin: `entity:${entityId}` },
455
+ );
456
+ }
457
+
458
+ /** Open an adapter-owned atomic asset that is embedded in a source document. */
459
+ export function openAuthoringAssetDocument(
460
+ store: AssetDocumentStore,
461
+ entityId: string,
462
+ subject: AuthoringAssetSubject,
463
+ ): string {
464
+ const assetPath = authoringAssetDataUrl(subject);
465
+ return openSpecDocument(
466
+ store,
467
+ `asset-editor:subject:${entityId}`,
468
+ subject.name,
469
+ {
470
+ assetPath,
471
+ kind: subject.kind,
472
+ displayName: subject.name,
473
+ ...(subject.sourcePath ? { sourcePath: subject.sourcePath } : {}),
474
+ },
475
+ subject.sourcePath ? { sourcePath: subject.sourcePath } : { origin: `entity:${entityId}` },
476
+ );
477
+ }
478
+
479
+ /** Open an online-library asset's detail/download document. */
480
+ export function openOnlineAssetDocument(store: AssetDocumentStore, online: OnlineAssetInfo): void {
481
+ const kind: AssetKind =
482
+ online.type === 'model'
483
+ ? 'model'
484
+ : online.type === 'animation' || online.type === 'source'
485
+ ? 'source'
486
+ : 'image';
487
+ openSpecDocument(
488
+ store,
489
+ `online:${online.source}:${online.id}`,
490
+ online.name,
491
+ { assetPath: '', kind, online },
492
+ { origin: `online:${online.source}:${online.id}` },
493
+ );
494
+ }
495
+
496
+ /** Point an online asset document at its downloaded local file — the local
497
+ * viewer takes over in place (same document id/title). */
498
+ export function resolveOnlineAssetDocument(id: string, localPath: string): void {
499
+ const spec = _specs.get(id);
500
+ if (!spec) return;
501
+ const { online: _dropped, ...rest } = spec;
502
+ _specs.set(id, { ...rest, assetPath: localPath });
503
+ notifyChanged();
504
+ }
505
+
506
+ /** Test-only reset — drops all specs without touching the document registry
507
+ * (pair it with `__resetWorkspaceDocumentRegistryForTest`). */
508
+ export function __resetAssetDocumentsForTest(): void {
509
+ _specs.clear();
510
+ _pathAliases.clear();
511
+ _focusReturnTargets.clear();
512
+ notifyChanged();
513
+ }
514
+
515
+ /** Keep a live editor bound to asset identity while its source path changes. */
516
+ export function applyAssetDocumentMutation(
517
+ documentId: string,
518
+ detail: AssetDocumentMutation,
519
+ ): void {
520
+ const spec = _specs.get(documentId);
521
+ if (!spec?.assetPath) return;
522
+ const current = normalizeAssetPath(spec.assetPath);
523
+ if (
524
+ detail.type === 'move' &&
525
+ detail.oldPath &&
526
+ detail.newPath &&
527
+ current === normalizeAssetPath(detail.oldPath)
528
+ ) {
529
+ const newPath = `/${normalizeAssetPath(detail.newPath)}`;
530
+ _specs.set(documentId, { ...spec, assetPath: newPath });
531
+ _pathAliases.set(normalizeAssetPath(newPath), documentId);
532
+ setWorkspaceDocumentTitle(documentId, newPath.split('/').pop() ?? newPath);
533
+ notifyChanged();
534
+ } else if (
535
+ detail.type === 'delete' &&
536
+ detail.paths?.some((path) => normalizeAssetPath(path) === current)
537
+ ) {
538
+ _specs.set(documentId, {
539
+ ...spec,
540
+ unavailableReason: `The source ${spec.assetPath} was deleted. Undo the deletion or close this editor.`,
541
+ });
542
+ notifyChanged();
543
+ } else if (
544
+ detail.type === 'change' &&
545
+ detail.paths?.some((path) => normalizeAssetPath(path) === current)
546
+ ) {
547
+ _specs.set(documentId, { ...spec, revision: (spec.revision ?? 0) + 1 });
548
+ notifyChanged();
549
+ }
550
+ }
551
+
552
+ export type AssetDocumentViewerRoute =
553
+ | 'online'
554
+ | 'model'
555
+ | 'environment'
556
+ | 'image'
557
+ | 'video'
558
+ | 'audio'
559
+ | 'source'
560
+ | 'json'
561
+ | 'unsupported';
562
+
563
+ /** Pure routing decision, exported so every recognized asset kind is test-pinned. */
564
+ export function assetDocumentViewerRoute(spec: AssetDocumentSpec): AssetDocumentViewerRoute {
565
+ if (spec.online) return 'online';
566
+ if (assetCapabilities(spec.assetPath).editor === 'environment') return 'environment';
567
+ if (spec.kind === 'json')
568
+ return 'json';
569
+ if (['model', 'image', 'video', 'audio', 'source'].includes(spec.kind))
570
+ return spec.kind as AssetDocumentViewerRoute;
571
+ return 'unsupported';
572
+ }
573
+
574
+ /** Where this asset CAME FROM — the document's own provenance block, one of
575
+ * the sections it publishes as its subject
576
+ * (`components/AssetEditorShell.tsx`). */
577
+ function sourceSection(spec: AssetDocumentSpec): InspectionSection {
578
+ return {
579
+ id: 'source',
580
+ title: 'Source',
581
+ icon: faFileLines,
582
+ order: PROPERTIES_SECTION_ORDER,
583
+ body: {
584
+ kind: 'custom',
585
+ render: () => <div style={{ padding: 4 }}>{spec.sourcePath ?? spec.assetPath}</div>,
586
+ },
587
+ };
588
+ }
589
+
590
+ /** Routes a spec to its center viewer/editor. */
591
+ function AssetViewerBody({
592
+ id,
593
+ spec,
594
+ active,
595
+ }: {
596
+ id: string;
597
+ spec: AssetDocumentSpec;
598
+ active: boolean;
599
+ }) {
600
+ if (spec.unavailableReason) {
601
+ return (
602
+ <AssetEditorShell
603
+ documentId={id}
604
+ active={active}
605
+ type={spec.kind}
606
+ title={spec.assetPath.split('/').pop() || 'Deleted asset'}
607
+ status="Source unavailable"
608
+ >
609
+ <div style={{ padding: 20, color: danger }}>{spec.unavailableReason}</div>
610
+ </AssetEditorShell>
611
+ );
612
+ }
613
+ const route = assetDocumentViewerRoute(spec);
614
+ if (route === 'online' && spec.online) {
615
+ const online = spec.online;
616
+ return (
617
+ <AssetEditorShell
618
+ documentId={id}
619
+ active={active}
620
+ type="online"
621
+ title={online.name}
622
+ sections={[
623
+ {
624
+ id: 'online-asset',
625
+ title: 'Asset',
626
+ icon: faCloudArrowDown,
627
+ order: PROPERTIES_SECTION_ORDER,
628
+ body: {
629
+ kind: 'custom',
630
+ render: () => (
631
+ <OnlineAssetDetail
632
+ online={online}
633
+ mode="inspector"
634
+ onResolved={(localPath) => resolveOnlineAssetDocument(id, localPath)}
635
+ />
636
+ ),
637
+ },
638
+ },
639
+ ]}
640
+ status={`${online.type} · ${online.source}`}
641
+ >
642
+ <OnlineAssetDetail online={online} mode="preview" />
643
+ </AssetEditorShell>
644
+ );
645
+ }
646
+ const title = (spec.displayName ?? spec.assetPath.split('/').pop()) || spec.entityId || 'Asset';
647
+ const viewerKey = `${id}:${spec.revision ?? 0}`;
648
+ if (route === 'model')
649
+ if (spec.assetPath)
650
+ return (
651
+ <ModelAssetDocument
652
+ documentId={id}
653
+ key={viewerKey}
654
+ assetPath={spec.assetPath}
655
+ displayName={title}
656
+ active={active}
657
+ />
658
+ );
659
+ else if (spec.entityId)
660
+ return (
661
+ <EntityModelDocument
662
+ documentId={id}
663
+ key={viewerKey}
664
+ entityId={spec.entityId}
665
+ displayName={title}
666
+ active={active}
667
+ />
668
+ );
669
+ else return <div style={{ padding: 20, color: danger }}>Model source is unavailable.</div>;
670
+ if (route === 'environment')
671
+ return (
672
+ <EnvironmentAssetDocument
673
+ key={viewerKey}
674
+ documentId={id}
675
+ assetPath={spec.assetPath}
676
+ active={active}
677
+ />
678
+ );
679
+ if (route === 'source')
680
+ return (
681
+ <SourceAssetViewer
682
+ key={viewerKey}
683
+ documentId={id}
684
+ assetPath={spec.assetPath}
685
+ active={active}
686
+ />
687
+ );
688
+ // A .json is content-routed: three.quarks names no extension of its own, so
689
+ // only the file's own structure can say whether this is a particle document
690
+ // or ordinary JSON. `JsonAssetDocument` owns the shell for both answers.
691
+ if (route === 'json')
692
+ return (
693
+ <JsonAssetDocument
694
+ key={viewerKey}
695
+ documentId={id}
696
+ assetPath={spec.assetPath}
697
+ displayName={title}
698
+ active={active}
699
+ sections={[sourceSection(spec)]}
700
+ status={`json · ${spec.sourcePath ?? spec.displayName ?? spec.assetPath}`}
701
+ />
702
+ );
703
+ let viewer: ReactNode;
704
+ switch (route) {
705
+ case 'image':
706
+ viewer = (
707
+ <ImageViewer
708
+ key={viewerKey}
709
+ assetPath={spec.assetPath}
710
+ {...(spec.displayName ? { displayName: spec.displayName } : {})}
711
+ />
712
+ );
713
+ break;
714
+ case 'video':
715
+ viewer = <VideoViewer key={viewerKey} assetPath={spec.assetPath} />;
716
+ break;
717
+ case 'audio':
718
+ viewer = <AudioViewer key={viewerKey} assetPath={spec.assetPath} />;
719
+ break;
720
+ default:
721
+ viewer = (
722
+ <div style={{ padding: 16, color: text[2], fontSize: 12 }}>
723
+ No viewer available for this asset type.
724
+ </div>
725
+ );
726
+ }
727
+ return (
728
+ <AssetEditorShell
729
+ documentId={id}
730
+ active={active}
731
+ type={route}
732
+ title={title}
733
+ fill={route === 'image' || route === 'video'}
734
+ sections={
735
+ (route === 'image' && !spec.assetPath.includes('#')) ||
736
+ route === 'video' ||
737
+ route === 'audio'
738
+ ? [
739
+ {
740
+ id: 'media-properties',
741
+ title: 'Properties',
742
+ icon: faFileLines,
743
+ order: PROPERTIES_SECTION_ORDER,
744
+ body: {
745
+ kind: 'custom',
746
+ render: () => (
747
+ <MediaProperties
748
+ key={viewerKey}
749
+ assetPath={spec.assetPath}
750
+ kind={route as 'image' | 'video' | 'audio'}
751
+ />
752
+ ),
753
+ },
754
+ },
755
+ ...(spec.sourcePath ? [sourceSection(spec)] : []),
756
+ ]
757
+ : [sourceSection(spec)]
758
+ }
759
+ status={`${route} · ${spec.sourcePath ?? spec.displayName ?? spec.assetPath}`}
760
+ >
761
+ {viewer}
762
+ </AssetEditorShell>
763
+ );
764
+ }
765
+
766
+ /**
767
+ * Full-size type-specific Asset Editor document host.
768
+ */
769
+ export function AssetDocumentContent({ documentId, active }: WorkspaceDocumentContentProps) {
770
+ useSyncExternalStore(subscribeAssetDocuments, assetDocumentsVersion);
771
+ useEffect(() => {
772
+ const handleMutation = (rawEvent: Event) => {
773
+ applyAssetDocumentMutation(
774
+ documentId,
775
+ (rawEvent as CustomEvent<AssetDocumentMutation>).detail,
776
+ );
777
+ };
778
+ window.addEventListener('editor:asset-mutation', handleMutation);
779
+ return () => window.removeEventListener('editor:asset-mutation', handleMutation);
780
+ }, [documentId]);
781
+ const spec = _specs.get(documentId);
782
+ return (
783
+ <div
784
+ data-testid={`asset-document:${documentId}`}
785
+ style={{
786
+ flex: 1,
787
+ minHeight: 0,
788
+ overflow: 'hidden',
789
+ pointerEvents: 'auto',
790
+ background: bg[1],
791
+ }}
792
+ >
793
+ {spec ? (
794
+ <Suspense
795
+ fallback={
796
+ <div style={{ padding: 16, color: text[2], fontSize: 12 }}>Loading asset viewer…</div>
797
+ }
798
+ >
799
+ <AssetViewerBody id={documentId} spec={spec} active={active} />
800
+ </Suspense>
801
+ ) : (
802
+ <div style={{ padding: 16, color: text[2], fontSize: 12 }}>
803
+ This asset document is no longer available.
804
+ </div>
805
+ )}
806
+ </div>
807
+ );
808
+ }
809
+
810
+ function normalizeAssetPath(path: string): string {
811
+ return path.split(/[?#]/, 1)[0]!.replace(/^\/+/, '');
812
+ }