@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,410 @@
1
+ import { Button, bg, border, danger, radius, TextArea, text } from '@volter/editor-sdk/widgets';
2
+ import { useState, useSyncExternalStore } from 'react';
3
+ import type { EditorAction } from '../action-registry';
4
+ import { registerDocumentOpener } from '../document-open-registry';
5
+ import type { ViewportTab } from '../editor-shell-store';
6
+ import {
7
+ getProjectTools,
8
+ projectToolsVersion,
9
+ refreshProjectTools,
10
+ runProjectTool,
11
+ subscribeProjectTools,
12
+ } from '../project-tools';
13
+ import {
14
+ getDocumentToolContributions,
15
+ getGlobalToolContributions,
16
+ getToolContributionLoadFailures,
17
+ subscribeToolContributions,
18
+ } from '../tool-loader';
19
+ import { waitUntil } from '../wait-until';
20
+ import {
21
+ activeWorkspaceDocumentId,
22
+ openWorkspaceDocument,
23
+ type WorkspaceDocumentContentProps,
24
+ } from '../workspace-document-registry';
25
+ import { registerWorkspaceDocumentRestorer } from '../workspace-document-restore';
26
+ import { openToolDocument } from './tool-documents';
27
+ import { defaultValueForSchema, ToolSchemaForm } from './tool-schema-form';
28
+
29
+ export const PROJECT_TOOLS_DOCUMENT_ID = 'project-tools';
30
+ export const PROJECT_TOOL_DOCUMENT_PREFIX = 'project-tool:';
31
+
32
+ export interface ProjectToolDocumentStore {
33
+ setActiveViewportTab(tab: ViewportTab): void;
34
+ }
35
+
36
+ function projectToolDocumentId(name: string): string {
37
+ return `${PROJECT_TOOL_DOCUMENT_PREFIX}${name}`;
38
+ }
39
+
40
+ export function openProjectToolsDocument(store: ProjectToolDocumentStore): void {
41
+ openWorkspaceDocument({
42
+ id: PROJECT_TOOLS_DOCUMENT_ID,
43
+ title: 'Project Tools',
44
+ kind: 'project-tool',
45
+ workspaceRole: 'workspace-reference',
46
+ Content: ProjectToolDocumentContent,
47
+ closeable: true,
48
+ presentation: () => ({ kind: 'workspace', id: PROJECT_TOOLS_DOCUMENT_ID }),
49
+ // The catalog is always truthfully reopenable — it lists whatever the
50
+ // project declares now — so it persists with no state of its own.
51
+ persist: () => ({ catalog: true }),
52
+ onActivate: () => store.setActiveViewportTab('edit'),
53
+ });
54
+ }
55
+
56
+ export function openProjectToolDocument(
57
+ store: ProjectToolDocumentStore | null,
58
+ name: string,
59
+ ): boolean {
60
+ const tool = getProjectTools().tools.find((entry) => entry.name === name);
61
+ if (!tool) return false;
62
+ const contributions = getDocumentToolContributions().filter((item) => item.tool?.name === name);
63
+ if (contributions.length === 1) return openToolDocument(store, contributions[0]!.id);
64
+ openWorkspaceDocument({
65
+ id: projectToolDocumentId(name),
66
+ title: tool.summary || tool.name,
67
+ kind: 'project-tool',
68
+ workspaceRole: 'workspace-reference',
69
+ provenance: { sourcePath: tool.sourcePath, origin: tool.name },
70
+ Content: ProjectToolDocumentContent,
71
+ closeable: true,
72
+ presentation: () => ({ kind: 'project-tool', name }),
73
+ persist: () => ({ name }),
74
+ ...(store ? { onActivate: () => store.setActiveViewportTab('edit') } : {}),
75
+ });
76
+ return true;
77
+ }
78
+
79
+ /**
80
+ * THE TWO ADDRESSES THIS MODULE ANSWERS (`document-open-registry.ts`), each
81
+ * the other half of a `presentation()` above: the CATALOG is a workspace
82
+ * document (`{ kind: 'workspace', id: 'project-tools' }`), one named tool is
83
+ * its own kind (`{ kind: 'project-tool', name }`). Registered at module load,
84
+ * the shape `packages/game/src/story-documents/three-story-documents.tsx:239`
85
+ * uses; the presenter addresses both without importing this file.
86
+ */
87
+ registerDocumentOpener<{ readonly id: string }>({
88
+ id: 'workspace',
89
+ owner: 'project-tool-documents',
90
+ // Every other workspace id belongs to the host's own workspace-document
91
+ // registry, or to the account document's opener.
92
+ open: (store, request) => {
93
+ if (request.id !== PROJECT_TOOLS_DOCUMENT_ID) return null;
94
+ openProjectToolsDocument(store);
95
+ return PROJECT_TOOLS_DOCUMENT_ID;
96
+ },
97
+ });
98
+
99
+ registerDocumentOpener<{ readonly name: string }>({
100
+ id: 'project-tool',
101
+ owner: 'project-tool-documents',
102
+ // `openProjectToolDocument` answers a boolean because it opens one of TWO
103
+ // documents — a single matching contribution's own tool document, or the
104
+ // generic runner — and the active id is which one it chose. That is exactly
105
+ // what the presenter read here before this was an address.
106
+ open: (store, request) =>
107
+ openProjectToolDocument(store, request.name) ? activeWorkspaceDocumentId() : null,
108
+ // The catalog is discovered asynchronously, so a durable address routinely
109
+ // names a tool before the project's tools have been read. Settling is the
110
+ // same refresh this kind's RESTORER prepares with (below), then the wait the
111
+ // presenter used to run as a poll around `open` itself.
112
+ settle: async (request) => {
113
+ await refreshProjectTools().catch(() => {});
114
+ if (await waitUntil(() => getProjectTools().tools.some((t) => t.name === request.name))) return;
115
+ throw new Error(`Project tool is not registered: ${request.name}`);
116
+ },
117
+ });
118
+
119
+ /** Reopen a persisted project-tool document: the catalog, or one registered
120
+ * callable's generic runner. The runner is self-verifying against the
121
+ * discovered catalog (`false` for a tool that is gone), which this kind's
122
+ * `prepare` has settled first. */
123
+ registerWorkspaceDocumentRestorer({
124
+ kind: 'project-tool',
125
+ owner: 'project-tool-documents',
126
+ prepare: () =>
127
+ refreshProjectTools().then(
128
+ () => {},
129
+ () => {},
130
+ ),
131
+ restore: ({ state, store }) => {
132
+ const record = state as { catalog?: unknown; name?: unknown } | null | undefined;
133
+ if (record?.catalog === true) {
134
+ openProjectToolsDocument(store);
135
+ return true;
136
+ }
137
+ return typeof record?.name === 'string' && openProjectToolDocument(store, record.name);
138
+ },
139
+ });
140
+
141
+ export function buildProjectToolActions(store: ProjectToolDocumentStore): EditorAction[] {
142
+ return [
143
+ {
144
+ id: 'project-tools:open',
145
+ label: 'Open Project Tools',
146
+ category: 'action',
147
+ execute: () => openProjectToolsDocument(store),
148
+ },
149
+ ...getProjectTools().tools.map((tool) => ({
150
+ id: `project-tool:${tool.name}`,
151
+ label: `Run: ${tool.summary || tool.name}`,
152
+ category: 'action' as const,
153
+ execute: () => void openProjectToolDocument(store, tool.name),
154
+ })),
155
+ ];
156
+ }
157
+
158
+ function JsonBlock({ value }: { value: unknown }) {
159
+ return <pre style={jsonStyle}>{JSON.stringify(value, null, 2)}</pre>;
160
+ }
161
+
162
+ export function ProjectToolDocumentContent({ documentId }: WorkspaceDocumentContentProps) {
163
+ useSyncExternalStore(subscribeProjectTools, projectToolsVersion);
164
+ useSyncExternalStore(subscribeToolContributions, getGlobalToolContributions);
165
+ const snapshot = getProjectTools();
166
+ const toolName = documentId.startsWith(PROJECT_TOOL_DOCUMENT_PREFIX)
167
+ ? documentId.slice(PROJECT_TOOL_DOCUMENT_PREFIX.length)
168
+ : null;
169
+ const tool = toolName ? snapshot.tools.find((entry) => entry.name === toolName) : null;
170
+ if (!toolName) {
171
+ return (
172
+ <div style={rootStyle} data-testid="project-tools-catalog">
173
+ <h2 style={headingStyle}>Project Tools</h2>
174
+ <p style={{ ...mutedStyle, margin: '0 0 18px', maxWidth: 720, lineHeight: 1.5 }}>
175
+ Explicitly registered in <code>package.json#vgai.tools</code>. Each tool is an ordinary
176
+ project function; a registration may pair it with optional editor contributions.
177
+ </p>
178
+ <div style={{ display: 'grid', gap: 8 }}>
179
+ {snapshot.tools.map((entry) => (
180
+ <Button
181
+ key={entry.name}
182
+ variant="ghost"
183
+ className="vgai-project-tool-card"
184
+ onClick={() => openProjectToolDocument(null, entry.name)}
185
+ data-testid={`project-command-card:${entry.name}`}
186
+ >
187
+ <span style={cardTitleRowStyle}>
188
+ <strong style={{ color: text[1], fontWeight: 600 }}>{entry.summary}</strong>
189
+ <span style={openLabelStyle}>Open</span>
190
+ </span>
191
+ <code style={{ color: text[2], fontSize: 'var(--vgai-font-sm)' }}>{entry.name}</code>
192
+ <span style={cardMetaRowStyle}>
193
+ <span>
194
+ {entry.host} · {entry.permission.risk} · {entry.sourcePath}
195
+ </span>
196
+ </span>
197
+ </Button>
198
+ ))}
199
+ </div>
200
+ {snapshot.tools.length === 0 && snapshot.loadErrors.length === 0 && (
201
+ <div style={mutedStyle}>No project tools registered.</div>
202
+ )}
203
+ {snapshot.loadErrors.map((error) => (
204
+ <div key={`${error.sourcePath}:${error.message}`} style={errorStyle}>
205
+ {error.sourcePath}: {error.message}
206
+ </div>
207
+ ))}
208
+ {/* The server's `loadErrors` cover the CALLABLES it imported in node;
209
+ contributions are only scanned there and imported in the tab, so a
210
+ contribution that throws on import can appear nowhere else on this
211
+ page — which is how a standing document went invisibly absent. */}
212
+ {getToolContributionLoadFailures().map((failure) => (
213
+ <div key={`${failure.entryPath}:${failure.error}`} style={errorStyle}>
214
+ {failure.entryPath}: {failure.error}
215
+ </div>
216
+ ))}
217
+ </div>
218
+ );
219
+ }
220
+
221
+ if (!tool) {
222
+ return <div style={rootStyle}>This tool is no longer available.</div>;
223
+ }
224
+
225
+ return <ProjectToolRunner key={tool.name} tool={tool} />;
226
+ }
227
+
228
+ function ProjectToolRunner({
229
+ tool,
230
+ }: {
231
+ tool: ReturnType<typeof getProjectTools>['tools'][number];
232
+ }) {
233
+ const presentations = getDocumentToolContributions().filter(
234
+ (item) => item.tool?.name === tool.name,
235
+ );
236
+ const initial = defaultValueForSchema(tool.inputSchema);
237
+ const [input, setInput] = useState<Record<string, unknown>>(
238
+ initial && typeof initial === 'object' && !Array.isArray(initial)
239
+ ? (initial as Record<string, unknown>)
240
+ : {},
241
+ );
242
+ const [rawText, setRawText] = useState(() => JSON.stringify(input, null, 2));
243
+ const [result, setResult] = useState<unknown>(null);
244
+ const [running, setRunning] = useState(false);
245
+ const [inputError, setInputError] = useState<string | null>(null);
246
+
247
+ const runnable = tool.host === 'node';
248
+ const execute = async () => {
249
+ const confirm =
250
+ tool.permission.risk === 'read' ||
251
+ window.confirm(`${tool.permission.summary}\n\nRun ${tool.name}?`);
252
+ if (!confirm) return;
253
+ setRunning(true);
254
+ try {
255
+ setResult(await runProjectTool(tool.name, input, { confirm: true }));
256
+ } catch (error) {
257
+ setResult({ ok: false, error: { code: 'REQUEST_FAILED', message: String(error) } });
258
+ } finally {
259
+ setRunning(false);
260
+ }
261
+ };
262
+
263
+ return (
264
+ <div style={rootStyle} data-testid={`project-tool:${tool.name}`}>
265
+ <h2 style={headingStyle}>{tool.summary}</h2>
266
+ <code style={{ color: text[2] }}>{tool.name}</code>
267
+ <p style={{ color: text[2] }}>{tool.description}</p>
268
+ <div style={mutedStyle}>
269
+ {tool.host} · {tool.permission.risk} · {tool.sourcePath}
270
+ </div>
271
+ {presentations.length > 0 && (
272
+ <section style={{ marginTop: 18, maxWidth: 760 }}>
273
+ <h3 style={{ margin: '0 0 8px', fontSize: 13 }}>Presentations</h3>
274
+ <div style={{ display: 'grid', gap: 8 }}>
275
+ {presentations.map((presentation) => (
276
+ <Button
277
+ key={presentation.id}
278
+ variant="ghost"
279
+ className="vgai-project-tool-card"
280
+ onClick={() => openToolDocument(null, presentation.id)}
281
+ >
282
+ <span style={cardTitleRowStyle}>
283
+ <strong style={{ color: text[1], fontWeight: 600 }}>{presentation.title}</strong>
284
+ <span style={openLabelStyle}>Open</span>
285
+ </span>
286
+ </Button>
287
+ ))}
288
+ </div>
289
+ </section>
290
+ )}
291
+ {!runnable && (
292
+ <div style={errorStyle}>
293
+ This runner supports Node-hosted tools. Import the underlying function directly in browser
294
+ or runtime code.
295
+ </div>
296
+ )}
297
+ <details open={presentations.length === 0} style={{ marginTop: 18, maxWidth: 760 }}>
298
+ <summary style={{ cursor: 'pointer', color: text[2], fontSize: 12 }}>
299
+ {presentations.length > 0 ? 'Advanced · invoke native schema' : 'Input'}
300
+ </summary>
301
+ <section style={{ marginTop: 12 }}>
302
+ <ToolSchemaForm
303
+ schema={tool.inputSchema}
304
+ value={input}
305
+ onChange={(next) => {
306
+ setInput(next);
307
+ setRawText(JSON.stringify(next, null, 2));
308
+ setInputError(null);
309
+ }}
310
+ />
311
+ </section>
312
+ {inputError && <div style={errorStyle}>{inputError}</div>}
313
+ <Button
314
+ variant="primary"
315
+ disabled={!runnable || running || inputError !== null}
316
+ aria-busy={running}
317
+ onClick={() => void execute()}
318
+ style={{ marginTop: 16 }}
319
+ >
320
+ {running ? 'Running…' : 'Run tool'}
321
+ </Button>
322
+ <details style={{ marginTop: 16 }}>
323
+ <summary style={labelStyle}>Advanced JSON</summary>
324
+ <TextArea
325
+ id={`tool-input:${tool.name}`}
326
+ value={rawText}
327
+ onChange={(event) => {
328
+ const nextText = event.target.value;
329
+ setRawText(nextText);
330
+ try {
331
+ const parsed = JSON.parse(nextText) as unknown;
332
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
333
+ throw new Error('Input must be a JSON object.');
334
+ }
335
+ setInput(parsed as Record<string, unknown>);
336
+ setInputError(null);
337
+ } catch (error) {
338
+ setInputError(error instanceof Error ? error.message : String(error));
339
+ }
340
+ }}
341
+ rows={10}
342
+ data-variant="code"
343
+ style={textareaStyle}
344
+ />
345
+ <details>
346
+ <summary style={labelStyle}>Input schema</summary>
347
+ <JsonBlock value={tool.inputSchema} />
348
+ </details>
349
+ </details>
350
+ </details>
351
+ {result !== null && (
352
+ <section>
353
+ <div style={labelStyle}>Result</div>
354
+ <JsonBlock value={result} />
355
+ </section>
356
+ )}
357
+ </div>
358
+ );
359
+ }
360
+
361
+ const rootStyle: React.CSSProperties = {
362
+ width: '100%',
363
+ maxWidth: 960,
364
+ margin: '0 auto',
365
+ padding: 24,
366
+ boxSizing: 'border-box',
367
+ overflow: 'auto',
368
+ pointerEvents: 'auto',
369
+ color: text[1],
370
+ fontSize: 'var(--vgai-font-base)',
371
+ };
372
+ const headingStyle: React.CSSProperties = { margin: '0 0 6px', fontSize: 20 };
373
+ const mutedStyle: React.CSSProperties = { color: text[2], fontSize: 'var(--vgai-font-sm)' };
374
+ const labelStyle: React.CSSProperties = { display: 'block', margin: '14px 0 6px', color: text[1] };
375
+ const cardTitleRowStyle: React.CSSProperties = {
376
+ display: 'flex',
377
+ width: '100%',
378
+ alignItems: 'center',
379
+ justifyContent: 'space-between',
380
+ gap: 12,
381
+ };
382
+ const openLabelStyle: React.CSSProperties = {
383
+ color: text[2],
384
+ fontSize: 'var(--vgai-font-sm)',
385
+ fontWeight: 500,
386
+ };
387
+ const cardMetaRowStyle: React.CSSProperties = {
388
+ display: 'flex',
389
+ width: '100%',
390
+ minWidth: 0,
391
+ alignItems: 'center',
392
+ justifyContent: 'space-between',
393
+ gap: 12,
394
+ color: text[2],
395
+ fontSize: 'var(--vgai-font-sm)',
396
+ };
397
+ const errorStyle: React.CSSProperties = { marginTop: 10, color: danger, whiteSpace: 'pre-wrap' };
398
+ const textareaStyle: React.CSSProperties = {
399
+ width: '100%',
400
+ boxSizing: 'border-box',
401
+ resize: 'vertical',
402
+ };
403
+ const jsonStyle: React.CSSProperties = {
404
+ overflow: 'auto',
405
+ padding: 10,
406
+ background: bg.inset,
407
+ border: `1px solid ${border[1]}`,
408
+ borderRadius: radius.sm,
409
+ color: text[2],
410
+ };
@@ -0,0 +1,223 @@
1
+ /** Scene-table navigation delegates rendering to installed document owners. */
2
+ import { openRegisteredDocument } from '../document-open-registry';
3
+ import type { ViewportTab } from '../editor-shell-store';
4
+ import { projectAdapterFacet, type ResolvedDocumentTable, subscribeProjectAdapter } from '../project-adapter';
5
+ import { planSceneDocument, sceneTabRow } from '../scene-document-plan';
6
+ import { openAvailableWorkspaceDocument } from '../workspace-available-documents';
7
+ import { activateWorkspaceDocument, setWorkspaceDocumentTitle } from '../workspace-document-registry';
8
+ import { rootDocumentId } from '../world-document-routing';
9
+ import { kindDocumentId, openKindDocument } from './kind-documents';
10
+
11
+ export interface SceneDocumentStore { setActiveViewportTab(tab: ViewportTab): void; }
12
+ export function reconcileSceneDocuments(_store: SceneDocumentStore, table: ResolvedDocumentTable | null): void {
13
+ if (!table) return;
14
+ for (const row of sceneTabRow(table)) {
15
+ if (row.plan.kind !== 'root-document') continue;
16
+ const id = rootDocumentId(row.plan.regionId);
17
+ if (id) setWorkspaceDocumentTitle(id, row.plan.title);
18
+ }
19
+ }
20
+ export function startSceneDocuments(store: SceneDocumentStore): () => void {
21
+ const run = () => reconcileSceneDocuments(store, projectAdapterFacet()?.scenes ?? null);
22
+ run();
23
+ return subscribeProjectAdapter(run);
24
+ }
25
+
26
+ export interface SceneOpenRefusal {
27
+ readonly ok: false;
28
+ readonly code:
29
+ | 'SCENE_TABLE_UNAVAILABLE'
30
+ | 'SCENE_NOT_FOUND'
31
+ | 'SCENE_NOT_OPENABLE'
32
+ /** Reached ONLY through the running game's own navigation, and no game is
33
+ * running here. Distinct from `SCENE_NOT_OPENABLE` because it is not a
34
+ * dead end: starting the game makes it openable (`scene-live-open.ts`). */
35
+ | 'SCENE_NAVIGATION_NOT_RUNNING'
36
+ | 'SCENE_DOCUMENT_NOT_MOUNTED';
37
+ readonly error: string;
38
+ readonly known?: readonly string[];
39
+ }
40
+
41
+ export type SceneOpenResult =
42
+ | { readonly ok: true; readonly documentId: string; readonly title: string }
43
+ | SceneOpenRefusal;
44
+
45
+ /**
46
+ * OPEN one piece of the adapter's scene table by id — the Edit half of the
47
+ * table's `open` verb, and the same verb for a scene, a prefab, or a story
48
+ * state (ARCHITECTURE-CORE §Editor: "any other piece … opens as a sibling
49
+ * document through the same verb").
50
+ *
51
+ * Every refusal is CODED because the classes are graded differently: a table
52
+ * that has not loaded is a session fact, an unknown id is a caller error, an
53
+ * unopenable entry is the GAME's own declaration (quoted verbatim), and a
54
+ * document the host has not mounted is a host gap. Prose alone cannot separate
55
+ * them.
56
+ */
57
+ /**
58
+ * How long {@link openSceneTableEntryWhenListed} will wait for the project
59
+ * adapter's table before refusing.
60
+ *
61
+ * MEASURED, not chosen: three cold `vgai edit` sessions on a
62
+ * `--template models` probe, opening `model:src/models/cube.blend` in a poll
63
+ * loop from the moment the tab connected — **6025 ms, 6831 ms, 6474 ms**, and
64
+ * in all three the first refusal was `SCENE_TABLE_UNAVAILABLE`, i.e. the
65
+ * adapter had not loaded at all. So for ~6.4 s after the editor reports
66
+ * "Editor ready" and the tab is present, the ONE general door for putting a
67
+ * document on screen answered "no". Every walk in the stage-transport unit
68
+ * had to wrap `editor.open` in a retry loop to get past it, and the frame
69
+ * hits the same window from the other side (WORK.md NEXT item 2).
70
+ *
71
+ * 15 s is that worst case with headroom for a slower box. It is a BOUND and
72
+ * not a fix for slowness: if opening a document ever legitimately needs
73
+ * longer than this, the thing to change is why, not this number.
74
+ */
75
+ const SCENE_TABLE_WAIT_MS = 15_000;
76
+
77
+ /**
78
+ * {@link openSceneTableEntry}, but WAITING for the adapter's table first.
79
+ *
80
+ * WHY THE WAIT CANNOT STOP AT THE FIRST RESOLUTION: a table resolves MORE
81
+ * THAN ONCE — a contributed finder (the mesh capability's models finder)
82
+ * registers after the first resolution and the table is resolved again with
83
+ * its entries — so "the adapter answered and your id is not in it" is not yet
84
+ * "your id does not exist". `kind-documents.tsx`'s `whenDocumentEntry` learned
85
+ * this on 2026-09-05 and has waited correctly ever since; this door never did,
86
+ * and that asymmetry IS the defect: two doors onto one table, one patient and
87
+ * one not.
88
+ *
89
+ * BOTH REFUSALS SURVIVE UNCHANGED. On timeout this returns exactly what the
90
+ * synchronous door would have returned at that moment — `SCENE_TABLE_UNAVAILABLE`
91
+ * if the adapter never loaded, `SCENE_NOT_FOUND` with the known ids if it did.
92
+ * The cost of the patience is paid by a genuinely unknown id, which now takes
93
+ * the full bound to be told so; that is the right side to lose on, because the
94
+ * alternative is the 2026-09-05 bug — refusing a document that was about to
95
+ * exist.
96
+ */
97
+ export async function openSceneTableEntryWhenListed(
98
+ store: SceneDocumentStore,
99
+ id: string,
100
+ ): Promise<SceneOpenResult> {
101
+ const listed = (): boolean =>
102
+ projectAdapterFacet()?.scenes.entries.some((candidate) => candidate.id === id) ?? false;
103
+ if (!listed()) {
104
+ await new Promise<void>((resolve) => {
105
+ let done = false;
106
+ const finish = (): void => {
107
+ if (done) return;
108
+ done = true;
109
+ clearTimeout(timer);
110
+ stop();
111
+ resolve();
112
+ };
113
+ const timer = setTimeout(finish, SCENE_TABLE_WAIT_MS);
114
+ const stop = subscribeProjectAdapter(() => {
115
+ if (listed()) finish();
116
+ });
117
+ // The table can resolve between the check above and the subscribe.
118
+ if (listed()) finish();
119
+ });
120
+ }
121
+ return openSceneTableEntry(store, id);
122
+ }
123
+
124
+ export function openSceneTableEntry(store: SceneDocumentStore, id: string): SceneOpenResult {
125
+ const facet = projectAdapterFacet();
126
+ if (!facet) {
127
+ return {
128
+ ok: false,
129
+ code: 'SCENE_TABLE_UNAVAILABLE',
130
+ error: 'open: the project adapter has not loaded yet, so there is no scene table to open.',
131
+ };
132
+ }
133
+ const entry = facet.scenes.entries.find((candidate) => candidate.id === id);
134
+ if (!entry) {
135
+ const known = facet.scenes.entries.map((candidate) => candidate.id);
136
+ return {
137
+ ok: false,
138
+ code: 'SCENE_NOT_FOUND',
139
+ error: `open: "${id}" is not in the adapter's scene table. Known ids: ${
140
+ known.length > 0 ? known.join(', ') : '(the table is empty)'
141
+ }.`,
142
+ known,
143
+ };
144
+ }
145
+ // A KIND document (a model, a page — any entry a project contribution
146
+ // authors, `kind-documents.tsx`) is opened the way a Content double-click
147
+ // opens it. Before this, `open` fell through to the scene planner, which
148
+ // asked the entry for a region and refused it by name — so an agent had no
149
+ // door to put the model it was writing on screen (2026-09-06).
150
+ if (
151
+ entry.kind !== 'scene' &&
152
+ entry.kind !== 'prefab' &&
153
+ openKindDocument(store, entry, { activate: true })
154
+ ) {
155
+ return { ok: true, documentId: kindDocumentId(entry.id), title: entry.label };
156
+ }
157
+ const plan = planSceneDocument(entry);
158
+ switch (plan.kind) {
159
+ case 'no-document':
160
+ // A LIVE-REACHED scene is not a dead end — it is a scene this session
161
+ // cannot reach because nothing is running. The verb tries the live half
162
+ // FIRST (`command-listener.ts`), so arriving here means there was no
163
+ // game. ANY `liveReach` earns this code: the planner sets the field only
164
+ // for the reaches the live half actually OPENS (`scene-document-plan.ts`
165
+ // states which), so "present" and "start the game and ask again" are
166
+ // the same statement.
167
+ if (plan.liveReach !== undefined) {
168
+ return {
169
+ ok: false,
170
+ code: 'SCENE_NAVIGATION_NOT_RUNNING',
171
+ error: `open: ${plan.reason} No game is running in this session.`,
172
+ };
173
+ }
174
+ return {
175
+ ok: false,
176
+ code: 'SCENE_NOT_OPENABLE',
177
+ // The adapter's own sentence, unaltered — see `scene-document-plan.ts`.
178
+ error: `open: "${id}" has no Edit document — ${plan.reason}`,
179
+ };
180
+ case 'root-document': {
181
+ const documentId = rootDocumentId(plan.regionId);
182
+ if (!documentId) {
183
+ return {
184
+ ok: false,
185
+ code: 'SCENE_DOCUMENT_NOT_MOUNTED',
186
+ error: `open: "${id}" is region "${plan.regionId}"'s own document, and no document is mounted for that region in this session.`,
187
+ };
188
+ }
189
+ // Reopen the registered root document if its tab was closed.
190
+ openAvailableWorkspaceDocument(documentId) || activateWorkspaceDocument(documentId);
191
+ return { ok: true, documentId, title: plan.title };
192
+ }
193
+ case 'isolation-document': {
194
+ const documentId = openRegisteredDocument('scene', store, { entry, activate: true });
195
+ return documentId
196
+ ? { ok: true, documentId, title: plan.title }
197
+ : { ok: false, code: 'SCENE_NOT_OPENABLE', error: `No installed scene editor can open "${id}".` };
198
+ }
199
+ case 'story-document': {
200
+ // The composed id is the whole address; resolving it, and saying WHICH
201
+ // way it failed, is the story kind's own job — it reports the specific
202
+ // reason (an id no composed story answers to, or a module whose medium
203
+ // is undeclared) to the session console, and this verb refuses with the
204
+ // one code the caller can act on.
205
+ const documentId = openRegisteredDocument('story', store, {
206
+ storyId: plan.storyId,
207
+ title: plan.title,
208
+ });
209
+ if (documentId === null) {
210
+ return {
211
+ ok: false,
212
+ code: 'SCENE_DOCUMENT_NOT_MOUNTED',
213
+ error:
214
+ `open: "${id}" is reached by story "${plan.storyId}", and nothing in this editor ` +
215
+ "opened it — either this project's story registry does not hold that id, or its " +
216
+ 'module declares no medium (declare it via `vgai.adapter.ts` regionIncludes or a ' +
217
+ 'manifest root entry). The session console names which.',
218
+ };
219
+ }
220
+ return { ok: true, documentId, title: plan.title };
221
+ }
222
+ }
223
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * A STAGE'S OWN KEYBOARD ACTIONS — the transform modes, the view presets,
3
+ * Frame, pivot, snap and the vertex-snap hold, bound for the stage whose
4
+ * document is ACTIVE.
5
+ *
6
+ * They belong to the STAGE, and until 2026-09-19 they were mounted from
7
+ * `stage-overlay-set.tsx`, which a stage only reaches when it has the shell
8
+ * above it (`StageHost.tsx`'s `shellStore && !chromeless`). A document
9
+ * contributed by a PACKAGE mounts outside `EditorProvider`, so the Model
10
+ * document reached NONE of them: measured live in the Code-OSS frame on a
11
+ * models project, the editor's action table held 29 entries — the shell set
12
+ * alone — and the stage's nine (`transform.combined/translate/rotate/scale`,
13
+ * `viewport.toggleSnap/frameSelection/cyclePivot/snapToFloor`, the four view
14
+ * presets) were absent, so G/R/S on the Model stage answered nothing at all.
15
+ * The overlay set is an OVERLAY condition (a bounded host must not pay for
16
+ * the collaboration client it reaches); which keys a stage answers is not,
17
+ * and the two were one line by accident.
18
+ *
19
+ * The ONE registry still means ONE holder (`hotkeys.ts`'s scope table, and
20
+ * `key-actions.ts`'s action table under the frame): the active
21
+ * document's stage, which is exactly the condition below.
22
+ *
23
+ * Lazily imported by `StageHost.tsx` so a bounded host neither loads nor pays
24
+ * for `editor-hotkeys.ts`'s closure — the same boundary, and the same reason,
25
+ * as `StageOverlays.tsx`.
26
+ */
27
+ import { useEffect } from 'react';
28
+ import { registerEditorViewportHotkeys } from '../editor-hotkeys';
29
+ import type { StageHandle } from './stage-overlay-set';
30
+
31
+ export function StageKeyboardBinding({ stage }: { readonly stage: StageHandle }): null {
32
+ useEffect(
33
+ () => registerEditorViewportHotkeys(stage.store, stage.viewport, stage.canvas),
34
+ [stage.store, stage.viewport, stage.canvas],
35
+ );
36
+ return null;
37
+ }