@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,1143 @@
1
+ /**
2
+ * THE ADAPTER LOADER — the host half of `vgai.adapter.ts`.
3
+ *
4
+ * ARCHITECTURE-CORE §The editor protocol: every game supplies a SERVER — its
5
+ * adapter — and the editor is a universal CLIENT of it. This module is where a
6
+ * project's adapter becomes a live binding table:
7
+ *
8
+ * 1. the project has `vgai.adapter.ts` ⇒ **the project's own file wins,
9
+ * always**. It is imported through the SAME door every other
10
+ * project-owned module goes through (`project-module-url.ts`'s
11
+ * `fsImportPath` — the ONE owner of `/@fs/` urls for project code, PD-3)
12
+ * and its default export is parsed by `parseAdapterDefinition`, which
13
+ * rejects an unknown key by name. This branch is FIRST because a game
14
+ * supplies its own protocol server: a project that ships the declaration
15
+ * has stated its table, and no host-side table may quietly outrank it —
16
+ * not even when its ingest root id happens to match a registry key
17
+ * (a user's `cuberun` folder is not ours);
18
+ * 2. the project is an INGEST GAME whose adapter the host's in-tree registry
19
+ * holds, and it shipped none of its own ⇒ that module is the adapter.
20
+ * Placement follows REALM (ARCHITECTURE-CORE §The editor protocol): a
21
+ * vendored game's bytes are served verbatim, so its host-realm declaration
22
+ * lives in `ingest/games/<id>/vgai.adapter.ts` and is imported through the
23
+ * registry glob, never through the project's file routes. **This branch is
24
+ * REACHABLE only because the step-1 probe can answer "no"**: the realm
25
+ * rule keeps a vendored game's adapter out of its served bundle, but
26
+ * "the file is not on disk" and "the probe says absent" were different
27
+ * answers until `probeProjectFile` learned to recognize the SPA fallback
28
+ * — a 200 serving `index.html` for every missing project-root path, on
29
+ * both the dev server and the hosted surface. An existence check reading
30
+ * `res.ok` is a constant TRUE here and makes this branch dead code, which
31
+ * is exactly how every vendored game silently lost its scene table once.
32
+ * **A binding taken from here is NEVER silent**:
33
+ * the facet's `source` says `'registry'` and its `modulePath` names the
34
+ * repo file, so `vgai status` reports whose declaration is running. A
35
+ * project bound this way did not ship that table; the host did, and the
36
+ * user is entitled to read that off the same door as everything else;
37
+ * 3. the project has NONE ⇒ it gets `nativeAdapter()`. **That absence IS the
38
+ * declared native default, not a silent fallback**: the same binding table
39
+ * the template ships explicitly, chosen because the project declared
40
+ * nothing else — there is no third behavior for "no adapter".
41
+ *
42
+ * The ordering also settles the one folder that can answer BOTH probes: an
43
+ * in-tree ingest fixture keeps its manifest and its adapter side by side, so
44
+ * opening `ingest/games/<id>/` as a folder project reaches THE SAME FILE
45
+ * through the project door. One declaration, one winner, decided by a rule that
46
+ * does not ask which population the project belongs to.
47
+ *
48
+ * It is also the ONE sanctioned importer of `@editor/finders`
49
+ * (`packages/engine/test/finder-import-boundary.test.ts` is the tripwire). The
50
+ * engine never runs a finder nobody selected; the loader runs exactly the
51
+ * selections the adapter's table names, with the inputs gathered here.
52
+ *
53
+ * The resolved table is published as the `adapter` facet of `/__editor/state`
54
+ * (`command-listener.ts`'s `collectState`), which is the minimal proof-of-load
55
+ * client: `vgai status` / `vgai eval 'await editor.status()'` read which
56
+ * adapter loaded, its regions, and its scene table.
57
+ *
58
+ * The OBSERVATION table has one client and it is not the facet: the ingest
59
+ * mount's system-adapter assembly (`ingest/ingest-render-debug.ts`'s
60
+ * `wireIngestSystems`) projects the declarations onto the host's ordinary
61
+ * `SystemAdapters.debug` alongside the game's own contract projection, so
62
+ * `game.providers()` / `game.state()` reach an adapter declaration through the
63
+ * SAME door first-party content uses. The facet publishes only the statically
64
+ * readable half (ids, kinds, whether an answer is bound); the closures travel
65
+ * host-side through `adapter-observation.ts`, because a closure cannot be a
66
+ * wire shape.
67
+ *
68
+ * The scene table has TWO real clients (ARCHITECTURE-CORE §Editor). The EDIT
69
+ * TAB ROW is the first: `components/scene-documents.tsx` subscribes to this
70
+ * facet and projects `scenes` as workspace documents — every authorable scene
71
+ * a tab, the default open by default — and `command-listener.ts`'s `open` verb
72
+ * resolves one entry through the same pure decision table
73
+ * (`scene-document-plan.ts`). Play's `open()`, walking the table as live
74
+ * navigation, is the second and is a later rung.
75
+ */
76
+
77
+ import type { EntrypointSource, FinderInput, ProjectComponentRef } from '@editor/finders';
78
+ import {
79
+ type FinderResult,
80
+ type ProjectSourceFile,
81
+ registerContributedFinder,
82
+ runFinderSelection,
83
+ } from '@editor/finders';
84
+ import { fsImportPath } from '@volter/editor-sdk/session/project-module-url';
85
+ import type {
86
+ AdapterDefinition,
87
+ AdapterRegion,
88
+ DocumentEntry,
89
+ ObservationDeclaration,
90
+ ObservationKind,
91
+ } from '@volter/editor-project/adapter/adapter-module';
92
+ import {
93
+ nativeAdapter,
94
+ parseAdapterDefinition,
95
+ regionsFromManifestRoots,
96
+ unmatchedRegionIncludeIds,
97
+ } from '@volter/editor-project/adapter/adapter-module';
98
+ import { declaredRoots, ingestRoots } from '@volter/editor-project/adapter/manifest-interpreter';
99
+ import type { ResolvedGameManifest } from '@volter/editor-project/manifest/load';
100
+ import { activeProjectKey } from './active-project';
101
+ import { setAdapterEditorConfiguration } from './adapter-editor-config';
102
+ import { setAdapterInputBinding, setAdapterObservations } from './adapter-observation';
103
+ import { connectSourceFileEvents } from './asset-events';
104
+ import {
105
+ listProjectComponents,
106
+ listProjectSourceFiles,
107
+ probeProjectFile,
108
+ readProjectTextFile,
109
+ } from './editor-api';
110
+ import { editorConsole } from './editor-console';
111
+ import { fetchGameManifest } from './manifest-project';
112
+ import { setPresentationRegions } from './presentation-surface';
113
+ import { type ActiveProject, getCurrentProject, onProjectChange } from './project-manager';
114
+ import { projectModuleChangeMatches, subscribeProjectModuleChange } from './project-module-changes';
115
+ import { notifyProjectShapeChanged, registerDocumentKindsSupplier } from './project-shape';
116
+ import { resolveRelativeSpecifier } from './resolve-relative-specifier';
117
+ import { storyPrefabsFinder } from './stories/prefabs-finder';
118
+ import {
119
+ contributedFinderModules,
120
+ documentContributionForKind,
121
+ refreshProjectToolContributions,
122
+ subscribeToolContributions,
123
+ } from './tool-loader';
124
+
125
+ /** The game's adapter module lives beside `vgai.project.json`, by contract. */
126
+ export const ADAPTER_MODULE_FILENAME = 'vgai.adapter.ts';
127
+
128
+ /** The resolved scene table — the adapter's declarations plus what its own
129
+ * finder selections found. */
130
+ export interface ResolvedDocumentTable {
131
+ readonly entries: readonly DocumentEntry[];
132
+ /** `null` = nothing settled a default, which stays a fact rather than a pick. */
133
+ readonly default: string | null;
134
+ }
135
+
136
+ /**
137
+ * One observation slot as it travels over `/__editor/state` — the statically
138
+ * readable half of an {@link ObservationDeclaration}, and nothing else.
139
+ *
140
+ * `answer` is a CLOSURE the host evaluates against the mounted game, so it
141
+ * cannot be serialized and is deliberately not here; `answered` carries the one
142
+ * fact its presence states. That split is the two-state rule made visible on
143
+ * the wire: `answered: false` reads as "declared, nothing binds it yet", which
144
+ * is a different answer from the slot not appearing at all. The closures
145
+ * themselves stay host-side in `adapter-observation.ts`, where the mount reads
146
+ * them.
147
+ */
148
+ export interface ProjectObservationFacet {
149
+ readonly id: string;
150
+ readonly kind: ObservationKind;
151
+ /** Whether the declaration binds an `answer`. */
152
+ readonly answered: boolean;
153
+ }
154
+
155
+ export interface ProjectAdapterFacet {
156
+ /**
157
+ * WHOSE declaration is running — the one field that answers it, because
158
+ * "a module loaded" never did.
159
+ *
160
+ * - `project` — the project's own `vgai.adapter.ts` supplied the table.
161
+ * - `registry` — the HOST's in-tree ingest registry supplied it, matched on
162
+ * this project's ingest root id. The project did not ship this
163
+ * declaration; a reader must be able to see that without pattern-matching
164
+ * a path, which is why it is a value here and not an inference from
165
+ * {@link modulePath}.
166
+ * - `native` — the project declared none and got `nativeAdapter()`, the
167
+ * declared native default.
168
+ */
169
+ readonly source: 'project' | 'registry' | 'native';
170
+ /**
171
+ * Where the loaded module came from, or `null` for the native default:
172
+ * project-relative for a project that owns its own file, and the REPO path
173
+ * (`packages/editor/src/ingest/games/<id>/vgai.adapter.ts`) for a registry
174
+ * binding, whose adapter is host-realm by the placement rule and therefore
175
+ * has no project-relative home to name.
176
+ */
177
+ readonly modulePath: string | null;
178
+ readonly editor?: Omit<NonNullable<AdapterDefinition['editor']>, 'Layout'> & {
179
+ readonly layout?: string;
180
+ };
181
+ readonly regions: readonly AdapterRegion[];
182
+ readonly scenes: ResolvedDocumentTable;
183
+ /** The observation table, wire-shaped. See {@link ProjectObservationFacet}. */
184
+ readonly observation: readonly ProjectObservationFacet[];
185
+ /** Finder diagnostics — what could not be answered, and why. Never swallowed. */
186
+ readonly notes: readonly string[];
187
+ /** True while a declared finder has no registration yet — a contribution's
188
+ * finder before the contribution pass — so the table is NOT final. The
189
+ * chrome treats the document kinds as unknown until this clears, rather
190
+ * than concluding "no model" from a table the pass is about to fill. */
191
+ readonly documentsPending: boolean;
192
+ /**
193
+ * A load/parse failure — or a probe that could not answer — named. Non-null
194
+ * means the project's OWN adapter did not load, and what stood in is
195
+ * whatever {@link source} says: a registry binding, or the declared native
196
+ * default. Never swallowed, because the editor still opening on a broken
197
+ * `vgai.adapter.ts` is only acceptable while it SAYS the file is broken.
198
+ */
199
+ readonly error: string | null;
200
+ }
201
+
202
+ let _facet: ProjectAdapterFacet | null = null;
203
+ /** The parsed declaration behind {@link _facet}, kept for
204
+ * {@link projectAdapterDefinition}. `null` whenever no declaration FILE was
205
+ * parsed — see that function for why the native default is not stored here. */
206
+ let _definition: AdapterDefinition | null = null;
207
+ let _owner = '__unscoped__';
208
+ /** The load in flight, so a reader can wait for the answer instead of racing
209
+ * it. See {@link projectAdapterDefinition}. */
210
+ let _inFlight: Promise<void> | null = null;
211
+ let _refreshEpoch = 0;
212
+ const _listeners = new Set<() => void>();
213
+
214
+ /** The loaded adapter table, or `null` before the first load completes —
215
+ * "nobody looked yet" is deliberately distinct from "the project has none". */
216
+ export function projectAdapterFacet(): ProjectAdapterFacet | null {
217
+ return _owner === activeProjectKey() ? _facet : null;
218
+ }
219
+
220
+ /**
221
+ * THE PARSED DECLARATION, for the one consumer that needs the whole thing:
222
+ * `binding-resolver.ts`, which hands it to `resolveRootBinding` so it lands on
223
+ * `binding.project.definition`.
224
+ *
225
+ * Two deliberate properties:
226
+ *
227
+ * - It **awaits the load in flight**. A resolve that read module state
228
+ * synchronously would answer `null` for a project whose adapter is still
229
+ * loading — a project-switch-then-Play race — and `null` on that binding
230
+ * means "this project declares no adapter", which would be a fabricated
231
+ * fact. Waiting is what makes the `null` honest.
232
+ * - The **declared native default is `null` here**, not `nativeAdapter()`.
233
+ * `binding.project.definition` answers "what did the PROJECT declare", so a
234
+ * project that shipped no declaration file must read as `null`. Which table
235
+ * actually stood in — project, registry, or native — is
236
+ * {@link ProjectAdapterFacet.source}'s job, and that is where a consumer
237
+ * asking the other question looks.
238
+ */
239
+ export async function projectAdapterDefinition(): Promise<AdapterDefinition | null> {
240
+ if (_inFlight) await _inFlight;
241
+ return _owner === activeProjectKey() ? _definition : null;
242
+ }
243
+
244
+ /** Initial layout waits for the adapter declaration, not deferred finder contributions. */
245
+ export function waitForProjectAdapter(): Promise<void> {
246
+ const ready = () => !getCurrentProject() || Boolean(projectAdapterFacet());
247
+ if (ready()) return Promise.resolve();
248
+ return new Promise<void>((resolve) => {
249
+ const stop = subscribeProjectAdapter(() => {
250
+ if (ready()) {
251
+ stop();
252
+ resolve();
253
+ }
254
+ });
255
+ });
256
+ }
257
+
258
+ export function subscribeProjectAdapter(fn: () => void): () => void {
259
+ _listeners.add(fn);
260
+ return () => _listeners.delete(fn);
261
+ }
262
+
263
+ function publish(
264
+ next: ProjectAdapterFacet | null,
265
+ observation: readonly ObservationDeclaration[] = [],
266
+ input: AdapterDefinition['input'] = undefined,
267
+ definition: AdapterDefinition | null = null,
268
+ ): void {
269
+ _owner = activeProjectKey();
270
+ _facet = next;
271
+ _definition = definition;
272
+ setAdapterEditorConfiguration(definition?.editor);
273
+ // The region table's OTHER reader, pushed rather than pulled: the capture and
274
+ // staleness sites ask "what is this project presented on" from inside the
275
+ // ingest mount and play-mode, and having them import this loader would drag
276
+ // its whole graph along. See `presentation-surface.ts`'s note — this stays the
277
+ // one place regions are decided.
278
+ setPresentationRegions(next?.regions ?? []);
279
+ // Same push, same reason, for the half of the observation table that cannot
280
+ // travel over the wire: the `answer` closures the ingest mount evaluates
281
+ // against the mounted game (`adapter-observation.ts`).
282
+ setAdapterObservations(observation);
283
+ setAdapterInputBinding(input ?? null);
284
+ for (const fn of _listeners) fn();
285
+ notifyProjectShapeChanged();
286
+ }
287
+
288
+ /** The wire half of one declaration — closures stripped, the fact of one kept. */
289
+ function observationFacet(declaration: ObservationDeclaration): ProjectObservationFacet {
290
+ return {
291
+ id: declaration.id,
292
+ kind: declaration.kind,
293
+ answered: declaration.answer !== undefined,
294
+ };
295
+ }
296
+
297
+ /** Test seam: drop the loaded table, both halves of it. */
298
+ export function __resetProjectAdapterForTest(): void {
299
+ _facet = null;
300
+ _definition = null;
301
+ _inFlight = null;
302
+ _refreshEpoch++;
303
+ setAdapterEditorConfiguration();
304
+ setAdapterObservations([]);
305
+ setAdapterInputBinding(null);
306
+ }
307
+
308
+ /**
309
+ * Import the project's own adapter module. Cache-busted like every other
310
+ * design-time project load, because a saved `vgai.adapter.ts` must be readable
311
+ * without restarting the editor.
312
+ *
313
+ * The url is built by `fsImportPath` (project-module-url.ts) and NOT by
314
+ * `projectEntryImportUrl`: a mount epoch exists to keep the roots of ONE mount
315
+ * on one module instance, and the adapter is not part of any mount — it is a
316
+ * binding table read WITHOUT booting the game. Taking an epoch here would
317
+ * inflate the mount-epoch census for a module no root shares.
318
+ */
319
+ function editorConfigurationFacet(editor: NonNullable<AdapterDefinition['editor']>) {
320
+ const { Layout, ...configuration } = editor;
321
+ return {
322
+ ...configuration,
323
+ ...(Layout ? { layout: Layout.displayName ?? Layout.name ?? 'Project layout' } : {}),
324
+ };
325
+ }
326
+
327
+ async function importAdapterModule(rootPath: string): Promise<unknown> {
328
+ // The server-less tier has no `/@fs/`. A storage-backed project's adapter
329
+ // loads through the same in-browser bundling door as every project module
330
+ // (fresh bundle per call — the cache-bust for free); a STAGED hosted
331
+ // example/ingest — reached only when the realm-correct probe found the
332
+ // file among its served bytes — bundles it from its own staged URL. Never
333
+ // the backend for those: it still holds some other project
334
+ // (`browserProjectStorageBacked`, the 2026-08-27 leak).
335
+ const url = `${fsImportPath(rootPath, ADAPTER_MODULE_FILENAME)}?t=${Date.now()}`;
336
+ return (await import(/* @vite-ignore */ url)) as unknown;
337
+ }
338
+
339
+ /**
340
+ * Map a component to its region by SURFACE. Exact, not heuristic: the manifest
341
+ * schema enforces at most ONE world root per medium (ARCHITECTURE-CORE
342
+ * §Roots), so a surface names at most one region.
343
+ */
344
+ function regionForSurface(regions: readonly AdapterRegion[], surface: string): string | null {
345
+ return regions.find((region) => region.surface === surface)?.id ?? null;
346
+ }
347
+
348
+ /**
349
+ * Resolve one of an entrypoint's relative import specifiers against the paths
350
+ * the project's own component index reports. Extension/index resolution is a
351
+ * filesystem question and the finders are pure, so the host answers it here —
352
+ * from a real index, never by guessing an extension onto a path.
353
+ */
354
+ function makeResolveModule(
355
+ components: readonly { readonly path: string }[],
356
+ ): (specifier: string, fromPath: string) => string | null {
357
+ const paths = new Set(components.map((component) => component.path));
358
+ return (specifier, fromPath) => resolveRelativeSpecifier(specifier, fromPath, paths);
359
+ }
360
+
361
+ /**
362
+ * The ingest game this project IS, or `null` — the key the in-tree adapter
363
+ * registry is looked up by.
364
+ *
365
+ * It is the manifest's own single `{ ingest }` root id, which is a DECLARATION
366
+ * read off the project (never a folder-name guess), and it is the same id
367
+ * `ingest/mount-ingest-root.ts` mounts by. Two ingest roots is not this
368
+ * module's error to report — that route already fails loudly on it
369
+ * (`reportTooManyIngestRoots`) — so here it simply names no game rather than
370
+ * picking one.
371
+ */
372
+ function ingestGameIdOf(manifest: ResolvedGameManifest): string | null {
373
+ const declared = ingestRoots(manifest);
374
+ return declared.length === 1 ? declared[0]!.id : null;
375
+ }
376
+
377
+ /** What a table with no finder selections is resolved against. */
378
+ const NO_FINDER_INPUT: FinderInput = { entrypoints: [], components: [] };
379
+
380
+ async function gatherFinderInput(
381
+ manifest: ResolvedGameManifest,
382
+ regions: readonly AdapterRegion[],
383
+ definition: AdapterDefinition,
384
+ ): Promise<FinderInput> {
385
+ const entrypoints: EntrypointSource[] = [];
386
+ for (const root of declaredRoots(manifest)) {
387
+ if (!root.entry) continue;
388
+ entrypoints.push({
389
+ regionId: root.id,
390
+ path: root.entry,
391
+ source: await readProjectTextFile(root.entry),
392
+ });
393
+ }
394
+
395
+ let components: ProjectComponentRef[] = [];
396
+ const listing = await listProjectComponents().catch(
397
+ (error: unknown): Awaited<ReturnType<typeof listProjectComponents>> => ({
398
+ ok: false,
399
+ reason: error instanceof Error ? error.message : String(error),
400
+ }),
401
+ );
402
+ if (listing.ok) {
403
+ components = listing.entries.map((component) => ({
404
+ name: component.name,
405
+ path: component.path,
406
+ region: regionForSurface(regions, component.surface),
407
+ }));
408
+ } else {
409
+ // No component index (the route is unavailable): prefabs
410
+ // still resolve from their stories, they just carry no source path. That
411
+ // degrade is fine; being silent about it was not — a prefab missing its
412
+ // source path looked like the prefab, not like the index.
413
+ editorConsole.warn(
414
+ `Component index unavailable, so prefabs resolve without source paths: ${listing.reason}`,
415
+ 'project',
416
+ );
417
+ }
418
+
419
+ // Every selection's `include` globs, read once: a contributed finder sees
420
+ // the project through these sources and nothing else.
421
+ const includes = new Set<string>();
422
+ for (const selection of definition.documents.find ?? []) {
423
+ for (const glob of (selection as { include?: readonly string[] }).include ?? [])
424
+ includes.add(glob);
425
+ }
426
+ const sources: ProjectSourceFile[] = [];
427
+ const files: string[] = [];
428
+ if (includes.size > 0) {
429
+ // Newest first: the table keeps this order, so "the one being worked on"
430
+ // is the first candidate when a fresh checkout has to pick a document.
431
+ const paths = await listProjectSourceFiles([...includes], { order: 'mtime' }).catch(
432
+ () => [] as string[],
433
+ );
434
+ for (const path of paths) {
435
+ files.push(path);
436
+ // A BINARY document is listed, never decoded. `@volter/editor-blender`'s finder
437
+ // includes `.blend` files, and reading one as text would hand a finder a
438
+ // string of replacement characters and call it a source. A NUL byte is
439
+ // the test because it is the one thing no text source contains, and it
440
+ // asks the bytes rather than a list of extensions the host would then
441
+ // own. COST, named rather than hidden: the bytes are fetched and decoded
442
+ // before the test can run, so a binary under a finder's globs is read
443
+ // once per adapter resolve. A selection that declared it wants paths
444
+ // only would close it; no shipped finder needs that yet.
445
+ const source = await readProjectTextFile(path);
446
+ if (source !== null && !source.includes('\0')) sources.push({ path, source });
447
+ }
448
+ }
449
+ return {
450
+ entrypoints,
451
+ components,
452
+ sources,
453
+ files,
454
+ resolveModule: makeResolveModule(components),
455
+ };
456
+ }
457
+
458
+ export type { EntrypointSource, FinderInput, ProjectComponentRef };
459
+
460
+ /**
461
+ * Assemble finder input from parts the caller already gathered. The live
462
+ * loader reads those parts off the editor session; the headless portfolio
463
+ * run reads them off disk. Both hand the SAME shape to
464
+ * {@link resolveDocumentTable}, which is the one fold.
465
+ */
466
+ export function buildFinderInput(parts: {
467
+ readonly entrypoints: readonly EntrypointSource[];
468
+ readonly components?: readonly ProjectComponentRef[];
469
+ }): FinderInput {
470
+ const components = parts.components ?? [];
471
+ return {
472
+ entrypoints: parts.entrypoints,
473
+ components,
474
+ resolveModule: makeResolveModule(components),
475
+ };
476
+ }
477
+
478
+ /** Run the adapter's own finder selections and fold them into one table. */
479
+ export function resolveDocumentTable(
480
+ definition: AdapterDefinition,
481
+ input: FinderInput,
482
+ ): {
483
+ table: ResolvedDocumentTable;
484
+ notes: string[];
485
+ /** Selections whose finder is not registered (yet). */
486
+ unresolved: number;
487
+ } {
488
+ const entries: DocumentEntry[] = [...(definition.documents.entries ?? [])];
489
+ const notes: string[] = [];
490
+ const settled: string[] = [];
491
+ let unresolved = 0;
492
+ for (const selection of definition.documents.find ?? []) {
493
+ let result: FinderResult;
494
+ try {
495
+ result = runFinderSelection(selection, input);
496
+ } catch (error) {
497
+ // A finder a CONTRIBUTION registers is not there until the contribution
498
+ // pass has run, which follows the first adapter load; the table is
499
+ // resolved again when it does (`subscribeToolContributions` below). A
500
+ // selection that stays unresolved is a standing note, never a throw.
501
+ notes.push(
502
+ `finder ${JSON.stringify(selection.finder)}: ${error instanceof Error ? error.message : String(error)}`,
503
+ );
504
+ unresolved += 1;
505
+ continue;
506
+ }
507
+ entries.push(...result.entries);
508
+ notes.push(...result.notes);
509
+ if (result.default !== undefined) settled.push(result.default);
510
+ }
511
+ // THE SESSION'S OWN MODEL DOCUMENT (ARCHITECTURE-CORE §The project model,
512
+ // "A model is Blender data"): `@volter/editor-blender`'s Model document opens a
513
+ // project's `.blend` files, which its `modelsFromBlendFiles` finder lists
514
+ // above. A project that declares the package but holds no `.blend` of its
515
+ // own still needs ONE — it is what `blender-start` presents into and what a
516
+ // first bpy call models in, saved to the session's default
517
+ // `models/model.blend` — so the standing `blender:runtime` entry stands
518
+ // exactly there: where that package's document is registered AND the table
519
+ // found no model of the project's own. Never as an entry no editor can
520
+ // open, and never as a second Model beside a real one.
521
+ if (
522
+ documentContributionForKind('model') !== undefined &&
523
+ !entries.some((entry) => entry.kind === 'model')
524
+ ) {
525
+ entries.push({
526
+ id: 'blender:runtime',
527
+ label: 'Model',
528
+ kind: 'model',
529
+ region: null,
530
+ authorable: true,
531
+ reach: { kind: 'root-mount' },
532
+ });
533
+ }
534
+ // The adapter's own declaration wins; otherwise a default stands only when
535
+ // exactly one finder settled one. Two candidates is a CHOICE, and the host
536
+ // does not make choices the game's author can state.
537
+ const fallback = settled.length === 1 ? settled[0]! : null;
538
+ return {
539
+ table: { entries, default: definition.documents.default ?? fallback },
540
+ notes,
541
+ unresolved,
542
+ };
543
+ }
544
+
545
+ /** Which adapter this project resolved to, and the provenance of the choice. */
546
+ export interface ResolvedAdapterChoice {
547
+ readonly definition: AdapterDefinition;
548
+ readonly source: 'project' | 'registry' | 'native';
549
+ readonly modulePath: string | null;
550
+ readonly error: string | null;
551
+ }
552
+
553
+ /** The project's own `vgai.adapter.ts` probe, as data — the live loader and
554
+ * the headless run answer this the same way, from different IO. */
555
+ export type ProjectAdapterProbe =
556
+ | { readonly kind: 'present'; readonly module: unknown }
557
+ | { readonly kind: 'broken'; readonly error: string }
558
+ | { readonly kind: 'absent' }
559
+ | { readonly kind: 'unreachable'; readonly error: string };
560
+
561
+ /**
562
+ * AN ADAPTER DEFINITION IS SOURCED, NOT GLOBBED — the door a LANE registers
563
+ * its host-realm declarations through, in the family of
564
+ * `workspace-document-restore.ts` (a kind owns its persisted state),
565
+ * `document-open-registry.ts` (a kind owns how it opens),
566
+ * `chrome-slot-registry.ts` (the host owns the place, a package owns what sits
567
+ * there) and `content-entry-source-registry.ts` (the host owns the Content
568
+ * scope, a package owns why a component is content).
569
+ *
570
+ * IT LIVES IN THIS FILE, not beside those, and the reason is the meter. Its
571
+ * only consumer is {@link resolveAdapterDefinition} below — the one place the
572
+ * three-way precedence lives — so a separate module is a host file traded for
573
+ * a host file: MEASURED, `main.tsx` stayed at 494 with the door split out and
574
+ * dropped to 493 with it here. A registry whose whole readership is one
575
+ * function belongs in that function's module.
576
+ *
577
+ * WHAT IT REPLACED. This module used to import `ingest/registry.ts` directly
578
+ * for branch 2, so the host's boot closure carried the ingest lane's
579
+ * `import.meta.glob` over `src/ingest/games/*` — every repo-vendored game's
580
+ * manifest and adapter module in every editor boot, a `models` build that
581
+ * mounts no unmodified game included. The lane that OWNS those declarations is
582
+ * `@vgai/game`, which already imports that registry from three of its own
583
+ * ingest modules; it registers here from `contributions/ingest.service.ts`.
584
+ *
585
+ * NOTHING REGISTERED IS A REAL ANSWER, AND IT IS A LOUD ONE — see
586
+ * {@link RegistryAdapterProbe}'s `unanswered`.
587
+ */
588
+ export interface AdapterDefinitionSource {
589
+ /** The source's id, in its own vocabulary. Part of every teaching message. */
590
+ readonly id: string;
591
+ /** Which module registered it. Re-registering the same owner+id REPLACES,
592
+ * so an HMR re-evaluation leaves one source, not two. */
593
+ readonly owner: string;
594
+ /** Ascending; ties keep registration order. */
595
+ readonly order?: number;
596
+ /** The repo path of the declaration this source holds for `rootId`, or
597
+ * `null` when it holds none. Never throws — a source that cannot answer
598
+ * says so by returning `null`. */
599
+ readonly modulePathFor: (rootId: string) => string | null;
600
+ /** Import that declaration. Only called when `modulePathFor` named a file;
601
+ * a module that exists and throws is LEFT to throw, so the loader can name
602
+ * the failure rather than swallow it. */
603
+ readonly importModule: (rootId: string) => Promise<unknown>;
604
+ }
605
+
606
+ const _adapterSources: AdapterDefinitionSource[] = [];
607
+
608
+ /** Install a source. Returns the teardown. */
609
+ export function registerAdapterDefinitionSource(source: AdapterDefinitionSource): () => void {
610
+ const stale = _adapterSources.findIndex(
611
+ (item) => item.owner === source.owner && item.id === source.id,
612
+ );
613
+ if (stale >= 0) _adapterSources.splice(stale, 1);
614
+ _adapterSources.push(source);
615
+ _adapterSources.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
616
+ return () => {
617
+ const at = _adapterSources.indexOf(source);
618
+ if (at >= 0) _adapterSources.splice(at, 1);
619
+ };
620
+ }
621
+
622
+ /** The first source that claims this root id, and the file it claims it
623
+ * through — `null` when none does, which is NOT "this project has no
624
+ * adapter" (see {@link RegistryAdapterProbe}). */
625
+ function adapterDefinitionBinding(
626
+ rootId: string,
627
+ ): { readonly source: AdapterDefinitionSource; readonly modulePath: string } | null {
628
+ for (const source of _adapterSources) {
629
+ const modulePath = source.modulePathFor(rootId);
630
+ if (modulePath !== null) return { source, modulePath };
631
+ }
632
+ return null;
633
+ }
634
+
635
+ /**
636
+ * The SOURCED-declaration probe, as data (`adapter-definition-source-registry.ts`).
637
+ *
638
+ * `unanswered` is this half's `unreachable`, and it exists for the same reason
639
+ * the project half's does: a manifest that DECLARES an ingest root has stated
640
+ * which lane owns its declaration, so "no registered source holds one" is an
641
+ * unanswered question about the BUILD, never an absence in the project. It is
642
+ * the anti-shim rule's standing warning — the native default still stands in so
643
+ * the editor opens, and the failure is named rather than mounted quietly.
644
+ *
645
+ * `absent` remains the honest answer of the DISK half (`coverage/
646
+ * portfolio-resolve.ts`), which looks at the filesystem and can genuinely say
647
+ * the file is not there.
648
+ */
649
+ export type RegistryAdapterProbe =
650
+ | { readonly kind: 'present'; readonly module: unknown; readonly path: string }
651
+ | { readonly kind: 'broken'; readonly path: string; readonly error: string }
652
+ | { readonly kind: 'unanswered'; readonly rootId: string; readonly sources: number }
653
+ | { readonly kind: 'absent' };
654
+
655
+ function adapterModuleDefault(module_: unknown): unknown {
656
+ if (module_ !== null && typeof module_ === 'object' && 'default' in module_) {
657
+ return (module_ as { default: unknown }).default;
658
+ }
659
+ return module_;
660
+ }
661
+
662
+ /**
663
+ * THE THREE-WAY PRECEDENCE as a pure function over already-loaded modules.
664
+ * {@link resolveAdapterDefinition} is the live IO half; the headless portfolio
665
+ * run is the disk half. Neither may invent a fourth branch.
666
+ */
667
+ export function selectAdapterDefinition(
668
+ project: ProjectAdapterProbe,
669
+ registry: RegistryAdapterProbe,
670
+ ): ResolvedAdapterChoice {
671
+ if (project.kind === 'present') {
672
+ try {
673
+ return {
674
+ definition: parseAdapterDefinition(adapterModuleDefault(project.module)),
675
+ source: 'project',
676
+ modulePath: ADAPTER_MODULE_FILENAME,
677
+ error: null,
678
+ };
679
+ } catch (err) {
680
+ return {
681
+ definition: nativeAdapter(),
682
+ source: 'native',
683
+ modulePath: null,
684
+ error: `${ADAPTER_MODULE_FILENAME} did not load — ${describe(err)}`,
685
+ };
686
+ }
687
+ }
688
+ if (project.kind === 'broken') {
689
+ return {
690
+ definition: nativeAdapter(),
691
+ source: 'native',
692
+ modulePath: null,
693
+ error: `${ADAPTER_MODULE_FILENAME} did not load — ${project.error}`,
694
+ };
695
+ }
696
+
697
+ const unknown = project.kind === 'unreachable' ? project.error : null;
698
+
699
+ if (registry.kind === 'present') {
700
+ try {
701
+ return {
702
+ definition: parseAdapterDefinition(adapterModuleDefault(registry.module)),
703
+ source: 'registry',
704
+ modulePath: registry.path,
705
+ error: unknown,
706
+ };
707
+ } catch (err) {
708
+ return {
709
+ definition: nativeAdapter(),
710
+ source: 'native',
711
+ modulePath: null,
712
+ error: `${registry.path} did not load — ${describe(err)}`,
713
+ };
714
+ }
715
+ }
716
+ if (registry.kind === 'broken') {
717
+ return {
718
+ definition: nativeAdapter(),
719
+ source: 'native',
720
+ modulePath: null,
721
+ error: `${registry.path} did not load — ${registry.error}`,
722
+ };
723
+ }
724
+ if (registry.kind === 'unanswered') {
725
+ return {
726
+ definition: nativeAdapter(),
727
+ source: 'native',
728
+ modulePath: null,
729
+ error:
730
+ `vgai.project.json declares the ingest root \`${registry.rootId}\`, and no registered ` +
731
+ `adapter-definition source holds a declaration for it ` +
732
+ `(${registry.sources} source${registry.sources === 1 ? '' : 's'} registered). The lane ` +
733
+ `that owns a vendored game's declaration is \`@vgai/game\`, which registers through ` +
734
+ `its \`ingest.service.ts\` contribution — so either this product does not compose that ` +
735
+ `package, ` +
736
+ `or its contribution pass did not load it. The declared native default is standing in, ` +
737
+ `which means this game's own scene table is NOT what the editor is showing.`,
738
+ };
739
+ }
740
+
741
+ return { definition: nativeAdapter(), source: 'native', modulePath: null, error: unknown };
742
+ }
743
+
744
+ /**
745
+ * Regions + scene table + observation from a chosen definition. The live
746
+ * loader and the headless run both land here — one fold, two IO fronts.
747
+ */
748
+ export function projectAdapterTableFrom(
749
+ manifest: ResolvedGameManifest,
750
+ choice: ResolvedAdapterChoice,
751
+ finderInput: FinderInput,
752
+ ): ProjectAdapterFacet {
753
+ const { definition, source, modulePath, error } = choice;
754
+ const regions =
755
+ definition.regions === 'manifest-roots'
756
+ ? regionsFromManifestRoots(declaredRoots(manifest), definition.regionIncludes)
757
+ : [...definition.regions];
758
+ const unmatchedRegions = unmatchedRegionIncludeIds(
759
+ declaredRoots(manifest),
760
+ definition.regionIncludes,
761
+ );
762
+ const { table, notes, unresolved } = resolveDocumentTable(definition, finderInput);
763
+ return {
764
+ source,
765
+ modulePath,
766
+ regions,
767
+ scenes: table,
768
+ documentsPending: unresolved > 0,
769
+ ...(definition.editor ? { editor: editorConfigurationFacet(definition.editor) } : {}),
770
+ observation: definition.observation.map(observationFacet),
771
+ notes:
772
+ unmatchedRegions.length > 0
773
+ ? [
774
+ ...notes,
775
+ `adapter: regionIncludes names ${unmatchedRegions.map((id) => `\`${id}\``).join(', ')}, ` +
776
+ `which no root in vgai.project.json declares (roots: ${declaredRoots(manifest)
777
+ .map((root) => `\`${root.id}\``)
778
+ .join(', ')}) — those globs were merged onto nothing.`,
779
+ ]
780
+ : notes,
781
+ error,
782
+ };
783
+ }
784
+
785
+ /**
786
+ * PICK THE ADAPTER — the three-way decision this module's header states, and
787
+ * the ONLY place the precedence lives.
788
+ *
789
+ * The project's OWN file is probed FIRST and outranks the registry, whatever id
790
+ * its ingest root declares.
791
+ *
792
+ * `probeProjectFile`, not `projectFileExists`, and that is the correctness of
793
+ * this function rather than a style preference: an existence probe that reads
794
+ * `res.ok` is a MEASURED CONSTANT TRUE here, because both shipped surfaces
795
+ * answer a missing project-root path with the editor's own `index.html` at 200
796
+ * (the dev server's SPA fallback; `docs/DEPLOY.md` §Surface 3 for hosted).
797
+ * Ordering a can't-say-no probe ahead of the registry made the registry branch
798
+ * unreachable — every vendored game lost its scene table to a fabricated
799
+ * "vgai.adapter.ts did not load". The probe now discriminates by CONTENT (an
800
+ * HTML document is the fallback, not the file) and reports "could not tell" as
801
+ * an answer of its own.
802
+ *
803
+ * Never throws: every failure comes back as a named `error` beside a table that
804
+ * still stands up, because an editor that dies on a project's config file
805
+ * cannot be used to fix that file.
806
+ */
807
+ async function resolveAdapterDefinition(
808
+ project: ActiveProject,
809
+ manifest: ResolvedGameManifest,
810
+ ): Promise<ResolvedAdapterChoice> {
811
+ const ingestGameId = ingestGameIdOf(manifest);
812
+ const presence = await probeProjectFile(ADAPTER_MODULE_FILENAME);
813
+
814
+ if (presence === 'present') {
815
+ try {
816
+ const module_ = (await importAdapterModule(project.rootPath)) as { default?: unknown };
817
+ return selectAdapterDefinition({ kind: 'present', module: module_ }, { kind: 'absent' });
818
+ } catch (err) {
819
+ // NO fallback to the registry. The file is really there and it is really
820
+ // broken, and quietly running the host's table for a game whose author is
821
+ // staring at their own syntax error is exactly the silence the
822
+ // project-first rule exists to remove. The failure is named and the
823
+ // declared native default stands in, so the editor still opens.
824
+ return selectAdapterDefinition({ kind: 'broken', error: describe(err) }, { kind: 'absent' });
825
+ }
826
+ }
827
+
828
+ // `unreachable` is not an absence — it is an unanswered question, and saying
829
+ // so is the difference between "this project has no adapter" and "we could
830
+ // not find out". A table is published either way, because the editor has to
831
+ // open; which one stood in is what `source` says.
832
+ const projectProbe: ProjectAdapterProbe =
833
+ presence === 'unreachable'
834
+ ? {
835
+ kind: 'unreachable',
836
+ error:
837
+ `${ADAPTER_MODULE_FILENAME} could not be probed — the project's file routes did not ` +
838
+ `answer, so whether this project owns an adapter is UNKNOWN.`,
839
+ }
840
+ : { kind: 'absent' };
841
+
842
+ if (ingestGameId === null) return selectAdapterDefinition(projectProbe, { kind: 'absent' });
843
+
844
+ // THE MANIFEST ALREADY KNOWS THE LANE, so the host is not guessing. This
845
+ // project declares an ingest root and shipped no adapter of its own, which
846
+ // means its declaration lives in a SOURCE a package registers
847
+ // ({@link registerAdapterDefinitionSource}) — and contributions load behind
848
+ // the opening viewport frame (`project-tool-discovery.ts`), while this runs
849
+ // at editor init (`startProjectAdapterLoad`). A document whose owner has not
850
+ // loaded cannot resolve; saying so by WAITING is honest, where mounting the
851
+ // native default and re-mounting when the registration lands is a silent
852
+ // degrade followed by a correction (owner decision, WORK.md §The open-source
853
+ // launch, "THE GAP IS 2"). The wait is paid ONLY here: a project with no
854
+ // ingest root never reaches this line, and one whose source is already
855
+ // registered (any resolve after the first) short-circuits below.
856
+ let binding = adapterDefinitionBinding(ingestGameId);
857
+ if (binding === null) {
858
+ await waitForContributedAdapterSources(ingestGameId);
859
+ binding = adapterDefinitionBinding(ingestGameId);
860
+ }
861
+
862
+ if (binding === null) {
863
+ const unanswered = selectAdapterDefinition(projectProbe, {
864
+ kind: 'unanswered',
865
+ rootId: ingestGameId,
866
+ sources: _adapterSources.length,
867
+ });
868
+ // THE FACET IS NOT A DOOR THE USER READS. `error` reaches
869
+ // `/__editor/state` and `vgai status` prints it, but `vgai console` stayed
870
+ // SILENT while the editor showed a vendored game the native default's
871
+ // scene table — measured live, 2026-09-19, by removing the wait above and
872
+ // reading both doors. "Unresolved editor console is remaining work" is the
873
+ // contract every `vgai` verb enforces, so a standing warning that never
874
+ // enters that ledger is not standing. Warning weight, not error: the
875
+ // editor opens and is usable, and what is wrong is the BUILD's lane list.
876
+ if (unanswered.error) editorConsole.warn(unanswered.error, 'ingest');
877
+ return unanswered;
878
+ }
879
+
880
+ try {
881
+ const module_ = (await binding.source.importModule(ingestGameId)) as { default?: unknown };
882
+ return selectAdapterDefinition(projectProbe, {
883
+ kind: 'present',
884
+ module: module_,
885
+ path: binding.modulePath,
886
+ });
887
+ } catch (err) {
888
+ return selectAdapterDefinition(projectProbe, {
889
+ kind: 'broken',
890
+ path: binding.modulePath,
891
+ error: describe(err),
892
+ });
893
+ }
894
+ }
895
+
896
+ /**
897
+ * THE WAIT, AND ITS NARRATION — one contribution pass per project open.
898
+ *
899
+ * `refreshProjectToolContributions` resolves only once the NEWEST pass has
900
+ * installed (its own docblock, and the four callers that already treat it that
901
+ * way — `project-tool-discovery.ts`, `components/tool-documents.tsx`,
902
+ * `workspace-state-persistence.ts`, `editor-view-presentation.ts`). It is a
903
+ * known startup cost, not a hung fetch: the workspace restore takes the same
904
+ * await for the same reason and records it at ~9s on a cold `full` scaffold.
905
+ *
906
+ * LATCHED PER PROJECT, and that is correctness rather than thrift. The pass's
907
+ * completion fires `subscribeToolContributions`, whose hook below re-resolves
908
+ * the table when the FINDER set changed — so an unlatched await would start a
909
+ * fresh pass inside every re-resolve it caused, and the two would drive each
910
+ * other forever. One pass per project open; every later resolve reads the
911
+ * settled promise.
912
+ *
913
+ * AND IT SAYS SO ON SCREEN. `components/ProjectLayout.tsx` already holds the
914
+ * boot behind `waitForProjectAdapter()` with a `role="status"` line; while this
915
+ * promise is pending that line NAMES the mechanism instead of reading "Loading
916
+ * project layout…" for nine silent seconds. An action whose success can be
917
+ * invisible must narrate itself.
918
+ */
919
+ let _sourceWaitOwner: string | null = null;
920
+ let _sourceWait: Promise<void> | null = null;
921
+ let _waitNarration: string | null = null;
922
+
923
+ /** What the adapter load is waiting for, for the boot's own status line —
924
+ * `null` whenever it is not waiting on anything a user should be told about. */
925
+ export function projectAdapterWaitNarration(): string | null {
926
+ return _waitNarration;
927
+ }
928
+
929
+ function waitForContributedAdapterSources(rootId: string): Promise<void> {
930
+ const owner = activeProjectKey();
931
+ if (_sourceWaitOwner !== owner) {
932
+ _sourceWaitOwner = owner;
933
+ _sourceWait = null;
934
+ }
935
+ if (!_sourceWait) {
936
+ _waitNarration =
937
+ `Loading the ingest lane — this project declares the unmodified-game root ` +
938
+ `“${rootId}”, whose adapter declaration a package supplies.`;
939
+ for (const fn of _listeners) fn();
940
+ _sourceWait = refreshProjectToolContributions()
941
+ .catch(() => {})
942
+ .finally(() => {
943
+ _waitNarration = null;
944
+ for (const fn of _listeners) fn();
945
+ });
946
+ }
947
+ return _sourceWait;
948
+ }
949
+
950
+ /**
951
+ * Load the open project's adapter and publish the resolved table.
952
+ *
953
+ * Never throws, for the reason {@link resolveAdapterDefinition} records.
954
+ */
955
+ /** The project's contributed finders, registered into this host's registry
956
+ * before the table resolves (ARCHITECTURE-CORE §The project model). A
957
+ * module that is not a finder is reported by path, never silently skipped. */
958
+ let unregisterContributedFinders: Array<() => void> = [];
959
+ let registeredFinderSignature = '';
960
+ function registerProjectFinders(): void {
961
+ const modules = contributedFinderModules();
962
+ const signature = modules.map((m) => m.entryPath).join('|');
963
+ if (
964
+ signature === registeredFinderSignature &&
965
+ modules.every((m) => registeredModules.has(m.module as object))
966
+ )
967
+ return;
968
+ for (const off of unregisterContributedFinders) off();
969
+ unregisterContributedFinders = [];
970
+ registeredModules = new WeakSet();
971
+ for (const { entryPath, module } of modules) {
972
+ try {
973
+ unregisterContributedFinders.push(
974
+ registerContributedFinder((module as { finder?: unknown } | null)?.finder, entryPath),
975
+ );
976
+ registeredModules.add(module as object);
977
+ } catch (error) {
978
+ editorConsole.error(error instanceof Error ? error.message : String(error), 'project');
979
+ }
980
+ }
981
+ registeredFinderSignature = signature;
982
+ }
983
+ let registeredModules = new WeakSet<object>();
984
+
985
+ // THE KIT'S OWN FINDER, registered once. `prefabsFromStories` is portable
986
+ // CSF's — the kit's design-time state (ARCHITECTURE-CORE §The target shape) —
987
+ // and it reads the story registry itself rather than taking a `FinderInput`
988
+ // field, which is what keeps the finder contract free of one lane's data.
989
+ // This file is the ONE sanctioned importer of the engine's finder namespace
990
+ // (`@editor/finders`'s header states the rule), so the registration is
991
+ // here rather than beside the rest of the lane in `stories/story-lane.ts`.
992
+ registerContributedFinder(storyPrefabsFinder, 'packages/editor/src/stories/prefabs-finder.ts');
993
+
994
+ // A contribution pass that changed the finder set re-resolves the table.
995
+ subscribeToolContributions(() => {
996
+ const modules = contributedFinderModules();
997
+ const changed =
998
+ modules.map((m) => m.entryPath).join('|') !== registeredFinderSignature ||
999
+ !modules.every((m) => registeredModules.has(m.module as object));
1000
+ if (changed && getCurrentProject()) void refreshProjectAdapter();
1001
+ });
1002
+
1003
+ registerDocumentKindsSupplier(() =>
1004
+ _facet && !_facet.documentsPending
1005
+ ? [...new Set(_facet.scenes.entries.map((entry) => entry.kind))]
1006
+ : null,
1007
+ );
1008
+
1009
+ export function refreshProjectAdapter(): Promise<void> {
1010
+ const run = loadAndPublishProjectAdapter(++_refreshEpoch, getCurrentProject());
1011
+ _inFlight = run;
1012
+ void run.then(() => {
1013
+ if (_inFlight === run) _inFlight = null;
1014
+ });
1015
+ return run;
1016
+ }
1017
+
1018
+ async function loadAndPublishProjectAdapter(
1019
+ epoch: number,
1020
+ project: ActiveProject | null,
1021
+ ): Promise<void> {
1022
+ // Declaration, story, and contribution refreshes can overlap. Only the
1023
+ // latest pass for the still-active project may replace its document table.
1024
+ const current = (): boolean => epoch === _refreshEpoch && getCurrentProject() === project;
1025
+ if (!project) {
1026
+ publish(null);
1027
+ return;
1028
+ }
1029
+
1030
+ let manifest: ResolvedGameManifest;
1031
+ try {
1032
+ manifest = await fetchGameManifest();
1033
+ } catch (error) {
1034
+ if (!current()) return;
1035
+ publish({
1036
+ source: 'native',
1037
+ modulePath: null,
1038
+ regions: [],
1039
+ scenes: { entries: [], default: null },
1040
+ observation: [],
1041
+ notes: [],
1042
+ documentsPending: false,
1043
+ error: `adapter: the project's manifest did not load, so no region could be derived — ${describe(error)}`,
1044
+ });
1045
+ return;
1046
+ }
1047
+
1048
+ if (!current()) return;
1049
+ const choice = await resolveAdapterDefinition(project, manifest);
1050
+ if (!current()) return;
1051
+ registerProjectFinders();
1052
+
1053
+ // Gathering finder input reads the project's own entrypoint sources and
1054
+ // component index; a table with no finder SELECTIONS has nothing to feed, and
1055
+ // an ingest game's sources are not the host's to read at all. Skipping is the
1056
+ // honest read of the declaration, not an optimization dressed as one.
1057
+ const regionsPreview =
1058
+ choice.definition.regions === 'manifest-roots'
1059
+ ? regionsFromManifestRoots(declaredRoots(manifest), choice.definition.regionIncludes)
1060
+ : [...choice.definition.regions];
1061
+ const input =
1062
+ (choice.definition.documents.find ?? []).length > 0
1063
+ ? await gatherFinderInput(manifest, regionsPreview, choice.definition)
1064
+ : NO_FINDER_INPUT;
1065
+ if (!current()) return;
1066
+ const facet = projectAdapterTableFrom(manifest, choice, input);
1067
+ // `native` is the DECLARED DEFAULT standing in for a project that shipped no
1068
+ // declaration file, so it is not a declaration this project made — see
1069
+ // `projectAdapterDefinition`.
1070
+ publish(
1071
+ facet,
1072
+ choice.definition.observation,
1073
+ choice.definition.input,
1074
+ choice.source === 'native' ? null : choice.definition,
1075
+ );
1076
+ }
1077
+
1078
+ function describe(error: unknown): string {
1079
+ return error instanceof Error ? error.message : String(error);
1080
+ }
1081
+
1082
+ /** Does a project-relative path fall under any declared finder's `include`? */
1083
+ function finderIncludesMatch(path: string): boolean {
1084
+ // No parsed declaration in hand (the native default, or a load in flight):
1085
+ // the globs are unknown, so a src change is treated as relevant rather than
1086
+ // dropped — a stale table costs more than a spare resolve.
1087
+ if (!_definition) return true;
1088
+ const selections = (_definition.documents.find ?? []) as ReadonlyArray<{
1089
+ include?: readonly string[];
1090
+ }>;
1091
+ for (const selection of selections) {
1092
+ for (const glob of selection.include ?? []) {
1093
+ if (globToRegExp(glob).test(path)) return true;
1094
+ }
1095
+ }
1096
+ return false;
1097
+ }
1098
+
1099
+ /** `src/models/**\/*.ts` → a RegExp over a project-relative path. `**` spans
1100
+ * directories, `*` stays inside one; everything else is literal. */
1101
+ function globToRegExp(glob: string): RegExp {
1102
+ const DEEP = '__DEEP__';
1103
+ const escaped = glob
1104
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
1105
+ .replace(/\*\*\//g, DEEP)
1106
+ .replace(/\*/g, '[^/]*')
1107
+ .split(DEEP)
1108
+ .join('(?:.*/)?');
1109
+ return new RegExp(`^${escaped}$`);
1110
+ }
1111
+
1112
+ /**
1113
+ * Load the adapter once at editor init and again on every project switch —
1114
+ * the same lifecycle shape `project-story-discovery.ts` uses, for the same
1115
+ * reason: the adapter is a PROJECT capability, not a side effect of mounting
1116
+ * a particular root.
1117
+ */
1118
+ export function startProjectAdapterLoad(): () => void {
1119
+ void refreshProjectAdapter();
1120
+ const changed = (path: string) => {
1121
+ if (
1122
+ projectModuleChangeMatches(path, ADAPTER_MODULE_FILENAME) ||
1123
+ /(?:^|\/)src\/.+\.[jt]sx?$/.test(path)
1124
+ ) {
1125
+ void refreshProjectAdapter();
1126
+ }
1127
+ };
1128
+ const stopModules = subscribeProjectModuleChange(changed);
1129
+ const stopProject = onProjectChange(() => {
1130
+ if (_owner !== activeProjectKey()) publish(null);
1131
+ void refreshProjectAdapter();
1132
+ });
1133
+ // A finder's SOURCE changed on disk — a model module written, a page added.
1134
+ // Nothing imports those files, so this is the only signal the table gets.
1135
+ const stopSources = connectSourceFileEvents((path) => {
1136
+ if (path === '' || finderIncludesMatch(path)) void refreshProjectAdapter();
1137
+ });
1138
+ return () => {
1139
+ stopModules();
1140
+ stopProject();
1141
+ stopSources();
1142
+ };
1143
+ }