@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,183 @@
1
+ /**
2
+ * Native Three selection presentation for editor-owned authoring viewports.
3
+ *
4
+ * A transform box answers where an object's editable bounds are; it does not
5
+ * answer which pixels belong to the selected object. Unity and Unreal draw
6
+ * both for that reason. This module supplies the geometry-following half with
7
+ * postprocessing's real OutlineEffect, while EditorViewport keeps native
8
+ * helpers for cameras, lights, bones and other nodes that draw no geometry.
9
+ *
10
+ * The effect is presentation only. It never enters a project render pipeline,
11
+ * changes source, or adds an authoring-data format.
12
+ */
13
+
14
+ import {
15
+ EDITOR_SELECTION_LAYER,
16
+ isInEditorOwnedSubtree,
17
+ } from '@volter/editor-threejs/viewport/editor-layers';
18
+ import type { OutlineEffect } from 'postprocessing';
19
+ import type * as THREE from 'three';
20
+
21
+ /**
22
+ * The three postprocessing bindings {@link createThreeSelectionOutline} needs,
23
+ * passed in rather than statically imported.
24
+ *
25
+ * `postprocessing` is ~200 kB of shaders and passes, and the surfaces this
26
+ * module serves do not all want it: `editor-viewport.ts` imports only
27
+ * {@link collectThreeSelectionOutlineTargets} (a pure traversal), and the
28
+ * Asset Lab 3D document builds its composer on its FIRST frame, not at module
29
+ * load. A static import here put the whole library in both closures
30
+ * eagerly. A caller that already loaded postprocessing for its own composer
31
+ * hands over its own bindings (`the world root's stage`); one that has not opens the
32
+ * lazy door {@link loadThreeSelectionOutline}.
33
+ */
34
+ export interface ThreeSelectionOutlineEffects {
35
+ readonly OutlineEffect: typeof import('postprocessing').OutlineEffect;
36
+ readonly BlendFunction: typeof import('postprocessing').BlendFunction;
37
+ readonly KernelSize: typeof import('postprocessing').KernelSize;
38
+ }
39
+
40
+ /**
41
+ * Build the editor's real postprocessing effect. A half-resolution medium
42
+ * blur resolves to a stable 3–4 CSS-pixel silhouette on both 1x and HiDPI
43
+ * displays instead of a driver-dependent hairline; pulse is deliberately
44
+ * disabled because selection is state, not an alert.
45
+ */
46
+ export function createThreeSelectionOutline(
47
+ { BlendFunction, KernelSize, OutlineEffect }: ThreeSelectionOutlineEffects,
48
+ scene: THREE.Scene,
49
+ camera: THREE.Camera,
50
+ colors: { readonly visible: number; readonly hidden: number },
51
+ ): OutlineEffect {
52
+ const effect = withoutSelectionLayerWarning(
53
+ () =>
54
+ new OutlineEffect(scene, camera, {
55
+ // Alpha keeps the selection color legible over both black and white
56
+ // materials. The effect's default screen blend washes out on pale assets.
57
+ blendFunction: BlendFunction.ALPHA,
58
+ visibleEdgeColor: colors.visible,
59
+ hiddenEdgeColor: colors.hidden,
60
+ edgeStrength: 5,
61
+ pulseSpeed: 0,
62
+ blur: true,
63
+ kernelSize: KernelSize.MEDIUM,
64
+ resolutionScale: 0.5,
65
+ xRay: true,
66
+ }),
67
+ );
68
+ // OutlineEffect temporarily removes layer 0 from selected meshes, renders
69
+ // scene depth, then renders only its selection layer for the mask. The
70
+ // editor camera normally enables ALL layers so it can draw layer-31 gizmos;
71
+ // without this exclusion the selected mesh remains in the depth pass via
72
+ // the effect's own layer and both masks cancel to no visible edge.
73
+ effect.selectionLayer = EDITOR_SELECTION_LAYER;
74
+ camera.layers.disable(EDITOR_SELECTION_LAYER);
75
+ return effect;
76
+ }
77
+
78
+ /**
79
+ * `OutlineEffect` constructs its `Selection` with a render layer taken from
80
+ * a module-wide counter, which the editor then overrides
81
+ * (`EDITOR_SELECTION_LAYER`, below). The counter still moves, and after 30
82
+ * constructions in one page — every Object3D document session builds its
83
+ * own effect — the library warns "Layer out of range, resetting to 2" and
84
+ * carries on. For this editor that line is noise: the layer it worries
85
+ * about is replaced before the effect ever renders. The console gate treats
86
+ * every warning as unresolved work, so the one message is held back for
87
+ * exactly the synchronous construction that provokes it; anything else the
88
+ * constructor says still reaches the console.
89
+ */
90
+ function withoutSelectionLayerWarning<T>(construct: () => T): T {
91
+ const warn = console.warn;
92
+ console.warn = (...args: unknown[]) => {
93
+ if (args[0] === 'Layer out of range, resetting to 2') return;
94
+ warn.apply(console, args);
95
+ };
96
+ try {
97
+ return construct();
98
+ } finally {
99
+ console.warn = warn;
100
+ }
101
+ }
102
+
103
+ /** THE LAZY DOOR: load `postprocessing` on demand and build the effect. For a
104
+ * caller with no composer of its own, so the library arrives with the
105
+ * silhouette rather than with the surface. Repeat calls are free — the module
106
+ * is cached after the first. */
107
+ export async function loadThreeSelectionOutline(
108
+ scene: THREE.Scene,
109
+ camera: THREE.Camera,
110
+ colors: { readonly visible: number; readonly hidden: number },
111
+ ): Promise<OutlineEffect> {
112
+ const { BlendFunction, KernelSize, OutlineEffect } = await import('postprocessing');
113
+ return createThreeSelectionOutline(
114
+ { BlendFunction, KernelSize, OutlineEffect },
115
+ scene,
116
+ camera,
117
+ colors,
118
+ );
119
+ }
120
+
121
+ /** Apply supplied renderer-ready colors without rebuilding GPU pass resources. */
122
+ export function setThreeSelectionOutlineColors(
123
+ effect: OutlineEffect,
124
+ colors: { readonly visible: number; readonly hidden: number },
125
+ ): void {
126
+ effect.visibleEdgeColor.setHex(colors.visible);
127
+ effect.hiddenEdgeColor.setHex(colors.hidden);
128
+ }
129
+
130
+ function isOutlineRenderable(object: THREE.Object3D): boolean {
131
+ if ((object as THREE.Bone).isBone) return false;
132
+ return (
133
+ (object as THREE.Mesh).isMesh ||
134
+ (object as THREE.Line).isLine ||
135
+ (object as THREE.Points).isPoints ||
136
+ (object as THREE.Sprite).isSprite
137
+ );
138
+ }
139
+
140
+ /**
141
+ * Expand selected semantic roots to the actual renderables postprocessing can
142
+ * mask. Selection rows commonly name a Group/component root, while an outline
143
+ * effect can only paint its Mesh/Line/Points/Sprite descendants.
144
+ */
145
+ export function collectThreeSelectionOutlineTargets(
146
+ selectedRoots: Iterable<THREE.Object3D>,
147
+ ): THREE.Object3D[] {
148
+ const targets: THREE.Object3D[] = [];
149
+ const seen = new Set<THREE.Object3D>();
150
+ for (const root of selectedRoots) {
151
+ // A bone has its own native joint highlight. Walking through it to a
152
+ // skinned/accessory descendant would make selecting the joint look like
153
+ // selecting the model instead.
154
+ if ((root as THREE.Bone).isBone) continue;
155
+ // Select what the component actually DRAWS, including implementation
156
+ // children folded out of the hierarchy. Unlike an AABB measurement, the
157
+ // mask pass cannot be poisoned by pooled/template geometry that is not
158
+ // currently rendered; it only paints pixels the subtree draws now.
159
+ root.traverse((object) => {
160
+ if (seen.has(object) || isInEditorOwnedSubtree(object) || !isOutlineRenderable(object)) {
161
+ return;
162
+ }
163
+ seen.add(object);
164
+ targets.push(object);
165
+ });
166
+ }
167
+ return targets;
168
+ }
169
+
170
+ /** Update only when the concrete renderable set changed, avoiding layer churn. */
171
+ export function syncThreeSelectionOutline(
172
+ effect: OutlineEffect,
173
+ selectedRoots: Iterable<THREE.Object3D>,
174
+ ): void {
175
+ const targets = collectThreeSelectionOutlineTargets(selectedRoots);
176
+ if (
177
+ effect.selection.size === targets.length &&
178
+ targets.every((target) => effect.selection.has(target))
179
+ ) {
180
+ return;
181
+ }
182
+ effect.selection.set(targets);
183
+ }
@@ -0,0 +1,61 @@
1
+ import { EDITOR_LAYER } from '@volter/editor-threejs/viewport/editor-layers';
2
+ import { getObjectMark, setObjectMark } from '@volter/editor-threejs/ecs/object-marks';
3
+ import * as THREE from 'three';
4
+
5
+ const SKELETON_HELPER_TYPE = 'skeletons';
6
+ const SKELETON_PARENT_COLOR = new THREE.Color(0x00e5ff);
7
+ const SKELETON_CHILD_COLOR = new THREE.Color(0xffb000);
8
+
9
+ export function styleEditorSkeletonHelper(helper: THREE.SkeletonHelper): void {
10
+ const colors = helper.geometry.getAttribute('color');
11
+ // SkeletonHelper emits two vertices per bone link. Three's stock pure
12
+ // blue/green endpoints disappear against vgai's dark viewport, especially
13
+ // in captures. Keep the native helper geometry, but use editor-contrast
14
+ // colors so the inspection affordance is actually visible.
15
+ for (let i = 0; i < colors.count; i += 2) {
16
+ colors.setXYZ(i, SKELETON_PARENT_COLOR.r, SKELETON_PARENT_COLOR.g, SKELETON_PARENT_COLOR.b);
17
+ if (i + 1 < colors.count) {
18
+ colors.setXYZ(i + 1, SKELETON_CHILD_COLOR.r, SKELETON_CHILD_COLOR.g, SKELETON_CHILD_COLOR.b);
19
+ }
20
+ }
21
+ colors.needsUpdate = true;
22
+ helper.renderOrder = 999;
23
+ const material = helper.material as THREE.LineBasicMaterial;
24
+ material.depthTest = false;
25
+ material.depthWrite = false;
26
+ material.toneMapped = false;
27
+ setObjectMark(helper, 'editorHelper', true);
28
+ setObjectMark(helper, 'editorHelperType', SKELETON_HELPER_TYPE);
29
+ helper.traverse((child) => child.layers.set(EDITOR_LAYER));
30
+ }
31
+
32
+ export function ensureSkeletonHelper(root: THREE.Object3D): THREE.SkeletonHelper | null {
33
+ let existing: THREE.SkeletonHelper | null = null;
34
+ let hasSkinnedMesh = false;
35
+ root.traverse((object) => {
36
+ if (getObjectMark(object, 'editorHelperType') === SKELETON_HELPER_TYPE) {
37
+ existing = object as THREE.SkeletonHelper;
38
+ }
39
+ if ((object as THREE.SkinnedMesh).isSkinnedMesh) hasSkinnedMesh = true;
40
+ });
41
+ if (existing || !hasSkinnedMesh) return existing;
42
+
43
+ const helper = new THREE.SkeletonHelper(root);
44
+ if (helper.bones.length === 0) {
45
+ helper.dispose();
46
+ return null;
47
+ }
48
+ styleEditorSkeletonHelper(helper);
49
+
50
+ // SkeletonHelper normally expects to be a scene-root sibling. Keeping it
51
+ // under the entity makes lifecycle cleanup automatic, so use entity-local
52
+ // coordinates instead of applying the entity world transform twice.
53
+ helper.matrix = new THREE.Matrix4();
54
+ helper.matrixAutoUpdate = false;
55
+ helper.name = 'EditorSkeletonHelper';
56
+ helper.visible = Boolean(getObjectMark(root, 'skeletonVisible'));
57
+ setObjectMark(helper, 'skeletonEnabled', helper.visible);
58
+ root.add(helper);
59
+ root.updateMatrixWorld(true);
60
+ return helper;
61
+ }
@@ -0,0 +1,167 @@
1
+ import * as THREE from 'three';
2
+
3
+ /**
4
+ * The colour to give a TONE-MAPPED material so the screen shows the palette's
5
+ * exact sRGB hex — the inverse of three's ACES fit (the input and output
6
+ * matrices and the RRT/ODT rational, `tonemapping_pars_fragment`) through the
7
+ * exposure; Linear divides by it; no tone mapping passes through; any other
8
+ * operator is left as-is.
9
+ *
10
+ * WHICH SURFACES ACTUALLY WANT IT, measured on the live Model stage
11
+ * (`document-script` over the session's scene, beside a `capture-editor-chrome`
12
+ * frame of the same instant — the chrome door serves the PRESENTED frame, so
13
+ * the two are the same pixels):
14
+ *
15
+ * - `scene.background` as a `THREE.Color`: **no**. three does not shade a
16
+ * colour background, it CLEARS to it (`WebGLBackground.setClear` →
17
+ * `glClearColor`), so no fragment shader and no tone map ever touches it.
18
+ * Measured: the scene held `#494949` — this function's inversion of the
19
+ * palette's `#3f3f3f` — and the screen read `#494949` exactly, luminance
20
+ * 73 against Blender's 63. The inversion was compensating for a transform
21
+ * that does not run, and that alone is what made the stage light.
22
+ * - the floor grid (`editor-floor-grid`, a `ShaderMaterial` with
23
+ * `toneMapped: true`): **yes**. Measured: `uColor` held `#575757` — this
24
+ * function's inversion of the palette's `#545454` — and the 1 m line read
25
+ * 83–84 on screen, i.e. the operator ran and landed it on the palette.
26
+ * - the floor axes (`LineSegments2`/`LineMaterial`, `toneMapped: false`):
27
+ * **no**. `toneMapped: false` is honoured per material (three compiles the
28
+ * tone-map chunk per program, not as a composer output pass), so the
29
+ * inverted colour reached the screen raw and over-saturated: `#ee234d`
30
+ * where Blender's X axis is `#cb293f`.
31
+ *
32
+ * So the operator runs PER MATERIAL here, not in a final composer pass: the
33
+ * grid's `toneMapped: true` was mapped and the axes' `toneMapped: false` was
34
+ * not, in the same frame, through the same composer.
35
+ */
36
+ export function toneMappedSourceColor(
37
+ hex: number,
38
+ renderer: THREE.WebGLRenderer | undefined,
39
+ ): THREE.Color {
40
+ const target = new THREE.Color(hex); // sRGB hex → linear, three's management
41
+ if (!renderer || renderer.toneMapping === THREE.NoToneMapping) return target;
42
+ const exposure = renderer.toneMappingExposure || 1;
43
+ if (renderer.toneMapping === THREE.LinearToneMapping) return target.multiplyScalar(1 / exposure);
44
+ if (renderer.toneMapping === THREE.AgXToneMapping) return agxSourceColor(target, exposure);
45
+ if (renderer.toneMapping !== THREE.ACESFilmicToneMapping) return target;
46
+ const IN: Mat3 = [
47
+ [0.59719, 0.35458, 0.04823],
48
+ [0.076, 0.90834, 0.01566],
49
+ [0.0284, 0.13383, 0.83777],
50
+ ];
51
+ const OUT: Mat3 = [
52
+ [1.60475, -0.53108, -0.07367],
53
+ [-0.10208, 1.10813, -0.00605],
54
+ [-0.00327, -0.07276, 1.07602],
55
+ ];
56
+ const fit = (v: number) =>
57
+ (v * (v + 0.0245786) - 0.000090537) / (v * (0.983729 * v + 0.432951) + 0.238081);
58
+ const invertFit = (f: number) => {
59
+ let lo = 0;
60
+ let hi = 64;
61
+ for (let i = 0; i < 60; i++) {
62
+ const mid = (lo + hi) / 2;
63
+ if (fit(mid) < f) lo = mid;
64
+ else hi = mid;
65
+ }
66
+ return (lo + hi) / 2;
67
+ };
68
+ const f = mulMat3(invertMat3(OUT), [target.r, target.g, target.b]);
69
+ const u = f.map((c) => (c <= 0 ? 0 : invertFit(Math.min(c, 0.999)))) as Vec3;
70
+ const x = mulMat3(invertMat3(IN), u).map((c) => Math.max(0, c) * (0.6 / exposure)) as Vec3;
71
+ return new THREE.Color().setRGB(x[0], x[1], x[2], THREE.LinearSRGBColorSpace);
72
+ }
73
+ /**
74
+ * The same inversion for AgX — the operator the Blender look's stage runs
75
+ * (`ToolViewportDressing.toneMapping`), because Blender's own scene does.
76
+ *
77
+ * Every step of `AgXToneMapping` (`tonemapping_pars_fragment.glsl`) run
78
+ * backwards: the Rec.2020 round trip, the outset matrix, the 2.2 linearize,
79
+ * the sigmoid (bisected — the contrast approximation is a sixth-order
80
+ * polynomial with no closed inverse), the log2 encode, and the inset matrix.
81
+ * three spells its matrices COLUMN-major, as GLSL's `mat3(vec3, vec3, vec3)`
82
+ * constructor does; these are the transposes, because {@link mulMat3} is
83
+ * row-major.
84
+ */
85
+ function agxSourceColor(target: THREE.Color, exposure: number): THREE.Color {
86
+ // three spells its matrices COLUMN-major, as GLSL's `mat3(vec3, vec3, vec3)`
87
+ // constructor does; these are the transposes, because {@link mulMat3} is
88
+ // row-major. Every row sums to 1 — each of the four is grey-preserving, and
89
+ // that is the cheap check that the transposition is the right way round.
90
+ const SRGB_TO_REC2020: Mat3 = [
91
+ [0.6274, 0.3293, 0.0433],
92
+ [0.0691, 0.9195, 0.0113],
93
+ [0.0164, 0.088, 0.8956],
94
+ ];
95
+ const REC2020_TO_SRGB: Mat3 = [
96
+ [1.6605, -0.5876, -0.0728],
97
+ [-0.1246, 1.1329, -0.0083],
98
+ [-0.0182, -0.1006, 1.1187],
99
+ ];
100
+ const INSET: Mat3 = [
101
+ [0.856627153315983, 0.0951212405381588, 0.0482516061458583],
102
+ [0.137318972929847, 0.761241990602591, 0.101439036467562],
103
+ [0.11189821299995, 0.0767994186031903, 0.811302368396859],
104
+ ];
105
+ const OUTSET: Mat3 = [
106
+ [1.1271005818144368, -0.11060664309660323, -0.016493938717834573],
107
+ [-0.1413297634984383, 1.157823702216272, -0.016493938717834257],
108
+ [-0.14132976349843826, -0.11060664309660294, 1.2519364065950405],
109
+ ];
110
+ const MIN_EV = -12.47393;
111
+ const MAX_EV = 4.026069;
112
+ const contrast = (x: number) => {
113
+ const x2 = x * x;
114
+ const x4 = x2 * x2;
115
+ return (
116
+ 15.5 * x4 * x2 -
117
+ 40.14 * x4 * x +
118
+ 31.96 * x4 -
119
+ 6.868 * x2 * x +
120
+ 0.4298 * x2 +
121
+ 0.1191 * x -
122
+ 0.00232
123
+ );
124
+ };
125
+ const invertContrast = (f: number) => {
126
+ let lo = 0;
127
+ let hi = 1;
128
+ for (let i = 0; i < 60; i++) {
129
+ const mid = (lo + hi) / 2;
130
+ if (contrast(mid) < f) lo = mid;
131
+ else hi = mid;
132
+ }
133
+ return (lo + hi) / 2;
134
+ };
135
+ // Every step of the forward operator, backwards and in reverse order.
136
+ const rec2020 = mulMat3(SRGB_TO_REC2020, [target.r, target.g, target.b]);
137
+ const sigmoid = mulMat3(
138
+ invertMat3(OUTSET),
139
+ rec2020.map((c) => Math.max(0, c) ** (1 / 2.2)) as Vec3,
140
+ );
141
+ const open = sigmoid.map((c) => 2 ** (invertContrast(c) * (MAX_EV - MIN_EV) + MIN_EV)) as Vec3;
142
+ const linear = mulMat3(REC2020_TO_SRGB, mulMat3(invertMat3(INSET), open)).map(
143
+ (c) => Math.max(0, c) / exposure,
144
+ ) as Vec3;
145
+ return new THREE.Color().setRGB(linear[0], linear[1], linear[2], THREE.LinearSRGBColorSpace);
146
+ }
147
+ type Vec3 = [number, number, number];
148
+ type Mat3 = [Vec3, Vec3, Vec3];
149
+ function mulMat3(m: Mat3, v: Vec3): Vec3 {
150
+ return [
151
+ m[0][0] * v[0] + m[0][1] * v[1] + m[0][2] * v[2],
152
+ m[1][0] * v[0] + m[1][1] * v[1] + m[1][2] * v[2],
153
+ m[2][0] * v[0] + m[2][1] * v[1] + m[2][2] * v[2],
154
+ ];
155
+ }
156
+ function invertMat3(m: Mat3): Mat3 {
157
+ const [[a, b, c], [d, e, f], [g, h, i]] = m;
158
+ const A = e * i - f * h;
159
+ const B = -(d * i - f * g);
160
+ const C = d * h - e * g;
161
+ const det = a * A + b * B + c * C;
162
+ return [
163
+ [A / det, -(b * i - c * h) / det, (b * f - c * e) / det],
164
+ [B / det, (a * i - c * g) / det, -(a * f - c * d) / det],
165
+ [C / det, -(a * h - b * g) / det, (a * e - b * d) / det],
166
+ ];
167
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * The LOOK LANES' neutral studio IBL — `scene.environment` for every surface
3
+ * that photographs an asset instead of displaying it: the Asset Lab capture
4
+ * path (`asset-preview.ts`: the model-file lane, `vgai screenshot <module>`
5
+ * through `project.bake.preview`, labeled shot sets, source-review sheets,
6
+ * splats) and the asset-browser thumbnails (`model-thumbnail.ts`).
7
+ *
8
+ * Why it exists: a `MeshStandardMaterial` with `metalness: 1` has NO diffuse
9
+ * term — everything it shows is reflected environment. Lit by directional
10
+ * lights alone it renders BLACK with a pinprick specular, so iron reads as
11
+ * painted plastic, and the measured cost of that (cold barrel, 2026-08-29) is
12
+ * that agents detune `metalness` to survive the screenshot — the look lane
13
+ * silently teaching a wrong material value into shipped assets.
14
+ *
15
+ * The bake is three's own `RoomEnvironment` through `PMREMGenerator`, reused
16
+ * verbatim from the document viewport's dressing
17
+ * ({@link createStandardEnvironment}) so a capture and the open document light
18
+ * the same subject the same way. Procedural: no HDRI binary, deterministic
19
+ * frame to frame.
20
+ *
21
+ * Two rules this module exists to hold:
22
+ *
23
+ * - **`environment` only, never `background`.** Each lane keeps its own
24
+ * backdrop (the Asset Lab's neutral clear colour, or transparent). An IBL
25
+ * that also painted the background would change every existing frame's
26
+ * composition, not just its materials.
27
+ * - **One bake per renderer.** A PMREM target is GL-context-bound, so it
28
+ * cannot be shared across renderers, and baking it per SCENE would pay for
29
+ * it once per shot in a 24-frame orbit. The cache is keyed by renderer and
30
+ * freed through {@link disposeStudioEnvironment}, which every caller that
31
+ * disposes its renderer must call — a PMREM render target leaks otherwise.
32
+ */
33
+
34
+ import {
35
+ createStandardEnvironment,
36
+ type StandardEnvironment,
37
+ } from '@volter/editor-threejs/viewport/environment';
38
+ import type * as THREE from 'three';
39
+
40
+ /**
41
+ * How hard the IBL pushes against the lane's existing key/fill rig.
42
+ *
43
+ * The key and fill are UNCHANGED — they still own the form, the direction of
44
+ * the shading and the highlight. This is a reflection budget, not a second key.
45
+ *
46
+ * The blend was picked by reading before/after pairs of the barrel GLB and a
47
+ * metalness sweep in the module lane, holding the wood constant and asking how
48
+ * little environment buys back the metal:
49
+ * - 1.0 with the ambient untouched: the iron reads, the wood lifts and flattens
50
+ * — the shadow side of the staves fills in and the stave-to-stave tone
51
+ * variation collapses;
52
+ * - 0.55 with {@link STUDIO_AMBIENT_WITH_ENVIRONMENT} at 0.35: metal good,
53
+ * wood still visibly brighter and lower-contrast than the historical frame;
54
+ * - 0.45 with the ambient at 0.25 (shipped): the wood frame is indistinguishable
55
+ * from the historical one at full scale — same warmth, same grain, same
56
+ * terminator — while every hoop gains a gradient across the band and the
57
+ * bottom hoop stops reading as a shadow.
58
+ */
59
+ export const STUDIO_ENVIRONMENT_INTENSITY = 0.45;
60
+
61
+ /**
62
+ * The lanes' ambient fill is REDUCED when the IBL is present: a
63
+ * hemisphere/ambient light is a crude stand-in for exactly what the environment
64
+ * now does properly, and leaving both at full strength double-pays the ambient
65
+ * term and washes the subject. Each lane multiplies its own historical ambient
66
+ * intensity by this, so the total ambient energy lands near where it was and
67
+ * only its DIRECTIONALITY improves.
68
+ */
69
+ export const STUDIO_AMBIENT_WITH_ENVIRONMENT = 0.25;
70
+
71
+ const perRenderer = new WeakMap<THREE.WebGLRenderer, StandardEnvironment>();
72
+
73
+ /**
74
+ * Light `scene` with the studio IBL baked for `renderer`, baking it on first
75
+ * use for that renderer. `scene.background` is deliberately untouched.
76
+ */
77
+ export function applyStudioEnvironment(scene: THREE.Scene, renderer: THREE.WebGLRenderer): void {
78
+ let environment = perRenderer.get(renderer);
79
+ if (!environment) {
80
+ environment = createStandardEnvironment(renderer);
81
+ perRenderer.set(renderer, environment);
82
+ }
83
+ scene.environment = environment.texture;
84
+ scene.environmentIntensity = STUDIO_ENVIRONMENT_INTENSITY;
85
+ }
86
+
87
+ /**
88
+ * Free the bake this renderer owns. Idempotent, and safe for a renderer that
89
+ * never had one — call it beside `renderer.dispose()`.
90
+ */
91
+ export function disposeStudioEnvironment(renderer: THREE.WebGLRenderer): void {
92
+ const environment = perRenderer.get(renderer);
93
+ if (!environment) return;
94
+ perRenderer.delete(renderer);
95
+ environment.dispose();
96
+ }
@@ -0,0 +1,23 @@
1
+ export type ThreeViewportProjection = 'perspective' | 'orthographic';
2
+
3
+ export interface ThreeViewportPresentationState {
4
+ readonly projection: ThreeViewportProjection;
5
+ }
6
+
7
+ let state: ThreeViewportPresentationState = { projection: 'perspective' };
8
+ const listeners = new Set<() => void>();
9
+
10
+ export function threeViewportPresentation(): ThreeViewportPresentationState {
11
+ return state;
12
+ }
13
+
14
+ export function subscribeThreeViewportPresentation(listener: () => void): () => void {
15
+ listeners.add(listener);
16
+ return () => listeners.delete(listener);
17
+ }
18
+
19
+ export function setThreeViewportProjection(projection: ThreeViewportProjection): void {
20
+ if (state.projection === projection) return;
21
+ state = { ...state, projection };
22
+ for (const listener of listeners) listener();
23
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * The `play` channel every project contribution's props carry — ONE resolution
3
+ * of "which live game is this contribution looking at", read by all four
4
+ * contribution hosts (`components/ToolHost.tsx`,
5
+ * `components/InspectorToolSection.tsx`,
6
+ * `components/AssetInspectorToolSection.tsx`, and `@vgai/game`'s generation
7
+ * documents).
8
+ *
9
+ * A dev-GUI contribution's whole subject is the running game (owner ruling,
10
+ * `docs/ARCHITECTURE-CORE.md`, 2026-08-19), and this is its door to one.
11
+ *
12
+ * THE DEPENDENCY POINTS THE OTHER WAY, deliberately — play-mode PUBLISHES a
13
+ * reader here, and this module imports nothing of play-mode's. That is the
14
+ * same shape `performance-sources.ts` uses, and it is load-bearing rather than
15
+ * stylistic: an inspector section importing play-mode drags the entire runtime
16
+ * boot chain (renderer setup, vendored postprocessing, WebGL) into every
17
+ * module graph that mounts one — which is a real cost in the editor and an
18
+ * outright import-time crash in a headless host.
19
+ *
20
+ * RESOURCE OWNERSHIP: the reader is play-mode's, for the exact lifetime of one
21
+ * play session. It is the only writer, `publishToolContributionPlay(null)` on
22
+ * exit is the only teardown, and nothing here caches its result.
23
+ */
24
+
25
+ import type { ToolContributionPlay } from '@volter/editor-sdk/contributions';
26
+ import { subscribeInspectedInstance } from './authoring/active-systems';
27
+
28
+ type PlayReader = () => ToolContributionPlay | null;
29
+
30
+ let _read: PlayReader | null = null;
31
+ const _listeners = new Set<() => void>();
32
+
33
+ /** Publish the live reader for a play session, or `null` when one ends. */
34
+ export function publishToolContributionPlay(read: PlayReader | null): void {
35
+ _read = read;
36
+ for (const listener of _listeners) listener();
37
+ }
38
+
39
+ /** The inspected play instance, or `null` while nothing is playing. */
40
+ export function toolContributionPlay(): ToolContributionPlay | null {
41
+ return _read?.() ?? null;
42
+ }
43
+
44
+ /**
45
+ * Observe the two things that change WHICH game a contribution is bound to: a
46
+ * session starting or stopping (the publication above), and the Inspect
47
+ * selector moving between live seats. Both, because either alone leaves a host
48
+ * describing another mount's game — and the split is exactly the case where
49
+ * that is invisible.
50
+ */
51
+ export function subscribeToolContributionPlay(listener: () => void): () => void {
52
+ _listeners.add(listener);
53
+ const unsubscribeInstance = subscribeInspectedInstance(listener);
54
+ return () => {
55
+ _listeners.delete(listener);
56
+ unsubscribeInstance();
57
+ };
58
+ }
59
+
60
+ /**
61
+ * A stable identity for the bound instance — the `getSnapshot` for
62
+ * {@link subscribeToolContributionPlay}.
63
+ *
64
+ * A STRING rather than the `play` object itself, because
65
+ * `useSyncExternalStore` compares snapshots with `Object.is` and
66
+ * {@link toolContributionPlay} builds a fresh object on every call: returning
67
+ * that would report a change on every render and loop. The mount id is enough
68
+ * to key on because it is never reused (`play-mode.ts`'s instance-identity
69
+ * block), so a remount is always a new value.
70
+ */
71
+ export function toolContributionPlayKey(): string {
72
+ const play = toolContributionPlay();
73
+ return play ? `play:${play.instanceId}` : 'stopped';
74
+ }