@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,881 @@
1
+ /**
2
+ * The product door onto EDITOR CHROME — read and drive what a person sees in
3
+ * the editor's named surfaces, through the session, without play mode.
4
+ *
5
+ * ## Why this exists (WO: "No product door drives or reads EDITOR CHROME")
6
+ *
7
+ * `vgai eval`'s `page()` step is play-mode-gated and rooted at the GAME
8
+ * container, so an editor surface that is not a running game — a capability's
9
+ * workspace document, the Data sheet, the Project Tools catalog — could be
10
+ * neither driven nor read through the product at all. The Sheets build could
11
+ * not live-verify a TSV paste or a sheet-tab click: it had a screenshot
12
+ * (`editor.captureActiveDocument`) and nothing else. That gap is the defect the
13
+ * doctrine names — when a live surface can only be diagnosed out of band, the
14
+ * product's own doors have failed.
15
+ *
16
+ * ## The API decision, and why it is this small
17
+ *
18
+ * ONE wire command (`document-probe`) carrying a step union, exposed on the
19
+ * live side as `editor.document.{query,click,drag,key,type,paste,select}`.
20
+ * Each verb is a thing an agent cannot otherwise do and cannot fake:
21
+ *
22
+ * - `query` — READ. What is rendered, as data: tag, role, text, attributes,
23
+ * value/checked/disabled, rect. The one door that answers "what does the
24
+ * human see" for a surface that is not a canvas.
25
+ * - `click` — a REAL user gesture (pointerdown/mousedown/mouseup/click plus
26
+ * focus), not `element.click()`, because a component listening on
27
+ * `pointerdown` (react-data-grid's cell selection does) is invisible to the
28
+ * synthetic shortcut. `clicks: 2` is a real DOUBLE click (numbered
29
+ * `detail` plus the `dblclick` React's `onDoubleClick` listens for) — the
30
+ * Outliner's own rename gesture, and an option on the same verb rather
31
+ * than a second one because it is the same gesture on the same target.
32
+ * - `type` — text into a focused field, character by character, committed
33
+ * with Enter. `paste` cannot stand in: an untrusted `ClipboardEvent`
34
+ * performs no default action, so a plain `<input>` keeps its old value.
35
+ * - `key` — keydown/keyup on the explicit target, else on whatever inside the
36
+ * scope has focus. Arrow keys ARE the spreadsheet's cursor. (No `keypress`:
37
+ * it is deprecated and no shipped surface listens for it.)
38
+ * - `paste` — a real `ClipboardEvent` with a populated `DataTransfer`, which
39
+ * is the only way to exercise a paste handler; nothing else in the product
40
+ * can produce one. Verified live against the Data sheet's own `onPaste`,
41
+ * which read the TSV and wrote the file.
42
+ * - `select` — choose a value on a `<select>`. `click` genuinely cannot do
43
+ * this: the option list of a native dropdown is drawn by the OS, so there
44
+ * is nothing inside the document's container for a pointer gesture to
45
+ * resolve against. And a plain `element.value = x` is a no-op against
46
+ * React, which caches the last value it wrote on the node and puts it back
47
+ * on the next render. So this verb goes through the PROTOTYPE's own value
48
+ * setter (which bypasses that cache) and then dispatches `input`/`change` —
49
+ * the established spelling from this repo's own probes, and the one React's
50
+ * synthetic-event layer honours. Without it the animation transport's clip
51
+ * and speed — two `<select>`s — were undrivable through the product.
52
+ *
53
+ * There is deliberately NO `screenshot` verb: `editor.captureActiveDocument()`
54
+ * already captures the document's content (`editor-view-presentation.ts`'s
55
+ * `activeDocumentContent`); this door's scope is the document's whole box —
56
+ * that content plus its header strip and shelf rail — and a synonym would be
57
+ * a second name for one behavior.
58
+ *
59
+ * ## Scope is a VOCABULARY, and the contract is that it is closed
60
+ *
61
+ * This is NOT general page automation, and the refusal is how that stays true.
62
+ * Every step resolves against ONE NAMED SURFACE — `document` (the default),
63
+ * `header`, `shelf`, `rail`, `outliner`, `content` ({@link DocumentProbeScope}) — and a
64
+ * selector that matches nothing inside it, or a resolved element that is not a
65
+ * descendant of it, is REFUSED with a message naming the scope rather than
66
+ * silently reaching into the rest of the page.
67
+ *
68
+ * ## How a scope finds its element: the owner's own stamp, never a class guess
69
+ *
70
+ * The Properties rail and the Outliner are VS CODE VIEWS (`vgai.properties`,
71
+ * `vgai.outliner`) holding React portals of ours, so their roots are not
72
+ * anywhere near the document's box and cannot be reached by walking down from
73
+ * it. They are found by the id THE WORKBENCH REGISTERED: the contribution
74
+ * hands each view's body element over as a named part, and `offerVgaiPart`
75
+ * (`frame/bridge.tsx`) stamps it `data-vgai-part="<part>"` at the moment of
76
+ * the handover. A pane that is disposed takes its stamped element with it, so
77
+ * a closed view answers "not open" instead of matching a stale node. The same
78
+ * shape as `activeDocumentContainer`'s `data-workspace-document-id` and as the
79
+ * portal pair below: the surface publishes its own identity, and this module
80
+ * never guesses from a class name.
81
+ *
82
+ * `rail` and `outliner` are EDITOR CHROME rather than the active document, so
83
+ * (unlike `document`/`header`/`shelf`) they are reachable while the Game
84
+ * document is active and while no document is open at all. The `editor.
85
+ * hierarchy()` / `editor.inspect()` doors still report what those panels
86
+ * RESOLVED; this door reports what they DREW and drives their controls —
87
+ * WALK 5's beats 7 and 12 are the gap it closes (`editor.setField('name')`
88
+ * writes the name, but it is not the Outliner's rename, and nothing at all
89
+ * could click a Properties tab).
90
+ *
91
+ * Two deliberate consequences of the containment rule:
92
+ *
93
+ * - A PORTALED overlay THIS DOCUMENT OPENED is in scope; anyone else's is
94
+ * not. Amended 2026-09-21 (B6), and what changed is the DOM rather than the
95
+ * rule: this note used to say a popover portaled to the theme root recorded
96
+ * no ownership, so any heuristic wide enough to reach it would also reach
97
+ * the editor's chrome menus. `ThemeRootPortal` records it now — an anchor
98
+ * span where the portal was declared and a matching stamp on what landed —
99
+ * so {@link scopeRoots} walks that pair from inside the document's box and
100
+ * reaches exactly the overlays the document opened. A chrome menu's anchor
101
+ * is in the chrome, so it is still refused by the same containment check.
102
+ * Without this a document's own header MENU could be opened by its trigger
103
+ * and never chosen from, which is "a control is accepted through its own
104
+ * click" failing one step short (measured on the Model document's Add
105
+ * menu). Widening further — every `.vgai-menu` on the root — is still the
106
+ * "general automation framework" this module exists to not become.
107
+ * - The GAME document is refused outright by the three scopes that ARE the
108
+ * active document (see `resolveDocumentScope`): a game is driven through
109
+ * its own registered commands and the play-gated `page` door, never
110
+ * through synthetic DOM gestures. `rail` and `outliner` are the editor's
111
+ * panels around it and stay open.
112
+ */
113
+
114
+ import type {
115
+ DocumentKeyStep,
116
+ DocumentProbeResult,
117
+ DocumentProbeScope,
118
+ DocumentProbeStep,
119
+ ProbedElement,
120
+ } from '@volter/editor-sdk/document-probe';
121
+ import { GAME_DOCUMENT_ID } from './workspace-document-ids';
122
+ import {
123
+ activeWorkspaceDocument,
124
+ activeWorkspaceDocumentId,
125
+ activeWorkspaceDocumentViewId,
126
+ } from './workspace-document-registry';
127
+
128
+ /** The active document's mounted content element — what the `document`,
129
+ * `header` and `shelf` scopes resolve from. Shared with
130
+ * `editor-view-presentation.ts`'s capture so a probe and a screenshot can
131
+ * never disagree about what "the active document" is. */
132
+ export function activeDocumentContainer(documentId: string): HTMLElement | null {
133
+ return (
134
+ Array.from(document.querySelectorAll<HTMLElement>('[data-workspace-document-id]')).find(
135
+ (element) =>
136
+ element.dataset['workspaceDocumentId'] === documentId &&
137
+ (activeWorkspaceDocumentViewId() === null ||
138
+ element.dataset['workspaceViewId'] === activeWorkspaceDocumentViewId()),
139
+ ) ?? null
140
+ );
141
+ }
142
+
143
+ interface Scope {
144
+ readonly container: HTMLElement;
145
+ readonly name: DocumentProbeScope;
146
+ readonly id: string;
147
+ readonly title: string;
148
+ }
149
+
150
+ /** THE VOCABULARY, and the one place each word is spelled. Every name resolves
151
+ * through its surface's OWN stamp — see this module's header. */
152
+ const SCOPE_NAMES: readonly DocumentProbeScope[] = [
153
+ 'document',
154
+ 'header',
155
+ 'shelf',
156
+ 'rail',
157
+ 'outliner',
158
+ 'content',
159
+ ];
160
+
161
+ /** The two scopes that are VS Code views: the part id the contribution hands
162
+ * over (`frame/bridge.tsx`'s `offerVgaiPart` stamps it), and the view id the
163
+ * workbench registered it under (`vgai.contribution.ts`). */
164
+ const VIEW_SCOPES = {
165
+ rail: { part: 'properties', view: 'vgai.properties', title: 'Properties' },
166
+ outliner: { part: 'outliner', view: 'vgai.outliner', title: 'Outliner' },
167
+ content: { part: 'content', view: 'vgai.content', title: 'Content' },
168
+ } as const satisfies Record<string, { part: string; view: string; title: string }>;
169
+
170
+ /** The two scopes that are strips of the ACTIVE DOCUMENT's own box, by the
171
+ * `data-testid` their components write (`DocumentHeaderStrip`,
172
+ * `DocumentShelfRail`). */
173
+ const DOCUMENT_STRIPS = {
174
+ header: { prefix: 'document-header', title: 'header strip' },
175
+ shelf: { prefix: 'document-shelf', title: 'tool shelf' },
176
+ } as const satisfies Record<string, { prefix: string; title: string }>;
177
+
178
+ /**
179
+ * EVERY ROOT THIS SCOPE COVERS — the document's own box, and whatever it
180
+ * PORTALED out of itself.
181
+ *
182
+ * `ThemeRootPortal` writes an anchor span where the portal was declared and
183
+ * stamps both ends with one generated id (`data-vgai-portal` in place,
184
+ * `data-vgai-portal-content` on what landed at the theme root). So a menu the
185
+ * document's own header opened is found by walking that pair from INSIDE the
186
+ * box — not by a heuristic over every `.vgai-menu` on the root, which would
187
+ * also reach the editor's chrome menus and is exactly the widening this
188
+ * module's header rules out. A portal the chrome opened has its anchor in the
189
+ * chrome, so it is not a root here and stays refused.
190
+ *
191
+ * Re-read per step rather than cached with the scope: a menu opens and closes
192
+ * between steps, which is the whole point of driving one.
193
+ */
194
+ function scopeRoots(scope: Scope): HTMLElement[] {
195
+ const roots: HTMLElement[] = [scope.container];
196
+ // TRANSITIVE, because a portal opens a portal: a menu's PANEL is portaled,
197
+ // and a submenu opened from one of its rows is portaled from inside THAT
198
+ // panel — so the second anchor is not in the document's box at all. One
199
+ // level deep found the Add menu's rows and none of Mesh's (measured
200
+ // 2026-09-21 on the Model document). The walk terminates because each id is
201
+ // taken once.
202
+ for (let index = 0; index < roots.length; index++) {
203
+ for (const anchor of roots[index]!.querySelectorAll<HTMLElement>('[data-vgai-portal]')) {
204
+ const id = anchor.dataset['vgaiPortal'];
205
+ if (id === undefined) continue;
206
+ const content = document.querySelector<HTMLElement>(
207
+ `[data-vgai-portal-content="${CSS.escape(id)}"]`,
208
+ );
209
+ if (content && !roots.includes(content)) roots.push(content);
210
+ }
211
+ }
212
+ return roots;
213
+ }
214
+
215
+ /** A registered VIEW's handed-over body, by the part id the workbench stamped
216
+ * on it. `null` when the view is closed — its pane took the element with it. */
217
+ function viewPart(part: string): HTMLElement | null {
218
+ return document.querySelector<HTMLElement>(`[data-vgai-part="${part}"]`);
219
+ }
220
+
221
+ function resolveScope(name: DocumentProbeScope): Scope {
222
+ const view =
223
+ name === 'rail' || name === 'outliner' || name === 'content' ? VIEW_SCOPES[name] : null;
224
+ if (view) {
225
+ const container = viewPart(view.part);
226
+ if (!container) {
227
+ throw new Error(
228
+ `The ${view.title} view (${view.view}) is not open, so scope '${name}' has nothing to ` +
229
+ 'reach. Open it from the workbench (View: Open View…) and retry — this door reads ' +
230
+ 'and drives what is on screen, and reports its absence rather than an empty match list.',
231
+ );
232
+ }
233
+ return { container, name, id: view.view, title: view.title };
234
+ }
235
+ return resolveDocumentScope(name);
236
+ }
237
+
238
+ /** `document`, `header` and `shelf` — the ACTIVE CENTRE DOCUMENT's box and the
239
+ * two strips inside it. */
240
+ function resolveDocumentScope(name: DocumentProbeScope): Scope {
241
+ const active = activeWorkspaceDocument();
242
+ if (!active)
243
+ throw new Error(
244
+ `No active editor document, so scope '${name}' has nothing to reach. The editor's own ` +
245
+ "views are still readable: scope 'outliner', scope 'rail', and scope 'content'.",
246
+ );
247
+ const id = activeWorkspaceDocumentId() ?? active.descriptor.id;
248
+ // The Game document is the one center document with its OWN doors — and the
249
+ // one where a synthetic DOM gesture is the exact instrument the doctrine
250
+ // bans ("never pilot a game with synthetic key events"). Its input is
251
+ // play-gated (`InputManager.setEnabled` + `gated-globals.ts`), so a probe
252
+ // here would either silently do nothing (not playing) or bypass the play
253
+ // path's contract (playing). Refuse toward the honest doors instead of
254
+ // becoming an ungated second one.
255
+ if (id === GAME_DOCUMENT_ID) {
256
+ throw new Error(
257
+ "The Game document is out of this door's scope: drive and read a game through its own " +
258
+ 'doors — `game.commands()`/`game.command(n)`/`game.state(n)`, or `page(step)` during ' +
259
+ "play — never through synthetic DOM gestures. The editor's own views around it stay " +
260
+ "readable while a game is active: scope 'outliner' and scope 'rail'.",
261
+ );
262
+ }
263
+ const container = activeDocumentContainer(id);
264
+ if (!container) {
265
+ throw new Error(
266
+ `The active document's surface is not mounted: ${id} (${active.title}). ` +
267
+ 'Present it first (`editor.present`) and retry.',
268
+ );
269
+ }
270
+ // The scope is the document's WHOLE box — its header strip and its shelf
271
+ // rail as well as its content (`WorkspaceDocumentSurface`: the strip is a
272
+ // sibling of the content element the id is written on). Three builders
273
+ // measured the same wall: a document's own header controls and menus were
274
+ // unreachable and unreadable through the product, so a header could not be
275
+ // driven by its own click. The Game document's refusal above still holds.
276
+ const box = container.closest<HTMLElement>('.vgai-dock-document') ?? container;
277
+ if (name === 'document') return { container: box, name, id, title: active.title };
278
+ // `header` / `shelf` — MEASURED to be inside the box already (the strips are
279
+ // this element's own children), so these names buy AIM rather than reach: a
280
+ // selector that also matches in the content can be pointed at one strip
281
+ // without counting indices. A document that draws no strip (`runtime:
282
+ // false` and no toolbar, or a workspace whose skin hides headers) refuses by
283
+ // name rather than answering zero matches.
284
+ const strip = DOCUMENT_STRIPS[name as keyof typeof DOCUMENT_STRIPS];
285
+ const element = box.querySelector<HTMLElement>(`[data-testid="${strip.prefix}:${id}"]`);
286
+ if (!element) {
287
+ throw new Error(
288
+ `The active document "${active.title}" (${id}) draws no ${strip.title}, so scope ` +
289
+ `'${name}' has nothing to reach. Its whole box is scope 'document'.`,
290
+ );
291
+ }
292
+ return { container: element, name, id, title: `${active.title} — ${strip.title}` };
293
+ }
294
+
295
+ /** The refusal that keeps this door scoped — it names the scope every time,
296
+ * and the vocabulary, because "wrong scope" is the likeliest cause. */
297
+ function outOfScope(scope: Scope, detail: string): Error {
298
+ return new Error(
299
+ `Out of scope: ${detail}. This step ran in scope '${scope.name}' — ` +
300
+ `"${scope.title}" (${scope.id}) — and reaches ONLY inside it. The other surfaces are ` +
301
+ `named, not walked to: ${SCOPE_NAMES.filter((name) => name !== scope.name)
302
+ .map((name) => `'${name}'`)
303
+ .join(', ')} (pass \`{ scope }\`). The rest of the page is deliberately unreachable.`,
304
+ );
305
+ }
306
+
307
+ const MAX_TEXT = 400;
308
+
309
+ /**
310
+ * The sentinel a custom property is resolved against.
311
+ *
312
+ * `getComputedStyle(el).getPropertyValue('--x')` answers with the DECLARED
313
+ * text, so a theme token reads back as its own `color-mix(…)` algebra rather
314
+ * than the colour it paints. To get the paint, the value has to go through a
315
+ * property the browser actually resolves. A throwaway span inside the element
316
+ * inherits the same custom-property cascade, so `color: var(--x)` on it
317
+ * computes to `rgb(…)`.
318
+ *
319
+ * The sentinel is how a NON-colour token stays honest: `color` silently keeps
320
+ * its previous value when the new one does not parse, so a token holding a
321
+ * length would otherwise be reported as whatever colour happened to be there.
322
+ * Seed the sentinel, apply the var, and an unchanged reading means "this did
323
+ * not resolve as a colour" — report the declared text instead of a lie.
324
+ */
325
+ const STYLE_PROBE_SENTINEL = 'rgb(1, 2, 3)';
326
+
327
+ /**
328
+ * `background-color`'s INITIAL value, which is what the probe below computes
329
+ * to whenever the substituted token is not a colour — CSS's own answer for a
330
+ * declaration that is invalid at computed-value time on a NON-INHERITED
331
+ * property.
332
+ */
333
+ const STYLE_PROBE_INITIAL = 'rgba(0, 0, 0, 0)';
334
+
335
+ /**
336
+ * What a custom property PAINTS, its declared text when it is not a colour,
337
+ * or the empty string when THE PROPERTY IS NOT DECLARED AT ALL.
338
+ *
339
+ * That last case is the one this door got wrong first and a builder caught: an
340
+ * undeclared `var(--x)` makes the whole declaration invalid, so the probe fell
341
+ * back and the reading came back as the surrounding text colour — a confident
342
+ * wrong answer, identical for a real token, a misspelled one and a group the
343
+ * palette does not declare. (Measured under Classic:
344
+ * `--vgai-viewport-background` and a deliberately nonexistent name both
345
+ * answered `rgb(197, 200, 206)`, and a reader nearly concluded Classic
346
+ * declares a viewport group.) A custom property's computed value is the empty
347
+ * string exactly when it is undeclared, so that is the answer.
348
+ *
349
+ * THE PAINT PROPERTY IS `background-color`, AND THAT IS THE WHOLE TRICK. It
350
+ * used to be `color`, with a sentinel seeded first and the reasoning "an
351
+ * unchanged reading means this did not resolve as a colour". That reasoning is
352
+ * wrong, because `color` is INHERITED: a declaration invalid at computed-value
353
+ * time does not keep its previous value, it takes the INHERITED one — so a
354
+ * token holding a length read back as whatever colour the surrounding text
355
+ * happened to be, the same confident lie one paragraph up. Measured 2026-09-21
356
+ * on the Outliner: `--vgai-tree-row-height`, which holds `20px`, answered
357
+ * `rgb(195, 195, 195)` while a reader was measuring row heights with it.
358
+ * `background-color` is NOT inherited, so the same invalid declaration lands
359
+ * on its initial value — `rgba(0, 0, 0, 0)`, a constant this module knows —
360
+ * and "did this resolve as a colour" becomes a fact instead of a guess. The
361
+ * sentinel is still seeded, so a browser that somehow leaves the declaration
362
+ * untouched is also caught. A token whose value IS `transparent` reports its
363
+ * declared text, which is the more useful of the two true answers.
364
+ */
365
+ function resolveCustomProperty(element: HTMLElement, name: string): string {
366
+ const declared = getComputedStyle(element).getPropertyValue(name).trim();
367
+ if (declared === '') return '';
368
+ const owner = element.ownerDocument;
369
+ const probe = owner.createElement('span');
370
+ // Out of flow and invisible: this must not reflow the surface being measured.
371
+ probe.style.position = 'absolute';
372
+ probe.style.pointerEvents = 'none';
373
+ probe.style.visibility = 'hidden';
374
+ probe.style.backgroundColor = STYLE_PROBE_SENTINEL;
375
+ element.appendChild(probe);
376
+ try {
377
+ probe.style.backgroundColor = `var(${name})`;
378
+ const painted = getComputedStyle(probe).backgroundColor.trim();
379
+ return painted === STYLE_PROBE_SENTINEL || painted === STYLE_PROBE_INITIAL ? declared : painted;
380
+ } finally {
381
+ probe.remove();
382
+ }
383
+ }
384
+
385
+ function resolveStyles(element: HTMLElement, names: readonly string[]): Record<string, string> {
386
+ const computed = getComputedStyle(element);
387
+ const styles: Record<string, string> = {};
388
+ for (const name of names) {
389
+ styles[name] = name.startsWith('--')
390
+ ? resolveCustomProperty(element, name)
391
+ : // A standard property is already RESOLVED by the browser here, which is
392
+ // the whole reason to ask for it: the token's expression has become the
393
+ // rgb the person sees.
394
+ computed.getPropertyValue(cssPropertyName(name)).trim();
395
+ }
396
+ return styles;
397
+ }
398
+
399
+ /** `backgroundColor` → `background-color`; a name already dashed passes through. */
400
+ function cssPropertyName(name: string): string {
401
+ return name.includes('-') ? name : name.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`);
402
+ }
403
+
404
+ function describeElement(
405
+ element: Element,
406
+ index: number,
407
+ styles?: readonly string[],
408
+ ): ProbedElement {
409
+ const html = element as HTMLElement;
410
+ const attributes: Record<string, string> = {};
411
+ for (const attribute of Array.from(element.attributes)) {
412
+ attributes[attribute.name] = attribute.value;
413
+ }
414
+ const rect = html.getBoundingClientRect();
415
+ const value = (element as HTMLInputElement).value;
416
+ const checked = (element as HTMLInputElement).checked;
417
+ // `||`, not `??`: in a BACKGROUNDED tab — this door's primary caller —
418
+ // Chrome answers `innerText` with the empty string for everything, so the
419
+ // nullish fallback never fired and every probe read a blank document
420
+ // (measured live, 2026-08-14). `textContent` is the honest answer there.
421
+ const text = (html.innerText || element.textContent || '').trim();
422
+ return {
423
+ index,
424
+ tag: element.tagName.toLowerCase(),
425
+ text: text.length > MAX_TEXT ? `${text.slice(0, MAX_TEXT)}…` : text,
426
+ attributes,
427
+ rect: { x: rect.x, y: rect.y, width: rect.width, height: rect.height },
428
+ ...(typeof value === 'string' ? { value } : {}),
429
+ ...(typeof checked === 'boolean' ? { checked } : {}),
430
+ ...(html.hasAttribute('disabled') ? { disabled: true } : {}),
431
+ ...(styles && styles.length > 0 ? { styles: resolveStyles(html, styles) } : {}),
432
+ };
433
+ }
434
+
435
+ /** Resolve one target inside the scope, refusing loudly rather than reaching out. */
436
+ function resolveTarget(scope: Scope, selector: string, index: number): HTMLElement {
437
+ const matches = matchesIn(scope, selector);
438
+ const element = matches[index];
439
+ if (!element) {
440
+ throw outOfScope(
441
+ scope,
442
+ `${JSON.stringify(selector)} matched ${matches.length} element(s), so index ${index} does not exist`,
443
+ );
444
+ }
445
+ // querySelectorAll on an element is already descendants-only; this asserts
446
+ // the invariant rather than trusting it. A PORTALED overlay the document
447
+ // opened is one of the roots ({@link scopeRoots}), so it passes here by
448
+ // being inside the root that owns it and not by any relaxation.
449
+ if (!scopeRoots(scope).some((root) => root.contains(element))) {
450
+ throw outOfScope(
451
+ scope,
452
+ `${JSON.stringify(selector)} resolved outside the document's container`,
453
+ );
454
+ }
455
+ return element;
456
+ }
457
+
458
+ function pointerInit(element: HTMLElement): MouseEventInit {
459
+ const rect = element.getBoundingClientRect();
460
+ return {
461
+ bubbles: true,
462
+ cancelable: true,
463
+ composed: true,
464
+ view: window,
465
+ clientX: rect.x + rect.width / 2,
466
+ clientY: rect.y + rect.height / 2,
467
+ button: 0,
468
+ buttons: 1,
469
+ };
470
+ }
471
+
472
+ /**
473
+ * A REAL click: the same event sequence a mouse produces. `element.click()`
474
+ * dispatches only `click`, so a component that selects on `pointerdown` (the
475
+ * sheet grid's cell selection) never sees the gesture at all.
476
+ *
477
+ * `clicks` > 1 is a REAL double click and not two calls to this function: the
478
+ * browser numbers consecutive presses in `detail`, and a `dblclick` follows
479
+ * the second `click`. React's `onDoubleClick` listens for that `dblclick` and
480
+ * nothing else, so a rename driven by two separate single clicks never starts
481
+ * (measured against the Outliner's own `onDoubleClick` → `onStartEditing`).
482
+ */
483
+ function dispatchClick(element: HTMLElement, clicks = 1): void {
484
+ const init = pointerInit(element);
485
+ const total = Math.max(1, Math.round(clicks));
486
+ withoutPointerCapture(element, () => {
487
+ for (let n = 1; n <= total; n++) {
488
+ const down = { ...init, detail: n };
489
+ const up = { ...init, buttons: 0, detail: n };
490
+ element.dispatchEvent(new PointerEvent('pointerdown', { ...down, pointerType: 'mouse' }));
491
+ element.dispatchEvent(new MouseEvent('mousedown', down));
492
+ element.focus();
493
+ element.dispatchEvent(new PointerEvent('pointerup', { ...up, pointerType: 'mouse' }));
494
+ element.dispatchEvent(new MouseEvent('mouseup', up));
495
+ element.dispatchEvent(new MouseEvent('click', up));
496
+ }
497
+ if (total >= 2) {
498
+ element.dispatchEvent(new MouseEvent('dblclick', { ...init, buttons: 0, detail: total }));
499
+ }
500
+ });
501
+ }
502
+
503
+ /**
504
+ * TYPE `text` into a field the way a person does — one character at a time,
505
+ * through the PROTOTYPE's value setter, between real `keydown`/`keyup`.
506
+ *
507
+ * Three traps this exists for, each measured on a shipped surface:
508
+ * - `paste` cannot stand in. An untrusted `ClipboardEvent` performs NO
509
+ * default action, so a plain `<input>` with no paste handler keeps its old
510
+ * value and the probe reports a click it never made.
511
+ * - `element.value = x` is invisible to React (the value tracker it installs
512
+ * on the node compares the new value against itself and re-renders the old
513
+ * one) — the same trap {@link setSelectValue} documents, and the same cure.
514
+ * - a field may commit on a KEY rather than on `input`. The Outliner's rename
515
+ * reads `event.currentTarget.value` inside `onKeyDown` for Enter; a numeric
516
+ * field in the Properties rail blurs on Enter. So the keys are real and the
517
+ * Enter is the caller's choice, not an implicit one.
518
+ */
519
+ function typeInto(element: HTMLElement, text: string, replace: boolean, enter: boolean): void {
520
+ const editable =
521
+ element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement ? element : null;
522
+ if (!editable) {
523
+ throw new Error(
524
+ `Cannot type into a <${element.tagName.toLowerCase()}>: this verb drives a text field. ` +
525
+ "Click the control that opens one first (the Outliner's rename is a double click — " +
526
+ '`click(selector, { clicks: 2 })`), then `type` into the field it puts up.',
527
+ );
528
+ }
529
+ const proto =
530
+ element instanceof HTMLTextAreaElement
531
+ ? HTMLTextAreaElement.prototype
532
+ : HTMLInputElement.prototype;
533
+ const setter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
534
+ const write = (next: string) => {
535
+ if (setter) setter.call(editable, next);
536
+ else editable.value = next;
537
+ };
538
+ editable.focus();
539
+ let held = editable.value;
540
+ if (replace && held !== '') {
541
+ // What select-all-and-delete produces, in one edit: a person's first
542
+ // keystroke over a selected field is a replacement, not N backspaces.
543
+ editable.select?.();
544
+ held = '';
545
+ write(held);
546
+ editable.dispatchEvent(
547
+ new InputEvent('input', {
548
+ bubbles: true,
549
+ composed: true,
550
+ inputType: 'deleteContentBackward',
551
+ }),
552
+ );
553
+ }
554
+ for (const character of [...text]) {
555
+ const init: KeyboardEventInit = {
556
+ key: character,
557
+ bubbles: true,
558
+ cancelable: true,
559
+ composed: true,
560
+ };
561
+ editable.dispatchEvent(new KeyboardEvent('keydown', init));
562
+ held += character;
563
+ write(held);
564
+ editable.dispatchEvent(
565
+ new InputEvent('input', {
566
+ bubbles: true,
567
+ composed: true,
568
+ inputType: 'insertText',
569
+ data: character,
570
+ }),
571
+ );
572
+ editable.dispatchEvent(new KeyboardEvent('keyup', init));
573
+ }
574
+ if (enter) {
575
+ const init: KeyboardEventInit = {
576
+ key: 'Enter',
577
+ code: 'Enter',
578
+ bubbles: true,
579
+ cancelable: true,
580
+ composed: true,
581
+ };
582
+ editable.dispatchEvent(new KeyboardEvent('keydown', init));
583
+ editable.dispatchEvent(new KeyboardEvent('keyup', init));
584
+ }
585
+ }
586
+
587
+ /**
588
+ * A synthetic pointer has no id the browser knows, so a listener that calls
589
+ * `setPointerCapture(event.pointerId)` — three's OrbitControls and
590
+ * TransformControls do, on every press — throws `NotFoundError` INTO THE
591
+ * CONSOLE whenever a document yields the press to the viewport (measured
592
+ * 2026-09-02: every missed click on an Asset Lab canvas). Listeners run
593
+ * synchronously inside `dispatchEvent`, so the capture calls are made
594
+ * harmless for exactly the dispatch and restored after; a real pointer is
595
+ * never affected.
596
+ */
597
+ function withoutPointerCapture(_element: HTMLElement, dispatch: () => void): void {
598
+ // On the PROTOTYPE, not the target: the viewport's controls listen on the
599
+ // canvas's container and capture THERE, so the event's whole bubble path
600
+ // has to be covered. Synchronous, restored in `finally`, and a real pointer
601
+ // (which never dispatches through here) is untouched.
602
+ const proto = Element.prototype;
603
+ const set = proto.setPointerCapture;
604
+ const release = proto.releasePointerCapture;
605
+ proto.setPointerCapture = () => {};
606
+ proto.releasePointerCapture = () => {};
607
+ try {
608
+ dispatch();
609
+ } finally {
610
+ proto.setPointerCapture = set;
611
+ proto.releasePointerCapture = release;
612
+ }
613
+ }
614
+
615
+ /** A REAL drag: press at `from`, move through `steps` points, release at `to`,
616
+ * all in the element's own box fractions — `dispatchClick`'s sequence with
617
+ * the moves a mouse makes between press and release. Moves carry
618
+ * `buttons: 1` (the primary button is held), the release `buttons: 0`. */
619
+ function dispatchDrag(
620
+ element: HTMLElement,
621
+ from: readonly [number, number],
622
+ to: readonly [number, number],
623
+ steps: number,
624
+ modifiers: { altKey?: boolean; ctrlKey?: boolean; metaKey?: boolean; shiftKey?: boolean } = {},
625
+ via: readonly (readonly [number, number])[] = [],
626
+ ): void {
627
+ const rect = element.getBoundingClientRect();
628
+ const at = (fx: number, fy: number): MouseEventInit => ({
629
+ bubbles: true,
630
+ cancelable: true,
631
+ composed: true,
632
+ view: window,
633
+ clientX: rect.x + rect.width * fx,
634
+ clientY: rect.y + rect.height * fy,
635
+ button: 0,
636
+ buttons: 1,
637
+ altKey: modifiers.altKey ?? false,
638
+ ctrlKey: modifiers.ctrlKey ?? false,
639
+ metaKey: modifiers.metaKey ?? false,
640
+ shiftKey: modifiers.shiftKey ?? false,
641
+ });
642
+ const start = at(from[0], from[1]);
643
+ withoutPointerCapture(element, () => {
644
+ // A REAL POINTER HOVERS BEFORE IT PRESSES, and some targets latch on the
645
+ // hover rather than on the press. three's `TransformControls` is the
646
+ // worked case: `pointerHover` is what sets `this.axis` from the picker
647
+ // under the cursor, and `pointerDown` does nothing at all while `axis` is
648
+ // null — so a synthetic drag that began with `pointerdown` handed the
649
+ // press to the VIEWPORT instead, which read it as a click on empty space
650
+ // and cleared the selection. Measured 2026-09-21 on a Blender Model
651
+ // document: dragging the X arrow of a freshly duplicated cube moved
652
+ // nothing and deselected it.
653
+ //
654
+ // Same family as the `button: -1` note below, and the same rule: mirror
655
+ // what the platform does. `buttons: 0` because nothing is held yet.
656
+ element.dispatchEvent(
657
+ new PointerEvent('pointermove', {
658
+ ...start,
659
+ button: -1,
660
+ buttons: 0,
661
+ pointerType: 'mouse',
662
+ }),
663
+ );
664
+ element.dispatchEvent(new MouseEvent('mousemove', { ...start, buttons: 0 }));
665
+ element.dispatchEvent(new PointerEvent('pointerdown', { ...start, pointerType: 'mouse' }));
666
+ element.dispatchEvent(new MouseEvent('mousedown', start));
667
+ element.focus();
668
+ const count = Math.max(1, Math.round(steps));
669
+ const path: (readonly [number, number])[] = [from, ...via, to];
670
+ for (let leg = 0; leg + 1 < path.length; leg++) {
671
+ const a = path[leg] as readonly [number, number];
672
+ const b = path[leg + 1] as readonly [number, number];
673
+ for (let i = 1; i <= count; i++) {
674
+ const t = i / count;
675
+ const move = at(a[0] + (b[0] - a[0]) * t, a[1] + (b[1] - a[1]) * t);
676
+ // `button: -1` ON A `pointermove`, because that is what a real mouse
677
+ // reports: `PointerEvent.button` names the button whose STATE CHANGED,
678
+ // and on a move none did. Held-ness lives in `buttons: 1` (kept by
679
+ // `at`). This is not a detail — three.js guards on it exactly:
680
+ // `TransformControls.pointerMove` returns at
681
+ // `pointer.button !== -1` (r180, :472), so a drag of a gizmo handle
682
+ // latched the axis, drew the drag helper line, and then moved NOTHING,
683
+ // forever (measured 2026-09-21 on a Blender Model document, where it
684
+ // read as "the transform provider is not writing"). `MouseEvent`'s
685
+ // legacy `mousemove` keeps `button: 0` — a real browser reports that
686
+ // one as 0, and mirroring the platform is the whole job here.
687
+ element.dispatchEvent(
688
+ new PointerEvent('pointermove', { ...move, button: -1, pointerType: 'mouse' }),
689
+ );
690
+ element.dispatchEvent(new MouseEvent('mousemove', move));
691
+ }
692
+ }
693
+ const end = { ...at(to[0], to[1]), buttons: 0 };
694
+ element.dispatchEvent(new PointerEvent('pointerup', { ...end, pointerType: 'mouse' }));
695
+ element.dispatchEvent(new MouseEvent('mouseup', end));
696
+ if (from[0] === to[0] && from[1] === to[1]) {
697
+ element.dispatchEvent(new MouseEvent('click', end));
698
+ }
699
+ });
700
+ }
701
+
702
+ /** The element a key goes to: the explicit target, else whatever inside the
703
+ * scope currently has focus, else the container itself. */
704
+ function keyTarget(scope: Scope, explicit: HTMLElement | null): HTMLElement {
705
+ if (explicit) return explicit;
706
+ const focused = document.activeElement;
707
+ if (focused instanceof HTMLElement && scopeRoots(scope).some((root) => root.contains(focused)))
708
+ return focused;
709
+ return scope.container;
710
+ }
711
+
712
+ /** Run a selector inside the scope, turning a malformed one into an honest error. */
713
+ function matchesIn(scope: Scope, selector: string): HTMLElement[] {
714
+ try {
715
+ return scopeRoots(scope).flatMap((root) =>
716
+ Array.from(root.querySelectorAll<HTMLElement>(selector)),
717
+ );
718
+ } catch (error) {
719
+ throw new Error(`Invalid selector ${JSON.stringify(selector)}: ${String(error)}`);
720
+ }
721
+ }
722
+
723
+ /** An optional `selector` resolves to a target; its absence means "the focused
724
+ * element inside the scope" — the shared shape of the `key` and `paste` steps. */
725
+ function gestureTarget(scope: Scope, step: { selector?: string; index?: number }): HTMLElement {
726
+ const explicit =
727
+ step.selector === undefined ? null : resolveTarget(scope, step.selector, step.index ?? 0);
728
+ return keyTarget(scope, explicit);
729
+ }
730
+
731
+ function keyInit(step: DocumentKeyStep): KeyboardEventInit {
732
+ return {
733
+ key: step.key,
734
+ code: step.code ?? step.key,
735
+ bubbles: true,
736
+ cancelable: true,
737
+ composed: true,
738
+ ...(step.ctrlKey ? { ctrlKey: true } : {}),
739
+ ...(step.metaKey ? { metaKey: true } : {}),
740
+ ...(step.shiftKey ? { shiftKey: true } : {}),
741
+ ...(step.altKey ? { altKey: true } : {}),
742
+ };
743
+ }
744
+
745
+ /**
746
+ * Set a `<select>`'s value the way React can see it.
747
+ *
748
+ * `HTMLSelectElement.prototype`'s own `value` setter is called explicitly
749
+ * because React installs a value tracker on the node: assigning through the
750
+ * instance updates that tracker too, so React compares the new value against
751
+ * itself, concludes nothing changed, and re-renders the OLD value. Going
752
+ * through the prototype descriptor leaves the tracker stale, which is exactly
753
+ * what makes the following `change` read as a real user edit.
754
+ */
755
+ function setSelectValue(element: HTMLSelectElement, value: string): void {
756
+ const options = [...element.options].map((option) => option.value);
757
+ if (!options.includes(value)) {
758
+ throw new Error(
759
+ `No option with value ${JSON.stringify(value)} on this <select> — it offers ` +
760
+ `${options.map((option) => JSON.stringify(option)).join(', ') || '(no options)'}. ` +
761
+ "The value is the option's `value`, not its label; `query` reports both.",
762
+ );
763
+ }
764
+ const setter = Object.getOwnPropertyDescriptor(HTMLSelectElement.prototype, 'value')?.set;
765
+ if (setter) setter.call(element, value);
766
+ else element.value = value;
767
+ element.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
768
+ element.dispatchEvent(new Event('change', { bubbles: true, composed: true }));
769
+ }
770
+
771
+ const PROBE_ACTIONS = ['query', 'click', 'drag', 'key', 'paste', 'select', 'type'] as const;
772
+
773
+ /**
774
+ * THE WIRE'S TWO FREE-FORM FIELDS, both refused by name.
775
+ *
776
+ * The wire hands the step in untyped (`cmd['step']`), so an unknown verb or an
777
+ * unknown scope must refuse HERE — an exhaustive switch alone would fall
778
+ * through and answer `ok` with nothing, and an unrecognized scope would
779
+ * quietly fall back to the document and answer about a surface the caller did
780
+ * not ask for. Both are the silent pass-through the strict-input rule bans.
781
+ */
782
+ function acceptStep(step: DocumentProbeStep): Scope {
783
+ const action = (step as { action?: unknown } | null | undefined)?.action;
784
+ if (!PROBE_ACTIONS.includes(action as (typeof PROBE_ACTIONS)[number])) {
785
+ throw new Error(
786
+ `Unknown document-probe action ${JSON.stringify(action)}. This door has exactly ` +
787
+ `${PROBE_ACTIONS.length} verbs: ${PROBE_ACTIONS.join(', ')} (\`editor.document.*\`).`,
788
+ );
789
+ }
790
+ const requested = (step as { scope?: unknown }).scope ?? 'document';
791
+ if (!SCOPE_NAMES.includes(requested as DocumentProbeScope)) {
792
+ throw new Error(
793
+ `Unknown scope ${JSON.stringify(requested)}. This door addresses exactly these ` +
794
+ `surfaces: ${SCOPE_NAMES.map((name) => `'${name}'`).join(', ')}.`,
795
+ );
796
+ }
797
+ return resolveScope(requested as DocumentProbeScope);
798
+ }
799
+
800
+ export async function runDocumentProbe(step: DocumentProbeStep): Promise<DocumentProbeResult> {
801
+ const scope = acceptStep(step);
802
+ const where = { name: scope.name, id: scope.id, title: scope.title };
803
+ /** Every gesture answers with the element it drove, so a transcript proves
804
+ * WHAT was driven and not merely that something was. */
805
+ const drove = (element: HTMLElement): DocumentProbeResult => ({
806
+ scope: where,
807
+ matched: 1,
808
+ elements: [describeElement(element, 0)],
809
+ });
810
+ switch (step.action) {
811
+ case 'query': {
812
+ const matches = matchesIn(scope, step.selector);
813
+ return {
814
+ scope: where,
815
+ matched: matches.length,
816
+ elements: matches
817
+ .slice(0, step.limit ?? 25)
818
+ .map((element, index) => describeElement(element, index, step.styles)),
819
+ };
820
+ }
821
+ case 'click': {
822
+ const element = resolveTarget(scope, step.selector, step.index ?? 0);
823
+ dispatchClick(element, step.clicks ?? 1);
824
+ return drove(element);
825
+ }
826
+ case 'type': {
827
+ const target = gestureTarget(scope, step);
828
+ typeInto(target, step.text, step.replace ?? true, step.enter ?? true);
829
+ return drove(target);
830
+ }
831
+ case 'drag': {
832
+ const element = resolveTarget(scope, step.selector, step.index ?? 0);
833
+ dispatchDrag(
834
+ element,
835
+ step.from,
836
+ step.to,
837
+ step.steps ?? 8,
838
+ {
839
+ ...(step.altKey === undefined ? {} : { altKey: step.altKey }),
840
+ ...(step.ctrlKey === undefined ? {} : { ctrlKey: step.ctrlKey }),
841
+ ...(step.metaKey === undefined ? {} : { metaKey: step.metaKey }),
842
+ ...(step.shiftKey === undefined ? {} : { shiftKey: step.shiftKey }),
843
+ },
844
+ step.via ?? [],
845
+ );
846
+ return drove(element);
847
+ }
848
+ case 'key': {
849
+ const target = gestureTarget(scope, step);
850
+ const init = keyInit(step);
851
+ target.dispatchEvent(new KeyboardEvent('keydown', init));
852
+ target.dispatchEvent(new KeyboardEvent('keyup', init));
853
+ return drove(target);
854
+ }
855
+ case 'select': {
856
+ const element = resolveTarget(scope, step.selector, step.index ?? 0);
857
+ if (!(element instanceof HTMLSelectElement)) {
858
+ throw new Error(
859
+ `${JSON.stringify(step.selector)} resolved a <${element.tagName.toLowerCase()}>, not a ` +
860
+ "<select>. This verb sets a dropdown's value; a button or a checkbox takes `click`.",
861
+ );
862
+ }
863
+ setSelectValue(element, step.value);
864
+ return drove(element);
865
+ }
866
+ case 'paste': {
867
+ const target = gestureTarget(scope, step);
868
+ const data = new DataTransfer();
869
+ data.setData('text/plain', step.text);
870
+ target.dispatchEvent(
871
+ new ClipboardEvent('paste', {
872
+ bubbles: true,
873
+ cancelable: true,
874
+ composed: true,
875
+ clipboardData: data,
876
+ }),
877
+ );
878
+ return drove(target);
879
+ }
880
+ }
881
+ }