@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,160 @@
1
+ /**
2
+ * The creation-site registry — the host-side half of the index whose
3
+ * source half is `server/creation-site-transform.ts`.
4
+ *
5
+ * ANTI-SHIM: the record NEVER touches the object. A `WeakMap` keyed by the
6
+ * constructed object is the whole storage, so an ingested game cannot see that
7
+ * it is being indexed — no new property, no symbol, no `userData` key, nothing
8
+ * enumerable, nothing a `for…in`/`Object.keys`/JSON round-trip can pick up.
9
+ * (This is the same rule the ingest adapter follows for its structural ids'
10
+ * sole exception, `userData.entityId`, which is a deliberate, documented
11
+ * identity tag; a provenance record has no such justification.)
12
+ *
13
+ * RESOURCE OWNERSHIP (brief-rule 4).
14
+ * - THE MAP is owned by this module and scoped to the editor page's realm.
15
+ * Every same-realm mount in the page shares it, which is correct: the
16
+ * stamped modules are ordinary modules in that one realm and their `new`
17
+ * expressions have exactly one place to report to.
18
+ * - IT IS NEVER CLEARED, deliberately. A `WeakMap` holds no strong reference,
19
+ * so a record dies with the object it describes — a game teardown that
20
+ * dropped its scene has already emptied the registry of everything that
21
+ * mattered. An explicit `clear()` on teardown would be a no-op dressed as
22
+ * cleanup, and it would be WRONG for the upgrade path, where a late-captured
23
+ * mount reuses objects constructed before the capture.
24
+ * - THE INSTALLED GLOBAL is the ownable resource, and it has one owner:
25
+ * `installGatedGameGlobals` (`gated-globals.ts`) installs it alongside the
26
+ * other `__vgai*` game-realm globals, once, at the same point in the
27
+ * lifecycle. {@link installCreationSiteRecorder} returns an uninstall for
28
+ * tests, which is the only caller that needs to take it back down.
29
+ */
30
+
31
+ /** A source location a stamped `new` expression reported. */
32
+ export interface CreationSite {
33
+ /** Project-root-RELATIVE path, POSIX separators (e.g. `src/scripts/sim/city.js`). */
34
+ file: string;
35
+ /** 1-based. */
36
+ line: number;
37
+ /** 0-based character offset. */
38
+ col: number;
39
+ }
40
+
41
+ /**
42
+ * What the read surfaces show for one object: either the anchor, or a NAMED
43
+ * reason there is none. There is no third, blank state — an unanchored object
44
+ * says why (the honest-floor rule).
45
+ */
46
+ export type CreationSiteAnchor =
47
+ | ({ anchored: true; kind: 'source'; display: string } & CreationSite)
48
+ | { anchored: false; reason: string };
49
+
50
+ /** The reason for an object that exists but was not constructed by a `new`
51
+ * expression in a served project module — a three.js internal, a GLTF
52
+ * loader's mesh, anything from a bundled dependency. The anchoring does not
53
+ * reach these, so this is what the surfaces say, verbatim. */
54
+ export const UNANCHORED_REASON =
55
+ 'constructed outside project source (library internals or loader output)';
56
+
57
+ /** The reason for a selection with no live object behind it at all. */
58
+ export const NO_OBJECT_REASON = 'no live object for this selection';
59
+
60
+ const sites = new WeakMap<object, CreationSite>();
61
+
62
+ /**
63
+ * How many objects each anchor has constructed — the MULTIPLICITY signal.
64
+ *
65
+ * SimCity builds all 256 tiles from one line (`const tile = new Tile(x, y)`,
66
+ * `src/scripts/sim/city.js:59`), so "can this edit be written at its creation
67
+ * site?" has a second answer beyond "is there a literal there": writing that
68
+ * line would move every tile, and writing it for one tile is a lie about what
69
+ * the source says. Nothing else in the system knows the count — the WeakMap
70
+ * above is deliberately un-enumerable — so it is counted here, at the one place
71
+ * every construction reports to.
72
+ *
73
+ * A PLAIN `Map`, not a WeakMap, and never decremented: the count is over
74
+ * CONSTRUCTIONS, not live objects. That is the conservative direction — a site
75
+ * that built and dropped a hundred objects still refuses, which costs a refusal
76
+ * the user can read, where a count that forgot its history would eventually let
77
+ * a shared site be written as if it were unique. It is keyed by the site string,
78
+ * so it holds no reference to anything the game owns and cannot leak an object.
79
+ */
80
+ const instances = new Map<string, number>();
81
+
82
+ function siteKey(site: CreationSite): string {
83
+ return `${site.file}:${site.line}:${site.col}`;
84
+ }
85
+
86
+ /** The global name the served prelude calls through (must match
87
+ * `CREATION_SITE_GLOBAL` in `server/creation-site-transform.ts`; the pairing is
88
+ * asserted by `creation-site-registry.test.ts`). */
89
+ export const CREATION_SITE_GLOBAL = '__vgaiRecordCreationSite';
90
+
91
+ type Recorder = (o: unknown, file: string, line: number, col: number) => void;
92
+
93
+ /**
94
+ * Record one construction. FIRST WRITE WINS: a constructor that returns an
95
+ * already-registered object (a cache, a singleton, an interned value) must not
96
+ * have its true creation site overwritten by the call site that merely asked
97
+ * for it again.
98
+ */
99
+ export function recordCreationSite(o: unknown, file: string, line: number, col: number): void {
100
+ if (o === null || (typeof o !== 'object' && typeof o !== 'function')) return;
101
+ if (sites.has(o as object)) return;
102
+ const site: CreationSite = { file, line, col };
103
+ sites.set(o as object, site);
104
+ const key = siteKey(site);
105
+ instances.set(key, (instances.get(key) ?? 0) + 1);
106
+ }
107
+
108
+ /**
109
+ * How many objects this anchor has constructed. `0` for a site nothing was ever
110
+ * recorded at. Only `1` may be written by a single-object edit; a deliberate
111
+ * creation-site-default edit may address the full count after naming it — see
112
+ * `creation-site-edit.ts`'s `CreationSiteWriteScope`.
113
+ */
114
+ export function instancesAtSite(site: CreationSite): number {
115
+ return instances.get(siteKey(site)) ?? 0;
116
+ }
117
+
118
+ /** Test-only: forget every recorded construction count. The WeakMap needs no
119
+ * equivalent (it is collected with its keys) and deliberately has none. */
120
+ export function resetCreationSiteCountsForTest(): void {
121
+ instances.clear();
122
+ }
123
+
124
+ /** The raw record for an object, or `null`. */
125
+ export function creationSiteOf(o: unknown): CreationSite | null {
126
+ if (o === null || (typeof o !== 'object' && typeof o !== 'function')) return null;
127
+ return sites.get(o as object) ?? null;
128
+ }
129
+
130
+ /** `src/scripts/sim/city.js:59` — the text the inspector shows and `vgai eval` reads. */
131
+ export function formatCreationSite(site: CreationSite): string {
132
+ return `${site.file}:${site.line}`;
133
+ }
134
+
135
+ /**
136
+ * The read-surface answer for one object: the anchor, or the named reason there
137
+ * isn't one. `null`/a non-object means the caller had no live object to ask
138
+ * about, which is a DIFFERENT honest answer from "the library built it".
139
+ */
140
+ export function creationSiteAnchor(o: unknown): CreationSiteAnchor {
141
+ if (o === null || o === undefined) return { anchored: false, reason: NO_OBJECT_REASON };
142
+ const site = creationSiteOf(o);
143
+ if (!site) return { anchored: false, reason: UNANCHORED_REASON };
144
+ return { anchored: true, kind: 'source', ...site, display: formatCreationSite(site) };
145
+ }
146
+
147
+ /**
148
+ * Publish {@link recordCreationSite} on `globalThis` for the served prelude.
149
+ * Idempotent. Returns an uninstall — see the module header for who owns this.
150
+ */
151
+ export function installCreationSiteRecorder(): () => void {
152
+ const g = globalThis as unknown as Record<string, unknown>;
153
+ const existing = g[CREATION_SITE_GLOBAL];
154
+ if (typeof existing === 'function') return () => {};
155
+ const recorder: Recorder = recordCreationSite;
156
+ g[CREATION_SITE_GLOBAL] = recorder;
157
+ return () => {
158
+ if (g[CREATION_SITE_GLOBAL] === recorder) delete g[CREATION_SITE_GLOBAL];
159
+ };
160
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * WHICH CODING HARNESSES CAN BE DELEGATED TO — the eleventh registry in the
3
+ * family of `workspace-document-restore.ts` (a kind owns its persisted state),
4
+ * `document-open-registry.ts` (a kind owns how it opens), `chrome-slot-registry
5
+ * .ts` (the host owns the place, a package owns what sits there),
6
+ * `content-entry-source-registry.ts` (the host owns the Content scope, a
7
+ * package owns why a component is content) and the seven since. Here: the host
8
+ * owns the DELEGATE FORM — the task text, the isolation choice, the branch
9
+ * name, the button, the notice line and `delegateEditorTask` itself — and a
10
+ * package owns WHICH AGENTS EXIST.
11
+ *
12
+ * WHY IT EXISTS. `components/WorktreeSwitcher.tsx`'s Delegate form was the
13
+ * single edge holding `api/agents.ts` and its three wire modules in the
14
+ * editor's eager import closure (WORK.md §The open-source launch, the four
15
+ * that remained of the harness bucket): it called `getHarnessChatSnapshot()`
16
+ * to list which harnesses are installed, and it named two of them by hand
17
+ * (`claude`, `codex`) for its container isolation. A base library for making
18
+ * IDEs should not know the name of a coding agent.
19
+ *
20
+ * THE DOOR THE RECORD NAMED WAS A BIGGER ONE, AND THE CODE DID NOT WANT IT.
21
+ * Unit 21 specified "a chrome slot that can hand its filler a dismiss/refresh
22
+ * callback", i.e. the whole form becoming `@vgai/agents`' surface. Measured
23
+ * against the form: of its ~120 lines, everything but the harness `<Select>`
24
+ * is the worktree board's own vocabulary — the branch slug, the three
25
+ * isolations, the copy about sibling checkouts and bounded containers, and
26
+ * `delegateEditorTask` (`api/worktrees.ts`, a `keep`-bucket module whose wire
27
+ * already takes a `harness` string). Moving it would have put the board's
28
+ * delegation UI inside the agents package AND changed `ChromeSlotFiller` —
29
+ * whose whole claim is that a slot is a PLACE, rendered `<filler.Content />`
30
+ * with no props — for every existing filler. What the host actually cannot
31
+ * answer is one question, so one question is what it asks.
32
+ *
33
+ * ASKED PER ISOLATION, because the answer differs and only the source knows
34
+ * how: a worktree or in-place delegation needs a harness that is INSTALLED and
35
+ * can start, while a bounded container authenticates the harness inside its
36
+ * own temporary home, so "installed here" is not the question. The host passes
37
+ * the isolation and reads a list; which of its harnesses satisfies which is
38
+ * the package's to decide.
39
+ *
40
+ * NOTHING REGISTERED IS A REAL ANSWER. With no source, the picker says "No
41
+ * harness ready" and Delegate stays disabled — the same honest emptiness a
42
+ * document kind with no registered opener gives. That is not a regression
43
+ * risk for any shipped build: `WorktreeSwitcher` renders only in `server`
44
+ * editor mode, and every dev session loads `@vgai/agents` through
45
+ * `SESSION_PACKAGES` (`server/project-tools.ts`), while a build with no
46
+ * session has no board at all.
47
+ */
48
+
49
+ /** How a delegated task is isolated from the caller's checkout — the board's
50
+ * own vocabulary, passed to a source as the question's context. */
51
+ export type DelegateIsolation = 'worktree' | 'current' | 'container';
52
+
53
+ export interface DelegateHarness {
54
+ /** The value `delegateEditorTask({ harness })` takes. */
55
+ readonly id: string;
56
+ /** What the picker shows. */
57
+ readonly label: string;
58
+ }
59
+
60
+ export interface DelegateHarnessSource {
61
+ /** Which module registered it. Re-registering the same owner REPLACES, so
62
+ * an HMR re-evaluation leaves one source, not two. */
63
+ readonly owner: string;
64
+ /** Ascending; ties keep registration order. */
65
+ readonly order?: number;
66
+ /** Asking is a fetch, so this is async; the form re-asks whenever the
67
+ * isolation or the registry version changes. */
68
+ readonly harnesses: (isolation: DelegateIsolation) => Promise<readonly DelegateHarness[]>;
69
+ }
70
+
71
+ const _sources: DelegateHarnessSource[] = [];
72
+ const listeners = new Set<() => void>();
73
+ let version = 0;
74
+
75
+ function publish(): void {
76
+ version++;
77
+ for (const listener of listeners) listener();
78
+ }
79
+
80
+ /** Install a source. Returns the teardown. */
81
+ export function registerDelegateHarnessSource(source: DelegateHarnessSource): () => void {
82
+ const stale = _sources.findIndex((item) => item.owner === source.owner);
83
+ if (stale >= 0) _sources.splice(stale, 1);
84
+ _sources.push(source);
85
+ _sources.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
86
+ publish();
87
+ return () => {
88
+ const at = _sources.indexOf(source);
89
+ if (at < 0) return;
90
+ _sources.splice(at, 1);
91
+ publish();
92
+ };
93
+ }
94
+
95
+ /**
96
+ * THE HOST'S ONE QUESTION. Every source's answer, in registration order, with
97
+ * the first claim on an id winning — two packages offering the same harness is
98
+ * one harness, and the one that registered earlier named it.
99
+ *
100
+ * A source that throws is skipped rather than taking the form down with it:
101
+ * the picker degrades to the harnesses that did answer, which is the same
102
+ * shape as no source at all.
103
+ */
104
+ export async function delegateHarnesses(
105
+ isolation: DelegateIsolation,
106
+ ): Promise<readonly DelegateHarness[]> {
107
+ const seen = new Set<string>();
108
+ const out: DelegateHarness[] = [];
109
+ for (const source of _sources) {
110
+ let answered: readonly DelegateHarness[];
111
+ try {
112
+ answered = await source.harnesses(isolation);
113
+ } catch {
114
+ continue;
115
+ }
116
+ for (const harness of answered) {
117
+ if (seen.has(harness.id)) continue;
118
+ seen.add(harness.id);
119
+ out.push(harness);
120
+ }
121
+ }
122
+ return out;
123
+ }
124
+
125
+ /** The form's `useSyncExternalStore` subscription: a source arriving with a
126
+ * contribution pass re-asks the question. */
127
+ export function subscribeDelegateHarnessSources(listener: () => void): () => void {
128
+ listeners.add(listener);
129
+ return () => {
130
+ listeners.delete(listener);
131
+ };
132
+ }
133
+
134
+ export function delegateHarnessRegistryVersion(): number {
135
+ return version;
136
+ }
137
+
138
+ /** Test-only reset (mirrors the restore, open, chrome-slot and content-entry
139
+ * registries'). */
140
+ export function __resetDelegateHarnessSourcesForTest(): void {
141
+ _sources.length = 0;
142
+ publish();
143
+ }
@@ -0,0 +1,177 @@
1
+ import {
2
+ object3DDocumentSession,
3
+ object3DDocumentStageAnnounced,
4
+ } from './authoring/object3d-document-session-registry';
5
+ import { DOCUMENT_REGISTRATION_TIMEOUT_MS, waitUntil } from './wait-until';
6
+
7
+ /**
8
+ * THE OPEN DOCUMENT'S OWN CONTEXT, published for the REPL door.
9
+ *
10
+ * A tool document (`workspace.document`) may hand the host ONE object — the
11
+ * live session it edits through — and `editor.document.run(ctx => …)`
12
+ * (`vgai eval`) runs a wire-carried step against it, in Edit mode, without
13
+ * play. This is the "explore in the REPL, commit as source" half of the
14
+ * parity program's one spine (docs/BLENDER-PARITY.md §Execution model): the
15
+ * mesh document publishes its `MeshEditSession`, whose `ctx` is the
16
+ * bpy-shaped edit context, so an agent phrases a modeling step the way the
17
+ * corpus does — context + selection + op — and reads the result back.
18
+ *
19
+ * Keyed by workspace document id; the door resolves the ACTIVE document and
20
+ * refuses by name when it published nothing (a data sheet, a story). A
21
+ * document re-publishes whenever its session object changes (a reload swaps
22
+ * it); publishing returns the unpublish.
23
+ *
24
+ * ITS SIBLING FACT, and the reason they share a module: whether a contributed
25
+ * document has MOUNTED AT ALL. Both are things a contribution's own React root
26
+ * tells the host about itself, and both exist because `ToolHost.tsx` renders
27
+ * that contribution into a SEPARATE root (`createEditorRoot(...).render(...)`),
28
+ * which React schedules — so nothing in the host's own commit can say whether
29
+ * the contribution has rendered yet. Every door that must not read a document
30
+ * "before it decided what it is" waits on one of these two.
31
+ */
32
+
33
+ const _contexts = new Map<string, unknown>();
34
+ const _listeners = new Set<() => void>();
35
+ const _mounted = new Set<string>();
36
+ const _mountListeners = new Set<() => void>();
37
+
38
+ let _version = 0;
39
+
40
+ function _notifyContexts(): void {
41
+ _version += 1;
42
+ for (const listener of [..._listeners]) listener();
43
+ }
44
+
45
+ export function publishDocumentContext(documentId: string, context: unknown): () => void {
46
+ _contexts.set(documentId, context);
47
+ _notifyContexts();
48
+ return () => {
49
+ if (_contexts.get(documentId) === context) _contexts.delete(documentId);
50
+ };
51
+ }
52
+
53
+ /**
54
+ * THE PUBLISHED CONTEXT HAS MOVED — said by the package that DRIVES the
55
+ * document, not by the one that published it.
56
+ *
57
+ * The context object is a live handle, so a package can change what it answers
58
+ * without ever republishing: `@volter/editor-blender` reads the engine through its RNA
59
+ * door and the answer decides which Properties TABS exist for the selected
60
+ * datablock (an armature has a Bone tab, a cube does not). Nothing in the
61
+ * host's own stores moves when that answer arrives, so before this existed the
62
+ * inspector had no reason to re-compose and the rail stayed at whatever the
63
+ * first render could see.
64
+ *
65
+ * `use-active-inspection.ts` subscribes to {@link documentContextVersion}, so
66
+ * this makes the whole inspection re-derive — matches included.
67
+ */
68
+ export function notifyDocumentContextChanged(documentId: string): void {
69
+ if (!_contexts.has(documentId)) return;
70
+ _notifyContexts();
71
+ }
72
+
73
+ export function subscribeDocumentContexts(listener: () => void): () => void {
74
+ _listeners.add(listener);
75
+ return () => {
76
+ _listeners.delete(listener);
77
+ };
78
+ }
79
+
80
+ export function documentContextVersion(): number {
81
+ return _version;
82
+ }
83
+
84
+ export function documentContextFor(documentId: string): unknown {
85
+ return _contexts.get(documentId);
86
+ }
87
+
88
+ /** Opening a document activates its tab before an async model import can
89
+ * publish its context. The REPL waits for that publication; it never executes
90
+ * against a placeholder or mistakes a loading document for an unsupported one. */
91
+ export function waitForDocumentContext(documentId: string, timeoutMs = 10_000): Promise<unknown> {
92
+ const current = documentContextFor(documentId);
93
+ if (current !== undefined) return Promise.resolve(current);
94
+ return new Promise((resolve) => {
95
+ const onPublish = () => {
96
+ const context = documentContextFor(documentId);
97
+ if (context === undefined) return;
98
+ clearTimeout(timeout);
99
+ _listeners.delete(onPublish);
100
+ resolve(context);
101
+ };
102
+ const timeout = setTimeout(() => {
103
+ _listeners.delete(onPublish);
104
+ resolve(undefined);
105
+ }, timeoutMs);
106
+ _listeners.add(onPublish);
107
+ });
108
+ }
109
+
110
+ export function __resetDocumentContextsForTest(): void {
111
+ _contexts.clear();
112
+ _mounted.clear();
113
+ }
114
+
115
+ /**
116
+ * A CONTRIBUTED DOCUMENT'S REACT ROOT HAS COMMITTED — published by the host's
117
+ * own wrapper INSIDE that root (`ToolHost.tsx`), so every child effect has run
118
+ * by the time it fires. That ordering is the whole point: it is what lets an
119
+ * opener's `ready` read an absent stage announcement as "this document has no
120
+ * stage" rather than "the stage has not rendered yet"
121
+ * (`authoring/object3d-document-session-registry.ts`).
122
+ */
123
+ export function markContributedDocumentMounted(documentId: string): () => void {
124
+ _mounted.add(documentId);
125
+ for (const listener of [..._mountListeners]) listener();
126
+ return () => {
127
+ _mounted.delete(documentId);
128
+ };
129
+ }
130
+
131
+ /** Resolves once this document's contribution has committed, `false` if the
132
+ * bounded window elapses first — the same shape and reason as
133
+ * {@link waitForDocumentContext} above. */
134
+ export function waitForContributedDocumentMount(
135
+ documentId: string,
136
+ timeoutMs = 10_000,
137
+ ): Promise<boolean> {
138
+ if (_mounted.has(documentId)) return Promise.resolve(true);
139
+ return new Promise((resolve) => {
140
+ const onMount = () => {
141
+ if (!_mounted.has(documentId)) return;
142
+ clearTimeout(timeout);
143
+ _mountListeners.delete(onMount);
144
+ resolve(true);
145
+ };
146
+ const timeout = setTimeout(() => {
147
+ _mountListeners.delete(onMount);
148
+ resolve(false);
149
+ }, timeoutMs);
150
+ _mountListeners.add(onMount);
151
+ });
152
+ }
153
+
154
+ /**
155
+ * THE SHARED HALF OF EVERY OPENER'S `ready` (`document-open-registry.ts`): a
156
+ * document whose stage announced itself
157
+ * (`authoring/object3d-document-session-registry.ts`) is not ready to be
158
+ * driven until that stage has registered its session, and one that announced
159
+ * nothing is ready already. Each caller has already waited for its own MOUNT
160
+ * to settle — the asset family for its Inspector, a contributed document for
161
+ * the React root above — which is what makes an absent announcement mean
162
+ * "this document has no stage" rather than "the stage has not rendered yet".
163
+ *
164
+ * The refusal sentence is the one `editor-view-presentation.ts` used to throw,
165
+ * moved to the kinds' own side unchanged.
166
+ */
167
+ export async function awaitAnnouncedObject3DDocumentSession(documentId: string): Promise<void> {
168
+ if (!object3DDocumentStageAnnounced(documentId)) return;
169
+ if (
170
+ await waitUntil(
171
+ () => object3DDocumentSession(documentId) !== null,
172
+ DOCUMENT_REGISTRATION_TIMEOUT_MS,
173
+ )
174
+ )
175
+ return;
176
+ throw new Error(`The requested Object3D view did not mount for document: ${documentId}`);
177
+ }
@@ -0,0 +1,200 @@
1
+ /**
2
+ * A DOCUMENT KIND OWNS HOW IT OPENS — the forward half of the seam whose
3
+ * backward half is `workspace-document-restore.ts` (docs/WORKBENCH.md §host
4
+ * versus package).
5
+ *
6
+ * THE RULE. The host ADDRESSES a document (a story on a module, an asset at a
7
+ * path); it does not construct one. A module that can open a kind registers an
8
+ * opener under a named id, and every host call site — the palette, the
9
+ * hierarchy's double-click, the Edit tab row, the layout restore, the view
10
+ * protocol, the SDK's `host.workspace` door — asks for that id instead of
11
+ * importing the document module. When nothing has registered the id (a build
12
+ * whose package list does not carry that kind, a contribution that has not
13
+ * loaded yet) the answer is `null`, and the caller does what it already did
14
+ * for a document it could not open.
15
+ *
16
+ * WHY IT EXISTS. `workspace-document-restore.ts` fixed the PERSISTENCE half:
17
+ * the host no longer names a document module to reopen one. The opening half
18
+ * was still `import { openThreeStoryDocument } from './components/…'` in ten
19
+ * host files, which is the same serialization defect one rung earlier — a
20
+ * document cannot leave for a package while the shell calls its constructor by
21
+ * name. Measured 2026-09-18 (WORK.md §The open-source launch, phase 1 unit 4).
22
+ *
23
+ * WHAT AN ID IS. A string in the host's own vocabulary, declared beside the
24
+ * kind's other host-side contracts (`stories/story-document-openers.ts` is the
25
+ * first), with a request type declared in the SAME place so both sides of the
26
+ * seam are typed against one declaration rather than against each other.
27
+ * Several modules may register one id; they are tried in registration order
28
+ * until one returns a document id.
29
+ */
30
+
31
+ import type { WorkspaceStateStore } from './workspace-document-restore';
32
+
33
+ export interface DocumentOpener<Request> {
34
+ /** The opener id — the address vocabulary, not a module name. */
35
+ readonly id: string;
36
+ /** Which module registered it. Re-registering the same owner+id REPLACES,
37
+ * so an HMR re-evaluation leaves one opener, not two. */
38
+ readonly owner: string;
39
+ /**
40
+ * Open (or activate) the document this request addresses, and answer its
41
+ * document id. `null` = this opener cannot serve THIS request (an
42
+ * undeclared medium, a story that is no longer exported); the next
43
+ * registered opener is tried.
44
+ *
45
+ * THROWING is the third answer, and it is different from `null`: "this
46
+ * address is MINE and it is broken" — a table that lists no such entry, a
47
+ * contribution whose module threw on import. The throw reaches the caller
48
+ * with the kind's own sentence instead of the host's generic "nothing can
49
+ * open this", which is the only way a reader learns which of the two
50
+ * happened. Only an opener that OWNS the id may throw; one of several
51
+ * openers sharing an id answers `null` so its siblings still get their turn.
52
+ */
53
+ readonly open: (store: WorkspaceStateStore, request: Request) => string | null;
54
+ /**
55
+ * The kind's own async settling step, awaited by
56
+ * {@link openRegisteredDocumentAsync} ONLY after a first synchronous attempt
57
+ * answered `null` — refresh the ledger this kind opens documents out of, then
58
+ * the attempt is made once more.
59
+ *
60
+ * It mirrors `WorkspaceDocumentRestorer.prepare` deliberately: a kind is the
61
+ * only thing that knows what "I might be able to open this once I have looked
62
+ * again" means, and the host must not hold that knowledge to address a
63
+ * document. Absent means one attempt is the whole answer.
64
+ *
65
+ * IT RECEIVES THE REQUEST, because settling is per-address: waiting for THIS
66
+ * table entry, loading THIS module, refreshing the contribution that would
67
+ * carry THIS id. `stories/story-opener.ts:92` is the evidence —
68
+ * it keeps two module-level `unsettled*` sets whose only job is to carry the
69
+ * failed request across to a settle that could not see it. (Existing openers
70
+ * that ignore the argument are unaffected; the dedupe below is by function
71
+ * identity, so one settle still runs once per attempt.) Like `open`, it may
72
+ * THROW to refuse the address with the kind's own sentence.
73
+ */
74
+ readonly settle?: (request: Request) => Promise<void>;
75
+ /**
76
+ * The kind's own POST-open readiness — `settle`'s mirror on the other side of
77
+ * the answer, awaited by {@link openRegisteredDocumentAsync} once an opener
78
+ * has produced a document id, before the caller is told the view is present.
79
+ *
80
+ * It exists because "opened" and "ready to be driven" are different instants
81
+ * for some kinds and the host cannot know which: an asset document answers
82
+ * its id the moment the tab is registered, while an immediate
83
+ * inspect/setField against it needs the Inspector mounted and (for a model)
84
+ * its live adapter installed — `waitForAssetDocumentInspector`, which the
85
+ * presenter used to await by hand because it knew the asset family's rule.
86
+ * Absent means the id IS the whole answer. May THROW to refuse.
87
+ *
88
+ * A synchronous caller ({@link openRegisteredDocument}) cannot await it, the
89
+ * same way it cannot settle — it gets the id and the kind's own doors are
90
+ * what it waits on.
91
+ */
92
+ readonly ready?: (documentId: string, request: Request) => Promise<void>;
93
+ }
94
+
95
+ // The registry is heterogeneous by construction — one id, one request type,
96
+ // checked at both ends by the shared declaration the id comes from.
97
+ // biome-ignore lint/suspicious/noExplicitAny: the map is keyed by id, not by type.
98
+ const _openers = new Map<string, DocumentOpener<any>[]>();
99
+
100
+ /** Install an opener. Returns the teardown. */
101
+ export function registerDocumentOpener<Request>(opener: DocumentOpener<Request>): () => void {
102
+ const existing = _openers.get(opener.id) ?? [];
103
+ const next = existing.filter((item) => item.owner !== opener.owner);
104
+ next.push(opener);
105
+ _openers.set(opener.id, next);
106
+ return () => {
107
+ const current = _openers.get(opener.id);
108
+ if (!current) return;
109
+ const remaining = current.filter((item) => item !== opener);
110
+ if (remaining.length === 0) _openers.delete(opener.id);
111
+ else _openers.set(opener.id, remaining);
112
+ };
113
+ }
114
+
115
+ /** Whether anything can open this id in THIS session — what a palette asks
116
+ * before offering an action that would otherwise do nothing. */
117
+ export function hasDocumentOpener(id: string): boolean {
118
+ return (_openers.get(id)?.length ?? 0) > 0;
119
+ }
120
+
121
+ /**
122
+ * Every address this session can open, sorted. The generic answer to "what
123
+ * DOES this editor open?", which is what a refusal owes its reader: a build
124
+ * whose package list carries no opener for the kind is a configuration fact,
125
+ * and naming the addresses that ARE registered is the difference between
126
+ * "this is broken" and "this editor is not that editor".
127
+ */
128
+ export function registeredDocumentOpenerIds(): readonly string[] {
129
+ return [..._openers.keys()].sort();
130
+ }
131
+
132
+ /**
133
+ * Open the document this request addresses, through whichever registered
134
+ * opener claims it. `null` = nothing here can open it, which is the same
135
+ * answer the host already gives for a story with no declared medium.
136
+ */
137
+ export function openRegisteredDocument<Request>(
138
+ id: string,
139
+ store: WorkspaceStateStore,
140
+ request: Request,
141
+ ): string | null {
142
+ for (const opener of _openers.get(id) ?? []) {
143
+ const opened = opener.open(store, request);
144
+ if (opened !== null) return opened;
145
+ }
146
+ return null;
147
+ }
148
+
149
+ /**
150
+ * The same question, asked where the caller can wait: try the registered
151
+ * openers once; if none claimed the request, let each kind SETTLE (refresh the
152
+ * ledger it opens out of) and try once more. Used by the VIEW PROTOCOL, which
153
+ * is already async and is the one caller that addresses a document by a name a
154
+ * person or an agent typed — the subject may simply not have been fetched yet.
155
+ */
156
+ export async function openRegisteredDocumentAsync<Request>(
157
+ id: string,
158
+ store: WorkspaceStateStore,
159
+ request: Request,
160
+ ): Promise<string | null> {
161
+ const openers = _openers.get(id) ?? [];
162
+ const direct = claim(openers, store, request);
163
+ if (direct) return settleReady(direct, request);
164
+ if (openers.length === 0) return null;
165
+ const settled = new Set<(request: Request) => Promise<void>>();
166
+ for (const opener of openers) if (opener.settle) settled.add(opener.settle);
167
+ for (const settle of settled) await settle(request);
168
+ if (settled.size === 0) return null;
169
+ const retried = claim(openers, store, request);
170
+ return retried === null ? null : settleReady(retried, request);
171
+ }
172
+
173
+ /** The first opener that answers a document id, and WHICH one — the async door
174
+ * needs the identity because `ready` belongs to the opener that claimed, not
175
+ * to every opener sharing the address. */
176
+ function claim<Request>(
177
+ // biome-ignore lint/suspicious/noExplicitAny: the map is keyed by id, not by type.
178
+ openers: readonly DocumentOpener<any>[],
179
+ store: WorkspaceStateStore,
180
+ request: Request,
181
+ ): { opener: DocumentOpener<Request>; documentId: string } | null {
182
+ for (const opener of openers) {
183
+ const documentId = opener.open(store, request);
184
+ if (documentId !== null) return { opener, documentId };
185
+ }
186
+ return null;
187
+ }
188
+
189
+ async function settleReady<Request>(
190
+ claimed: { opener: DocumentOpener<Request>; documentId: string },
191
+ request: Request,
192
+ ): Promise<string> {
193
+ await claimed.opener.ready?.(claimed.documentId, request);
194
+ return claimed.documentId;
195
+ }
196
+
197
+ /** Test-only reset (mirrors the restore registry's own). */
198
+ export function __resetDocumentOpenersForTest(): void {
199
+ _openers.clear();
200
+ }