@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,831 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname, join, relative, resolve, sep } from 'node:path';
3
+ import { resolveManifestPath } from '@volter/editor-project/manifest/locate';
4
+ import type {
5
+ FileRegionAnswer,
6
+ ImportersOf,
7
+ RegionBinding,
8
+ } from '../src/ui-source/file-region-resolver';
9
+ import { resolveFileRegion } from '../src/ui-source/file-region-resolver';
10
+ import type { DeclaredRootSurface, SourceDialectEvidence } from '../src/ui-source/oid-transform';
11
+ import { sourceDialectEvidence, sourceProvesR3f } from '../src/ui-source/oid-transform';
12
+ import {
13
+ ADAPTER_MODULE_FILENAME,
14
+ adapterRegionIncludes,
15
+ resetAdapterRegionIncludesCacheForTest,
16
+ } from './adapter-region-includes';
17
+
18
+ /**
19
+ * The dev server's HALF of the one file→region decision: the filesystem
20
+ * questions `../src/ui-source/file-region-resolver.ts` injects (which regions
21
+ * does this project declare, and is THIS file one of their entries), plus the
22
+ * OID-specific diagnostics built on the answer.
23
+ *
24
+ * Shared by the OID-stamping transform (`../vite-plugin-ui-oid.ts`'s
25
+ * `transform` hook) and the HMR classifier (`./project-hmr-files.ts`'s
26
+ * `classifyProjectHotUpdate`). Before this module existed, the two answered
27
+ * the "is this file R3F or react-dom" question with two SEPARATE copies of
28
+ * the same substring probe, "kept in lockstep by hand" per their own doc
29
+ * comments — a latent drift bug of its own, independent of the probe's
30
+ * original blind spot (see the oid-attribute-surface-decision PR). Sharing
31
+ * this helper makes drift structurally impossible: both call the same
32
+ * function over the same manifest-walk cache.
33
+ *
34
+ * There is ONE surface vocabulary — 'three' | 'canvas' | 'dom'. This module
35
+ * carries no alias map, because `packages/project/src/manifest/schema.ts`
36
+ * normalizes nothing (the legacy-removal doctrine): a manifest naming an old
37
+ * spelling fails `RootAdapterSchema` loudly at load, naming the canonical
38
+ * value — so there is nothing for this probe to translate.
39
+ *
40
+ * Deliberately dependency-light (no Zod, no `@vgai/project/manifest/load`) — same
41
+ * bar `vite-plugin-ui-oid.ts`'s `nearestManifestExcludesIngestReact` already
42
+ * holds for this vite-config-time file: a best-effort identity probe, not
43
+ * manifest validation. A malformed manifest never crashes a transform/HMR
44
+ * hook; it degrades to "no declared regions", which the resolver answers as
45
+ * AMBIGUOUS and every caller already handles (the documented `data-oid`
46
+ * default here, the plain 'react' Fast-Refresh classification in
47
+ * `classifyProjectHotUpdate`) — loudly, never as a silent guess.
48
+ */
49
+
50
+ function normalizeSurface(value: unknown): DeclaredRootSurface | undefined {
51
+ if (value === 'three' || value === 'canvas' || value === 'dom') return value;
52
+ return undefined;
53
+ }
54
+
55
+ /** Best-effort surface extraction from one root's raw (unvalidated) `adapter`
56
+ * field — either the bare-string shorthand or the `{ module | ingest,
57
+ * surface }` object forms (`RootAdapterSchema`). Never throws on a
58
+ * malformed shape. */
59
+ function rootDeclaredSurface(adapter: unknown): DeclaredRootSurface | undefined {
60
+ if (typeof adapter === 'string') return normalizeSurface(adapter);
61
+ if (adapter && typeof adapter === 'object') {
62
+ return normalizeSurface((adapter as { surface?: unknown }).surface);
63
+ }
64
+ return undefined;
65
+ }
66
+
67
+ interface CachedManifestRoots {
68
+ /** Absolute directory the manifest itself lives in — a root's `entry` is
69
+ * project-relative to THIS directory, not to the queried file's own. */
70
+ readonly manifestDir: string;
71
+ readonly roots: ReadonlyArray<{
72
+ readonly id?: unknown;
73
+ readonly entry?: unknown;
74
+ readonly adapter?: unknown;
75
+ }>;
76
+ /** The project's regions in the resolver's vocabulary: one per declared
77
+ * root, carrying whatever `include` globs its `vgai.adapter.ts` states. */
78
+ readonly regions: readonly RegionBinding[];
79
+ /** The adapter declares a `regions` binding this tier could not read
80
+ * statically — reported, never read as "declares nothing". */
81
+ readonly includesUnreadable: boolean;
82
+ }
83
+
84
+ /** Cache: absolute directory -> nearest ancestor `vgai.project.json`'s parsed
85
+ * roots (or `null` if none exists above it). Cached both ways (hit and
86
+ * miss), the same pattern `vite-plugin-ui-oid.ts`'s
87
+ * `nearestManifestExcludesIngestReact` uses for the identical walk — no
88
+ * invalidation on manifest edit; a `vgai.project.json` edit already triggers
89
+ * `classifyProjectHotUpdate`'s `'restart'` kind, so a stale in-process
90
+ * cache here is no worse than the existing precedent. */
91
+ const manifestRootsCache = new Map<string, CachedManifestRoots | null>();
92
+
93
+ /**
94
+ * The manifest that OWNS `absFile` — the ancestor walk, then the open
95
+ * project's own claim.
96
+ *
97
+ * A project's sources are normally under its manifest, and the walk finds
98
+ * them. An INGEST root's are not: the racing-game's manifest lives in the
99
+ * editor's in-tree registry while the game it mounts is vendored at
100
+ * `vendor/games/racing-game/src/`, six levels away — measured, no
101
+ * `vgai.project.json` is an ancestor of ANY of that game's 45 `.tsx` files, so
102
+ * the walk answers "no project" and every declaration in its `vgai.adapter.ts`
103
+ * is unreadable for exactly the files it is about.
104
+ *
105
+ * The claim is read from the manifest, not guessed: a root's `entry` (and an
106
+ * a root's `world.entry`) is a path the manifest itself
107
+ * states and the host EXECUTES, so the directory it names is part of that
108
+ * project's tree by the project's own account. Nothing else extends a
109
+ * project's reach — a file under no declared entry's directory stays
110
+ * unclaimed, which is why opening one project cannot make the editor's own
111
+ * sources, or another project's, look like its.
112
+ */
113
+ function manifestOwning(absFile: string, projectRoot?: string): CachedManifestRoots | null {
114
+ const nearest = nearestManifestRoots(dirname(absFile));
115
+ if (nearest || projectRoot === undefined) return nearest;
116
+ const project = nearestManifestRoots(resolve(projectRoot));
117
+ if (!project) return null;
118
+ return declaredEntryDirs(project).some((dir) => absFile.startsWith(`${dir}${sep}`))
119
+ ? project
120
+ : null;
121
+ }
122
+
123
+ /** Every directory a project's manifest names an entry in, absolute. */
124
+ function declaredEntryDirs(manifest: CachedManifestRoots): string[] {
125
+ const dirs: string[] = [];
126
+ for (const root of manifest.roots) {
127
+ if (!root || typeof root !== 'object') continue;
128
+ for (const entry of [
129
+ (root as { entry?: unknown }).entry,
130
+ ((root as { world?: { entry?: unknown } }).world?.entry ?? undefined) as unknown,
131
+ ]) {
132
+ if (typeof entry !== 'string') continue;
133
+ dirs.push(dirname(resolve(manifest.manifestDir, entry)));
134
+ }
135
+ }
136
+ return dirs;
137
+ }
138
+
139
+ function nearestManifestRoots(dir: string): CachedManifestRoots | null {
140
+ const cached = manifestRootsCache.get(dir);
141
+ if (cached !== undefined) return cached;
142
+
143
+ const manifestPath = resolveManifestPath(dir);
144
+ let raw: string | undefined;
145
+ try {
146
+ raw = readFileSync(manifestPath, 'utf8');
147
+ } catch (err) {
148
+ // ENOENT (no manifest at this level) -> keep walking up. Any OTHER fs
149
+ // error (e.g. EACCES) is a real environment problem, not "no manifest
150
+ // here" — never silently swallowed.
151
+ if ((err as NodeJS.ErrnoException)?.code !== 'ENOENT') throw err;
152
+ }
153
+
154
+ let result: CachedManifestRoots | null;
155
+ if (raw === undefined) {
156
+ const parent = dirname(dir);
157
+ result = parent === dir ? null : nearestManifestRoots(parent);
158
+ } else {
159
+ result = null;
160
+ try {
161
+ const parsed: unknown = JSON.parse(raw);
162
+ const roots = (parsed as { roots?: unknown } | null)?.roots;
163
+ if (Array.isArray(roots)) {
164
+ result = cachedRoots(dir, roots as CachedManifestRoots['roots']);
165
+ }
166
+ } catch {
167
+ // Malformed JSON: honest "no manifest usable here" — never crash the
168
+ // transform/HMR hook over a broken file.
169
+ result = null;
170
+ }
171
+ }
172
+ manifestRootsCache.set(dir, result);
173
+ return result;
174
+ }
175
+
176
+ /** Join the manifest's roots to the `include` globs the project's own
177
+ * `vgai.adapter.ts` declares for them — the resolver's region vocabulary. */
178
+ function cachedRoots(dir: string, roots: CachedManifestRoots['roots']): CachedManifestRoots {
179
+ const declared = adapterRegionIncludes(dir);
180
+ const regions: RegionBinding[] = [];
181
+ for (const root of roots) {
182
+ if (!root || typeof root !== 'object') continue;
183
+ const id = typeof root.id === 'string' ? root.id : undefined;
184
+ const surface = rootDeclaredSurface(root.adapter);
185
+ if (id === undefined || surface === undefined) continue;
186
+ const include = declared.byRegionId.get(id);
187
+ const mounts = declared.mountsByRegionId.get(id);
188
+ regions.push({
189
+ id,
190
+ surface,
191
+ ...(include ? { include } : {}),
192
+ ...(mounts?.length ? { mounts } : {}),
193
+ });
194
+ }
195
+ return { manifestDir: dir, roots, regions, includesUnreadable: declared.unreadable };
196
+ }
197
+
198
+ /**
199
+ * Resolve the declared surface for ONE project file by walking up to the
200
+ * nearest `vgai.project.json` and matching the file against a root's `entry`
201
+ * path EXACTLY (resolved absolute). Returns `undefined` — "ambiguous, fall
202
+ * back to the substring probe" — when no manifest is reachable, no root's
203
+ * `entry` matches this exact file (e.g. an imported component that isn't
204
+ * itself a root's entry), or the matching root's adapter shape doesn't
205
+ * resolve to a surface. This function never throws over manifest CONTENT;
206
+ * only a genuine filesystem error other than "not found" propagates.
207
+ */
208
+ export function declaredSurfaceForProjectFile(file: string): DeclaredRootSurface | undefined {
209
+ return regionOfEntry(file)?.surface;
210
+ }
211
+
212
+ /**
213
+ * The resolver's `regionOfEntry` for this tier: walk up to the nearest
214
+ * `vgai.project.json` and match `file` against a root's `entry` path EXACTLY
215
+ * (resolved absolute). `undefined` when no manifest is reachable, no root's
216
+ * `entry` is this exact file, or the matching root's adapter shape resolves to
217
+ * no surface. Never throws over manifest CONTENT; only a genuine filesystem
218
+ * error other than "not found" propagates.
219
+ */
220
+ function regionOfEntry(file: string, projectRoot?: string): RegionBinding | undefined {
221
+ const absFile = resolve(file);
222
+ const manifest = manifestOwning(absFile, projectRoot);
223
+ if (!manifest) return undefined;
224
+ for (const root of manifest.roots) {
225
+ if (!root || typeof root !== 'object') continue;
226
+ const entry = (root as { entry?: unknown }).entry;
227
+ if (typeof entry !== 'string') continue;
228
+ if (resolve(manifest.manifestDir, entry) !== absFile) continue;
229
+ const id = typeof root.id === 'string' ? root.id : undefined;
230
+ const surface = rootDeclaredSurface((root as { adapter?: unknown }).adapter);
231
+ if (surface === undefined) return undefined;
232
+ return { id: id ?? entry, surface };
233
+ }
234
+ return undefined;
235
+ }
236
+
237
+ /** Re-exported so this tier's callers keep ONE import for the graph seam. */
238
+ export type { ImportersOf } from '../src/ui-source/file-region-resolver';
239
+
240
+ /** Structural (not imported) shape of Vite's `ModuleGraph`. */
241
+ interface ModuleNodeLike {
242
+ readonly id: string | null;
243
+ /** Vite's query-free physical source path. Present for file-backed nodes. */
244
+ readonly file?: string | null;
245
+ readonly importers: Iterable<ModuleNodeLike>;
246
+ }
247
+
248
+ export interface ModuleGraphLike {
249
+ getModuleById(id: string): ModuleNodeLike | undefined;
250
+ /**
251
+ * One physical project file can have several module nodes: mount isolation
252
+ * gives every instance its own `?vgai-mount=` URL. Surface ownership is a
253
+ * property of the source file, so the adapter must visit every variant.
254
+ */
255
+ getModulesByFile?(file: string): Set<ModuleNodeLike> | undefined;
256
+ }
257
+
258
+ /** Query/hash-free file identity for Vite module ids and `/@fs/` urls. */
259
+ function moduleFileId(id: string): string {
260
+ const clean = id.split(/[?#]/, 1)[0] ?? id;
261
+ return clean.startsWith('/@fs/') ? clean.slice('/@fs'.length) : clean;
262
+ }
263
+
264
+ function moduleVariants(graph: ModuleGraphLike, id: string, file: string): ModuleNodeLike[] {
265
+ const variants = graph.getModulesByFile?.(file);
266
+ if (variants?.size) return [...variants];
267
+ const node = graph.getModuleById(id) ?? graph.getModuleById(file);
268
+ return node ? [node] : [];
269
+ }
270
+
271
+ /**
272
+ * The importing FILES of these nodes — and a virtual module is not one.
273
+ *
274
+ * Vite's convention is that a `\0`-prefixed id was synthesized by a plugin and
275
+ * has no path behind it; `ModuleNode.file` is `null` for exactly those, which
276
+ * is why the `?? importer.id` fallback below can hand back something that is
277
+ * not a path at all. Every caller of this set treats a member as a file:
278
+ * `regionOfEntry` resolves it absolute and walks up looking for the
279
+ * `vgai.project.json` that owns it.
280
+ *
281
+ * MEASURED 2026-09-21 (WORK.md step 3 P1), in a live session: a product's
282
+ * `vgai:contributions/@volter/editor-blender` module imports that package's
283
+ * contributions, so `\0vgai:contributions/@volter/editor-blender` arrived here as an
284
+ * importer, `resolve()` made it `<cwd>/\0vgai:contributions/@vgai`, and the
285
+ * manifest walk's `readFileSync` threw `The argument 'path' must be a string,
286
+ * Uint8Array, or URL without null bytes`. That is a 500 from the OID plugin on
287
+ * every contribution of that package, which the editor reported as "the editor
288
+ * app failed to start: Failed to fetch dynamically imported module" — the
289
+ * cause named nowhere near the symptom.
290
+ *
291
+ * DROPPING IT IS THE ANSWER, not skipping past it to ITS importers: a region is
292
+ * a claim about the project's own files, so a module with no file can neither
293
+ * be a region's entry nor a step on a path to one.
294
+ */
295
+ function importerFiles(nodes: readonly ModuleNodeLike[]): Set<string> {
296
+ const files = new Set<string>();
297
+ for (const node of nodes) {
298
+ for (const importer of node.importers) {
299
+ const importerFile = importer.file ?? importer.id;
300
+ if (!importerFile || importerFile.startsWith('\0')) continue;
301
+ files.add(moduleFileId(importerFile));
302
+ }
303
+ }
304
+ return files;
305
+ }
306
+
307
+ /** Adapts a `ModuleGraphLike` (a live Vite dev server's `server.moduleGraph`)
308
+ * into an `ImportersOf` callback. `undefined` in, `undefined` out — every
309
+ * caller already treats "no graph available" the same as "no importers
310
+ * found", both degrading to the entry-only answer. */
311
+ export function importersFromModuleGraph(graph: ModuleGraphLike | undefined): ImportersOf {
312
+ return (id: string) => {
313
+ if (!graph) return undefined;
314
+ const file = moduleFileId(id);
315
+ const nodes = moduleVariants(graph, id, file);
316
+ if (nodes.length === 0) return undefined;
317
+ return importerFiles(nodes);
318
+ };
319
+ }
320
+
321
+ /**
322
+ * The dev server's file->region answer, in the shared resolver's terms:
323
+ * declared include, then root entry, then live import-graph reach.
324
+ *
325
+ * A per-root BUNDLE would answer this for free — every file its `onLoad`
326
+ * touches genuinely belongs to that entry's root. The dev server has no
327
+ * per-root build:
328
+ * Vite serves one shared module graph for the whole project, so reach up the
329
+ * graph (`importersOf`, backed by Vite's own `ModuleGraph` via
330
+ * `importersFromModuleGraph`) is how a file that is not itself an entry gets
331
+ * placed. That reach is the load-bearing fact — the region's entry is what the
332
+ * host EXECUTES at mount — which is why it is the only automatic rung.
333
+ */
334
+ export function resolveProjectFileRegion(
335
+ file: string,
336
+ importersOf?: ImportersOf,
337
+ projectRoot?: string,
338
+ ): FileRegionAnswer {
339
+ const absFile = resolve(file);
340
+ const manifest = manifestOwning(absFile, projectRoot);
341
+ return resolveFileRegion({
342
+ file: absFile,
343
+ ...(manifest ? { projectRelative: relative(manifest.manifestDir, absFile) } : {}),
344
+ ...(manifest ? { regions: manifest.regions } : {}),
345
+ regionOfEntry: (candidate) => regionOfEntry(candidate, projectRoot),
346
+ ...(importersOf ? { importersOf } : {}),
347
+ });
348
+ }
349
+
350
+ /** The surface half of {@link resolveProjectFileRegion} — `undefined` when the
351
+ * answer is ambiguous, which every caller reports rather than guesses around. */
352
+ export function declaredSurfaceForProjectFileViaGraph(
353
+ file: string,
354
+ importersOf?: ImportersOf,
355
+ ): DeclaredRootSurface | undefined {
356
+ return resolveProjectFileRegion(file, importersOf).surface;
357
+ }
358
+
359
+ /** Test-only: clear the manifest cache between cases that plant/replace a
360
+ * `vgai.project.json` at the same path. */
361
+ export function resetDeclaredSurfaceCacheForTest(): void {
362
+ manifestRootsCache.clear();
363
+ resetAdapterRegionIncludesCacheForTest();
364
+ reportedDiagnostics.clear();
365
+ }
366
+
367
+ // ---------------------------------------------------------------------------
368
+ // The DIAGNOSTIC half: an ambiguous stamp must never be silent.
369
+ //
370
+ // `resolveProjectFileRegion` above answers "which region?" from declarations
371
+ // and reach alone. When nothing places a file it answers AMBIGUOUS, and the
372
+ // stamper applies the documented default (`data-oid`) — VISIBLY. Until this
373
+ // section existed the ambiguous cases looked identical from outside: the
374
+ // surface still rendered, selection just stopped resolving, with nothing
375
+ // printed anywhere.
376
+ //
377
+ // The reasons, and what each earns:
378
+ // 1. No `vgai.project.json` above the file at all (ingest, ad-hoc source, the
379
+ // editor's own `ui-editor/editable-components/*.tsx`). NOT diagnosable —
380
+ // there is no project to be wrong about, and warning here would fire on
381
+ // every non-project file. Silent by design.
382
+ // 2. The file is inside a project but no region reaches it and none declares
383
+ // it. The attribute is then a pure DEFAULT, not a decision -> `OID001`,
384
+ // naming the region-include fix. Portable story modules are exempt: they
385
+ // are explicit design-time documents, not runtime-root candidates, and
386
+ // their rendered subject's own module owns selectable spatial nodes.
387
+ // 3. Reached from regions of DIFFERING surfaces — a genuinely shared module.
388
+ // Not resolvable from this file's bytes, by anyone, ever: the diagnostic
389
+ // IS the deliverable, and the region include is the repair -> `OID002`.
390
+ // 4. The file's own rendered HOST ELEMENTS contradict the region that owns
391
+ // it -> `OID003`. DECLARED-VS-MEASURED DRIFT, which ARCHITECTURE-CORE
392
+ // §The editor protocol names as the third and only remaining answer when
393
+ // a declaration and the world disagree: the region still decides the
394
+ // attribute, and the disagreement is said out loud so the author can fix
395
+ // the declaration (or the file).
396
+ // Plus one that is not an ambiguity at all but is equally silent and equally
397
+ // fatal to selection: a file that renders BOTH R3F-only and DOM-only
398
+ // intrinsics -> `OID004`. A per-FILE attribute cannot serve both dialects, so
399
+ // no answer is correct; this is reported, never guessed around.
400
+ //
401
+ // THE SOURCE-TEXT SCANS THAT REMAIN, AND WHY. `sourceDialectEvidence` feeds
402
+ // this section's diagnostic PROSE (`evidenceSummary`, OID003, OID004) —
403
+ // reporting what a file renders is not classifying it, and NO attribute below
404
+ // is decided from a file's own bytes. Not one: the region decides every stamp,
405
+ // and where the region and the file's rendered tags disagree the disagreement
406
+ // is said out loud at the volume its consequence deserves.
407
+ // ---------------------------------------------------------------------------
408
+
409
+ export type OidSurfaceDiagnosticCode = 'OID001' | 'OID002' | 'OID003' | 'OID004';
410
+
411
+ export interface OidSurfaceDiagnostic {
412
+ code: OidSurfaceDiagnosticCode;
413
+ /** Self-contained: names the file, the attribute that was stamped, why, and
414
+ * what the author can do about it. Written to be readable on its own in a
415
+ * terminal line or an editor console row — no engine source required. */
416
+ message: string;
417
+ }
418
+
419
+ /** How `resolveOidSurface` reached its answer. */
420
+ export type OidSurfaceVia =
421
+ /** A region's `include` glob names this exact file. */
422
+ | 'declared-include'
423
+ /** This exact file is a manifest root's `entry` — the manifest decides. */
424
+ | 'root-entry'
425
+ /** Reachable from exactly one region `entry` through the live import graph. */
426
+ | 'import-graph'
427
+ /** Nothing decided; `data-oid` is the default. */
428
+ | 'default';
429
+
430
+ export interface OidSurfaceDecision {
431
+ readonly file: string;
432
+ readonly attribute: 'data-oid' | 'userData-oid';
433
+ readonly surface?: DeclaredRootSurface;
434
+ readonly via: OidSurfaceVia;
435
+ readonly diagnostics: readonly OidSurfaceDiagnostic[];
436
+ }
437
+
438
+ /** One root as the diagnostic reports it — `id → surface (entry)`. */
439
+ function describeRoots(manifest: CachedManifestRoots): string {
440
+ const described = manifest.roots.map((root) => {
441
+ if (!root || typeof root !== 'object') return '(malformed root)';
442
+ const id = (root as { id?: unknown }).id;
443
+ const entry = typeof root.entry === 'string' ? root.entry : '(no entry)';
444
+ const surface = rootDeclaredSurface(root.adapter) ?? '(no surface)';
445
+ return `${typeof id === 'string' ? id : '(no id)'} → ${surface} (${entry})`;
446
+ });
447
+ return described.length > 0 ? described.join(', ') : '(no roots)';
448
+ }
449
+
450
+ /** Portable CSF is a design-time document. A component-only story commonly
451
+ * renders `<Story />` or a provider decorator and is reached from no runtime
452
+ * root, but importing it from one or giving it a manifest root would be exactly
453
+ * the wrong repair. The preview lane qualifies its rendered output
454
+ * independently, while selectable nodes retain the OIDs stamped in the
455
+ * prefab/component source they came from. */
456
+ function isPortableStoryModule(file: string): boolean {
457
+ return /\.stories\.[jt]sx?$/i.test(file);
458
+ }
459
+
460
+ function evidenceSummary(evidence: SourceDialectEvidence): string {
461
+ const parts: string[] = [];
462
+ if (evidence.reconcilerImport) parts.push('imports the R3F reconciler');
463
+ if (evidence.r3fOnlyTags.length > 0) {
464
+ parts.push(`renders R3F-only <${evidence.r3fOnlyTags.slice(0, 4).join('>, <')}>`);
465
+ }
466
+ if (evidence.domOnlyTags.length > 0) {
467
+ parts.push(`renders DOM-only <${evidence.domOnlyTags.slice(0, 4).join('>, <')}>`);
468
+ }
469
+ return parts.length > 0 ? parts.join('; ') : 'carries no dialect evidence of its own';
470
+ }
471
+
472
+ /**
473
+ * The file's own host elements are DOM elements: it renders at least one
474
+ * DOM-only intrinsic and NOT ONE R3F-only intrinsic.
475
+ *
476
+ * READ ONLY BY DIAGNOSTICS. It decides no attribute anywhere — every stamp
477
+ * below comes from the region answer (or, for a THREE host element, from the
478
+ * crash guard). What it is for is telling the difference between a
479
+ * disagreement worth an author's attention and one that is not:
480
+ *
481
+ * - a file the region places on `three` while its host elements are DOM is
482
+ * DRIFT, and `OID003` says so (the region still stamps, so the drift is
483
+ * visible in the editor rather than papered over);
484
+ * - an UNPLACED file whose host elements are DOM already carries the right
485
+ * attribute by construction — the default IS `data-oid` — so `OID001`
486
+ * stays quiet for it. Measured with this same resolver over the shipped
487
+ * projects: warning there would add 5 lines to a fresh template scaffold's
488
+ * boot and 6-8 to each example, on files whose stamp is correct. A
489
+ * diagnostic channel agents are told to trust cannot cry wolf.
490
+ */
491
+ function rendersOnlyDomHosts(evidence: SourceDialectEvidence): boolean {
492
+ return evidence.domOnlyTags.length > 0 && evidence.r3fOnlyTags.length === 0;
493
+ }
494
+
495
+ /**
496
+ * The file's own host elements are THREE objects: it renders at least one
497
+ * R3F-only intrinsic and NOT ONE DOM-only intrinsic.
498
+ *
499
+ * READ ONLY BY DIAGNOSTICS, exactly like {@link rendersOnlyDomHosts} — it
500
+ * decides no attribute. The asymmetry it feeds is in the diagnostic's PROSE,
501
+ * not in the answer: `userData-oid` on a DOM host element is NOISY (React logs
502
+ * an unrecognized prop and selection does not resolve), while `data-oid` on an
503
+ * R3F host element is FATAL and silently so for exactly one mount — fiber's
504
+ * `applyProps` pierces the dashed prop, `data` is not a property of any THREE
505
+ * class, so the first apply writes `object.data = '<oid>'` and the SECOND
506
+ * pierces into that string and throws `R3F: Cannot set "data-oid". Ensure it is
507
+ * an object before setting "oid".` (`userData` exists on every THREE object, so
508
+ * `userData-oid` is idempotent — proven in `ui-source-r3f-dialect.test.ts`.)
509
+ *
510
+ * MEASURED (2026-08-16, the starter-kit-3d-platformer port, live editor
511
+ * session): prefab modules whose only importer in Vite's graph at transform
512
+ * time was their own colocated `*.stories.tsx` were stamped `data-oid`; the
513
+ * second mount of the same story threw on the prefab's module-level shared
514
+ * geometry, 8 uncaught page errors in one session. Reach is an ORDER-DEPENDENT
515
+ * fact about one dev server's graph at one moment, which is why the repair is a
516
+ * DECLARATION — the region `include` this diagnostic names — and not a
517
+ * byte-level override that would make the declaration untestable.
518
+ */
519
+ function rendersOnlyR3fHosts(evidence: SourceDialectEvidence): boolean {
520
+ return evidence.r3fOnlyTags.length > 0 && evidence.domOnlyTags.length === 0;
521
+ }
522
+
523
+ /**
524
+ * THE FATAL DIRECTION of declared-vs-measured drift: a `dom`/`canvas` region
525
+ * owning a file whose host elements are THREE objects — see
526
+ * {@link rendersOnlyR3fHosts} for what `data-oid` does to one of those.
527
+ *
528
+ * Reported, and the region still stamps: which region owns a file is the game's
529
+ * to state, and the include glob is where it states it. `why` names how the
530
+ * non-`three` answer came to be believed, so the diagnostic reads as one
531
+ * sentence.
532
+ */
533
+ function r3fHostDriftDiagnostic(
534
+ absFile: string,
535
+ why: string,
536
+ evidence: SourceDialectEvidence,
537
+ rootsNote: string,
538
+ ): OidSurfaceDiagnostic {
539
+ return {
540
+ code: 'OID003',
541
+ message:
542
+ `${absFile} ${why}, but the file itself ${evidenceSummary(evidence)} and renders no DOM ` +
543
+ 'element at all — its host elements are THREE objects, and `data-oid` on one of those is ' +
544
+ 'not a degraded stamp: react-three-fiber pierces the dash, writes `object.data` on the ' +
545
+ 'first apply and THROWS on the next one (`R3F: Cannot set "data-oid"`), so this file will ' +
546
+ 'break the running game, not merely fail to select. If it really is part of a ' +
547
+ `\`dom\`/\`canvas\` region, move its R3F elements out of it; otherwise give it a \`three\` ` +
548
+ `root of its own, or declare it onto the \`three\` surface with an \`include\` glob (or a ` +
549
+ `\`mounts\` entry, if this root mounts \`three\` beside another surface) in ` +
550
+ `${ADAPTER_MODULE_FILENAME}. Roots considered: ${rootsNote}.`,
551
+ };
552
+ }
553
+
554
+ /** `via` in the OID vocabulary for a region answer. */
555
+ function viaOf(answer: FileRegionAnswer): OidSurfaceVia {
556
+ return answer.via === 'ambiguous' ? 'default' : answer.via;
557
+ }
558
+
559
+ /** How the diagnostic names the way a `three` surface came to be believed. */
560
+ function whyOf(answer: FileRegionAnswer): string {
561
+ if (answer.via === 'declared-include') {
562
+ return `is declared onto the \`${answer.regionId}\` region's \`three\` surface in ${ADAPTER_MODULE_FILENAME}`;
563
+ }
564
+ if (answer.via === 'root-entry')
565
+ return 'is declared as a `three` root entry in vgai.project.json';
566
+ return 'is reached only from a `three` root';
567
+ }
568
+
569
+ /** OID001's extra clause about what the defaulted file's own bytes say — the
570
+ * consequence, graded. A file whose HOST elements are THREE objects gets the
571
+ * `data-oid` default and will THROW inside fiber on the second apply (see
572
+ * {@link rendersOnlyR3fHosts}), so its clause says so; a file that merely
573
+ * IMPORTS the reconciler has nothing for a wrong stamp to land on and only
574
+ * fails to select. */
575
+ function unresolvableClause(evidence: SourceDialectEvidence): string {
576
+ if (rendersOnlyR3fHosts(evidence)) {
577
+ return (
578
+ ` — and this file ${evidenceSummary(evidence)}, so the \`data-oid\` default lands on THREE ` +
579
+ 'objects: react-three-fiber pierces the dash, writes `object.data` on the first apply and ' +
580
+ 'THROWS on the next one. Declaring this file is not optional cleanup — the running game ' +
581
+ 'breaks without it'
582
+ );
583
+ }
584
+ return sourceProvesR3f(evidence)
585
+ ? ` — and this file ${evidenceSummary(evidence)}, so its selection will NOT resolve`
586
+ : '';
587
+ }
588
+
589
+ /**
590
+ * The ONE decision the dev server's OID stamping and HMR classification
591
+ * both make, now WITH its reasoning attached.
592
+ *
593
+ * Precedence is the shared resolver's, and it has no source-text rung:
594
+ * 1. a region's `include` glob — or one of its `mounts` — names this file;
595
+ * 2. this file IS a region's `entry`;
596
+ * 3. exactly one surface reached through the live import graph.
597
+ * Anything else is AMBIGUOUS: `data-oid` is stamped as the documented DEFAULT
598
+ * (`via: 'default'`) and said out loud, rather than inferred from the file's
599
+ * own bytes. There is no exception in either direction — a file whose host
600
+ * elements contradict its region keeps the region's stamp and gets `OID003`,
601
+ * whichever way the contradiction points. A file that renders both dialects'
602
+ * intrinsics gets neither and the `OID004` diagnostic.
603
+ */
604
+ export function resolveOidSurface(
605
+ file: string,
606
+ code: string,
607
+ importersOf?: ImportersOf,
608
+ projectRoot?: string,
609
+ ): OidSurfaceDecision {
610
+ const absFile = resolve(file);
611
+ const evidence = sourceDialectEvidence(code);
612
+ const diagnostics: OidSurfaceDiagnostic[] = [];
613
+ const manifest = manifestOwning(absFile, projectRoot);
614
+
615
+ if (evidence.r3fOnlyTags.length > 0 && evidence.domOnlyTags.length > 0) {
616
+ diagnostics.push({
617
+ code: 'OID004',
618
+ message:
619
+ `${absFile} renders BOTH R3F-only (<${evidence.r3fOnlyTags.slice(0, 3).join('>, <')}>) ` +
620
+ `and DOM-only (<${evidence.domOnlyTags.slice(0, 3).join('>, <')}>) elements. The editor ` +
621
+ 'stamps ONE source-id attribute per file (`userData-oid` for the R3F reconciler, ' +
622
+ '`data-oid` for react-dom), so selection can only resolve for one of the two dialects ' +
623
+ 'in this file. Split the DOM part (e.g. the contents of a drei <Html>) into its own ' +
624
+ 'module to make both halves selectable.',
625
+ });
626
+ }
627
+
628
+ const answer = resolveProjectFileRegion(absFile, importersOf, projectRoot);
629
+ if (answer.surface !== undefined) {
630
+ // DRIFT, REPORTED AND NOT ACTED ON: a `three` region owning a file whose
631
+ // HOST ELEMENTS are DOM elements. The region still stamps — it is the
632
+ // game's own statement about which surface this file renders on, and a
633
+ // stamp derived from the file's bytes instead would make the declaration
634
+ // untestable, which is how a wrong declaration used to stay invisible. What
635
+ // the author sees is the disagreement itself, plus the two declarations
636
+ // that resolve it.
637
+ if (answer.surface === 'three' && rendersOnlyDomHosts(evidence)) {
638
+ diagnostics.push({
639
+ code: 'OID003',
640
+ message:
641
+ `${absFile} ${whyOf(answer)}, but the file itself ${evidenceSummary(evidence)} and ` +
642
+ 'renders no R3F element at all — its host elements are DOM elements, so the ' +
643
+ '`userData-oid` stamped from that region is an unrecognized React prop on every one of ' +
644
+ 'them and selection resolves for neither dialect. This is the normal shape for ' +
645
+ 'HUD/overlay modules that import `@react-three/fiber` only for `addEffect`/`useFrame`: ' +
646
+ `declare them onto this root's \`dom\` surface with a \`mounts\` entry in ` +
647
+ `${ADAPTER_MODULE_FILENAME}, or give them a \`dom\` root of their own in ` +
648
+ 'vgai.project.json. If this file really does author R3F content, render an R3F element ' +
649
+ `in it. Roots considered: ${manifest ? describeRoots(manifest) : '(none)'}.`,
650
+ });
651
+ }
652
+ // DRIFT, REPORTED AND NOT ACTED ON — the FATAL direction: a non-`three`
653
+ // region owning a file whose HOST ELEMENTS are THREE objects. The region
654
+ // still stamps, for the same reason it does above: a stamp derived from the
655
+ // file's bytes would make the declaration untestable. What differs is the
656
+ // consequence, and the diagnostic says it (`r3fHostDriftDiagnostic`).
657
+ if (answer.surface !== 'three' && rendersOnlyR3fHosts(evidence)) {
658
+ diagnostics.push(
659
+ r3fHostDriftDiagnostic(
660
+ absFile,
661
+ `belongs to a \`${answer.surface}\` region (${answer.via})`,
662
+ evidence,
663
+ manifest ? describeRoots(manifest) : '(none)',
664
+ ),
665
+ );
666
+ }
667
+ // DRIFT, REPORTED AND NOT ACTED ON: a non-`three` region reaching a file
668
+ // that IMPORTS the R3F reconciler but renders no host element of either
669
+ // dialect. Import reach is not render reach — a `dom` root CAN render an
670
+ // R3F `<Canvas>`, and its children are reached through the graph — so this
671
+ // is a real, nameable disagreement between the declaration and the source,
672
+ // with nothing in the file for a wrong stamp to land on. "Which region owns
673
+ // this file" stays the game's to state, and the include glob is where it
674
+ // states it; the diagnostic is the part that was load-bearing.
675
+ if (
676
+ answer.surface !== 'three' &&
677
+ evidence.reconcilerImport &&
678
+ evidence.r3fOnlyTags.length === 0 &&
679
+ evidence.domOnlyTags.length === 0
680
+ ) {
681
+ diagnostics.push({
682
+ code: 'OID003',
683
+ message:
684
+ `${absFile} belongs to a \`${answer.surface}\` region (${answer.via}), but the file ` +
685
+ `itself ${evidenceSummary(evidence)} — import reach is not render reach (a ` +
686
+ `\`${answer.surface}\` root can render an R3F <Canvas>). \`data-oid\` is stamped from ` +
687
+ 'the region, so any R3F element here will not resolve to a selection. Give this file a ' +
688
+ `\`three\` root of its own, or declare it onto the \`three\` surface with an \`include\` ` +
689
+ `glob (or a \`mounts\` entry) in ${ADAPTER_MODULE_FILENAME}. Roots considered: ` +
690
+ `${manifest ? describeRoots(manifest) : '(none)'}.`,
691
+ });
692
+ }
693
+ return {
694
+ file: absFile,
695
+ attribute: answer.surface === 'three' ? 'userData-oid' : 'data-oid',
696
+ surface: answer.surface,
697
+ via: viaOf(answer),
698
+ diagnostics,
699
+ };
700
+ }
701
+
702
+ if (answer.ambiguity === 'differing-surfaces') {
703
+ diagnostics.push({
704
+ code: 'OID002',
705
+ message:
706
+ `${absFile} is reached from regions of DIFFERING surfaces (${answer.reached.join(', ')}), ` +
707
+ 'so which reconciler renders its JSX is genuinely undecidable from reach — the editor ' +
708
+ 'stamped `data-oid`, the documented DEFAULT, and selection will not resolve for the ' +
709
+ 'other surface. Split the shared part into a module per surface, or declare it via a ' +
710
+ `region \`include\` glob in ${ADAPTER_MODULE_FILENAME}. Roots considered: ` +
711
+ `${manifest ? describeRoots(manifest) : '(none)'}.`,
712
+ });
713
+ } else if (
714
+ manifest &&
715
+ importersOf &&
716
+ // The noise gate — see `rendersOnlyDomHosts`: an unplaced file whose host
717
+ // elements are DOM already carries the attribute it needs, because the
718
+ // default IS `data-oid`. Warning there would fire on ordinary components in
719
+ // every shipped project, and noise is how a real signal gets ignored.
720
+ !rendersOnlyDomHosts(evidence) &&
721
+ // The story exemption is a NOISE gate too, and it stops at the fatal case:
722
+ // an unplaced story whose own host elements are THREE objects takes the
723
+ // `data-oid` default onto them and throws inside fiber on the second mount
724
+ // (see `rendersOnlyR3fHosts` — that is the exact shape the 2026-08-16
725
+ // measurement took). Silence about a crash is not noise reduction.
726
+ (!isPortableStoryModule(absFile) || rendersOnlyR3fHosts(evidence))
727
+ ) {
728
+ // Gated on `importersOf` because "no region reaches it" is only a FINDING
729
+ // when a graph was actually consulted; without one (a `vite build` of the
730
+ // hosted editor, a one-off transform in a test) it is just ignorance, and
731
+ // claiming it would warn about every non-entry file in the project.
732
+ diagnostics.push({
733
+ code: 'OID001',
734
+ message:
735
+ `${absFile} is inside the project at ${join(manifest.manifestDir, 'vgai.project.json')} but ` +
736
+ 'no region reaches it and none declares it, so the source-id attribute it carries is a ' +
737
+ `DEFAULT, not a decision${unresolvableClause(evidence)}. Import it from a region entry (directly or ` +
738
+ 'transitively), give it a root, or declare it via a region `include` glob in ' +
739
+ `${ADAPTER_MODULE_FILENAME}. Roots considered: ${describeRoots(manifest)}.`,
740
+ });
741
+ }
742
+
743
+ if (manifest?.includesUnreadable) {
744
+ diagnostics.push({
745
+ code: 'OID001',
746
+ message:
747
+ `${absFile}: ${join(manifest.manifestDir, ADAPTER_MODULE_FILENAME)} declares a \`regions\` ` +
748
+ 'binding this tier could not read statically, so any `include` globs in it were NOT ' +
749
+ 'consulted for this file. The adapter table must be statically evaluable — write the ' +
750
+ 'regions as an array of object literals with literal `id` and `include` values.',
751
+ });
752
+ }
753
+
754
+ return { file: absFile, attribute: 'data-oid', via: 'default', diagnostics };
755
+ }
756
+
757
+ /**
758
+ * The graph-FREE subset of the diagnostics above: the conflicts that a file's
759
+ * own bytes and the manifest settle between them, with no live import graph
760
+ * involved. Called by `server/project-validation.ts`'s `validateSource`, which
761
+ * runs on every project source write and reports through the three surfaces
762
+ * issue #103 established — the terminal, the editor console (the `server-log`
763
+ * SSE event), and `/__editor/state` (what `vgai status` prints).
764
+ *
765
+ * Excludes `OID001`/`OID002` by construction: both need graph reach to be true,
766
+ * and without a graph EVERY non-entry file looks unreachable — a warning on
767
+ * every child component would be noise, and noise is how a real signal gets
768
+ * ignored. (`resolveOidSurface` already refuses to emit `OID001` without an
769
+ * `importersOf`; the filter below states the same intent locally.) Those two are
770
+ * emitted at stamp time instead, where the graph exists.
771
+ */
772
+ export function oidSurfaceSourceConflicts(
773
+ file: string,
774
+ code: string,
775
+ ): readonly OidSurfaceDiagnostic[] {
776
+ const absFile = resolve(file);
777
+ const evidence = sourceDialectEvidence(code);
778
+ // OID003 is kept as well as OID004 here: with no graph, the only OID003s
779
+ // `resolveOidSurface` can reach are its HOST-ELEMENT disagreements firing on
780
+ // a file the manifest itself places (a root `entry`, or a declared
781
+ // `include`/`mounts`) — genuine declaration-vs-source conflicts, graph-free by
782
+ // construction. Each reports the stamp it produced, so nothing below repeats
783
+ // them.
784
+ const diagnostics = resolveOidSurface(absFile, code).diagnostics.filter(
785
+ (diagnostic) => diagnostic.code === 'OID004' || diagnostic.code === 'OID003',
786
+ );
787
+ const direct = declaredSurfaceForProjectFile(absFile);
788
+ // The leftover case those do not cover: an entry that IMPORTS the reconciler
789
+ // while rendering no host element of EITHER dialect. A `dom`/`canvas` entry
790
+ // that imports fiber for `addEffect`/`useFrame` and renders DOM elements has
791
+ // no R3F element to fail to resolve; one that renders THREE objects is the
792
+ // fatal-direction drift, already reported above.
793
+ if (
794
+ direct !== undefined &&
795
+ direct !== 'three' &&
796
+ evidence.reconcilerImport &&
797
+ evidence.r3fOnlyTags.length === 0 &&
798
+ evidence.domOnlyTags.length === 0
799
+ ) {
800
+ diagnostics.push({
801
+ code: 'OID003',
802
+ message:
803
+ `${absFile} is declared as a \`${direct}\` root entry in vgai.project.json, but the file ` +
804
+ `${evidenceSummary(evidence)}. The manifest wins (\`data-oid\` is stamped), so any R3F ` +
805
+ 'element here will not resolve to a selection. Declare a `three` root for the R3F part.',
806
+ });
807
+ }
808
+ return diagnostics;
809
+ }
810
+
811
+ /** Warn-once keys, so a hot module re-transformed on every keystroke prints its
812
+ * diagnostic once per reason instead of once per edit. */
813
+ const reportedDiagnostics = new Set<string>();
814
+
815
+ /**
816
+ * Print a decision's diagnostics to the dev server's terminal, once per
817
+ * (file, code). `[ui-oid]` is the prefix the OID plugin's existing warnings
818
+ * already use, so this lands in the same place an author is already reading
819
+ * when `vgai edit` is running.
820
+ */
821
+ export function reportOidSurfaceDiagnostics(
822
+ decision: OidSurfaceDecision,
823
+ warn: (message: string) => void = (message) => console.warn(message),
824
+ ): void {
825
+ for (const diagnostic of decision.diagnostics) {
826
+ const key = `${decision.file}::${diagnostic.code}`;
827
+ if (reportedDiagnostics.has(key)) continue;
828
+ reportedDiagnostics.add(key);
829
+ warn(`[ui-oid] ${diagnostic.code}: ${diagnostic.message}`);
830
+ }
831
+ }