@volter/sdk 0.0.0-stage → 0.5.203

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 (523) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +20 -0
  3. package/README.md +38 -3
  4. package/package.json +510 -4
  5. package/src/account.ts +210 -0
  6. package/src/chrome.ts +88 -0
  7. package/src/client.ts +1646 -0
  8. package/src/commands.ts +66 -0
  9. package/src/contributions.ts +619 -0
  10. package/src/css-numeric-style.ts +97 -0
  11. package/src/document-probe.ts +282 -0
  12. package/src/editor-view.ts +225 -0
  13. package/src/extension.ts +40 -0
  14. package/src/generations.ts +178 -0
  15. package/src/host.ts +1157 -0
  16. package/src/http-transport.browser.ts +14 -0
  17. package/src/http-transport.node.ts +19 -0
  18. package/src/index.ts +131 -0
  19. package/src/kit/CapabilityCoverageSection.tsx +185 -0
  20. package/src/kit/account-client.ts +333 -0
  21. package/src/kit/action-registry.ts +317 -0
  22. package/src/kit/active-product.ts +76 -0
  23. package/src/kit/active-project.ts +155 -0
  24. package/src/kit/adapter-editor-config.ts +25 -0
  25. package/src/kit/adapter-module.ts +7 -0
  26. package/src/kit/adapter-observation.ts +49 -0
  27. package/src/kit/animation/animation-clock.ts +479 -0
  28. package/src/kit/animation/stage-transport.ts +385 -0
  29. package/src/kit/api/assets.ts +365 -0
  30. package/src/kit/api/project-open.ts +355 -0
  31. package/src/kit/api/project-source.ts +180 -0
  32. package/src/kit/api/project-state.ts +110 -0
  33. package/src/kit/api/relay.ts +270 -0
  34. package/src/kit/api/themes.ts +45 -0
  35. package/src/kit/api-asset-library-wire.ts +45 -0
  36. package/src/kit/api-base.ts +10 -0
  37. package/src/kit/api-build.ts +99 -0
  38. package/src/kit/api-git-wire.ts +56 -0
  39. package/src/kit/api-logs.ts +92 -0
  40. package/src/kit/api-project-identity.ts +74 -0
  41. package/src/kit/api-settings.ts +36 -0
  42. package/src/kit/api-worktrees.ts +205 -0
  43. package/src/kit/asset-capabilities.ts +344 -0
  44. package/src/kit/asset-compare-core.ts +171 -0
  45. package/src/kit/asset-editor-context.tsx +101 -0
  46. package/src/kit/asset-events.ts +96 -0
  47. package/src/kit/asset-inspector-actions.ts +87 -0
  48. package/src/kit/asset-selection-viewer-registry.ts +113 -0
  49. package/src/kit/asset-selection.ts +146 -0
  50. package/src/kit/asset-thumbnails.ts +25 -0
  51. package/src/kit/asset-viewers.ts +115 -0
  52. package/src/kit/asset-workflow/asset-import-jobs.ts +106 -0
  53. package/src/kit/asset-workflow/asset-ledger-backend.ts +126 -0
  54. package/src/kit/asset-workflow/asset-ledger.ts +156 -0
  55. package/src/kit/asset-workflow/asset-materialization-report.ts +140 -0
  56. package/src/kit/asset-workflow/asset-pack-manifest.ts +320 -0
  57. package/src/kit/asset-workflow/asset-types.ts +142 -0
  58. package/src/kit/asset-workflow/audio-preview-player.ts +193 -0
  59. package/src/kit/asset-workflow/audio-waveform.ts +22 -0
  60. package/src/kit/asset-workflow/cloud-asset-client.ts +263 -0
  61. package/src/kit/asset-workflow/hosted-asset-materialization.ts +236 -0
  62. package/src/kit/asset-workflow/image-view-scale.ts +31 -0
  63. package/src/kit/asset-workflow/import-contract.ts +124 -0
  64. package/src/kit/asset-workflow/ledger-write-lock.ts +244 -0
  65. package/src/kit/asset-workflow/pixi-spritesheet.ts +197 -0
  66. package/src/kit/asset-workflow/preview-resource-lifetime.ts +44 -0
  67. package/src/kit/asset-workflow/project-asset-commands.ts +20 -0
  68. package/src/kit/asset-workflow/project-content.ts +288 -0
  69. package/src/kit/asset-workflow/project-source-index.ts +545 -0
  70. package/src/kit/asset-workflow/thumbnail-system.ts +256 -0
  71. package/src/kit/authoring/active-adapter.ts +200 -0
  72. package/src/kit/authoring/active-systems.ts +422 -0
  73. package/src/kit/authoring/adapter-key.ts +18 -0
  74. package/src/kit/authoring/authoring-asset-url.ts +27 -0
  75. package/src/kit/authoring/bootstrap-state.ts +49 -0
  76. package/src/kit/authoring/boundary-authoring-adapter.ts +189 -0
  77. package/src/kit/authoring/canvas-scene-guides.ts +84 -0
  78. package/src/kit/authoring/composite-authoring-adapter.ts +2109 -0
  79. package/src/kit/authoring/consumer-actions.ts +531 -0
  80. package/src/kit/authoring/design-time-layers.ts +852 -0
  81. package/src/kit/authoring/design-time-mount-registry.ts +244 -0
  82. package/src/kit/authoring/edit-mode-authoring.ts +637 -0
  83. package/src/kit/authoring/empty-project-authoring.ts +22 -0
  84. package/src/kit/authoring/instance-source-menu.ts +135 -0
  85. package/src/kit/authoring/layered-pick.ts +185 -0
  86. package/src/kit/authoring/mounted-root-subjects.ts +146 -0
  87. package/src/kit/authoring/no-authoring-adapter.ts +59 -0
  88. package/src/kit/authoring/object3d-document-persistence.ts +122 -0
  89. package/src/kit/authoring/panel-authoring.ts +121 -0
  90. package/src/kit/authoring/project-authoring-session.ts +105 -0
  91. package/src/kit/authoring/provenance.ts +99 -0
  92. package/src/kit/authoring/react-canvas-navigation.ts +259 -0
  93. package/src/kit/authoring/react-design-canvas-style.ts +20 -0
  94. package/src/kit/authoring/react-story-board.ts +917 -0
  95. package/src/kit/authoring/selection-scope.ts +195 -0
  96. package/src/kit/authoring/shell-document-ops.ts +169 -0
  97. package/src/kit/authoring/story-board-chrome-fit.ts +107 -0
  98. package/src/kit/authoring/story-board-presentation.ts +111 -0
  99. package/src/kit/authoring/three-root.ts +67 -0
  100. package/src/kit/authoring/viewport-tool-context.ts +73 -0
  101. package/src/kit/authoring/viewport-tool-owner.ts +38 -0
  102. package/src/kit/authoring/world-session-state.ts +101 -0
  103. package/src/kit/authoring-seam-evidence.ts +300 -0
  104. package/src/kit/availability-tick.ts +66 -0
  105. package/src/kit/bitmap-label.ts +120 -0
  106. package/src/kit/boot-routing.ts +392 -0
  107. package/src/kit/breakpoint-state.ts +43 -0
  108. package/src/kit/build-identity.ts +16 -0
  109. package/src/kit/bytes-codec.ts +62 -0
  110. package/src/kit/cancellation-reason.ts +58 -0
  111. package/src/kit/canvas-frames.ts +88 -0
  112. package/src/kit/capture-camera-pose.ts +77 -0
  113. package/src/kit/capture-size.ts +88 -0
  114. package/src/kit/chrome-registry.ts +159 -0
  115. package/src/kit/chrome-slot-registry.ts +91 -0
  116. package/src/kit/collaboration-client.ts +264 -0
  117. package/src/kit/collaboration-presence.ts +41 -0
  118. package/src/kit/command-dispatch.ts +19 -0
  119. package/src/kit/command-listener.ts +2182 -0
  120. package/src/kit/command-registry.ts +71 -0
  121. package/src/kit/component-board-registry.ts +205 -0
  122. package/src/kit/component-states-registry.ts +199 -0
  123. package/src/kit/components/AlignToolbar.tsx +204 -0
  124. package/src/kit/components/ApplicationMenus.tsx +372 -0
  125. package/src/kit/components/AssetEditorShell.tsx +216 -0
  126. package/src/kit/components/AssetInspectorToolSection.tsx +124 -0
  127. package/src/kit/components/BoardRulers.tsx +354 -0
  128. package/src/kit/components/CanvasAddNodeDialogs.tsx +529 -0
  129. package/src/kit/components/CanvasSceneViewport.tsx +1195 -0
  130. package/src/kit/components/ChromeSlot.tsx +20 -0
  131. package/src/kit/components/CodeView.tsx +470 -0
  132. package/src/kit/components/CompactInspectorShell.tsx +39 -0
  133. package/src/kit/components/ConsolePanel.tsx +273 -0
  134. package/src/kit/components/GameplaySessionTimeline.tsx +295 -0
  135. package/src/kit/components/InspectionProjection.tsx +932 -0
  136. package/src/kit/components/Inspector.tsx +270 -0
  137. package/src/kit/components/InspectorCanvasPreview.tsx +35 -0
  138. package/src/kit/components/InspectorFieldsSection.tsx +290 -0
  139. package/src/kit/components/InspectorStoriesSection.tsx +92 -0
  140. package/src/kit/components/InspectorToolSection.tsx +96 -0
  141. package/src/kit/components/InspectorTransformSection.tsx +245 -0
  142. package/src/kit/components/LightExplorerPanel.tsx +433 -0
  143. package/src/kit/components/MediaProperties.tsx +145 -0
  144. package/src/kit/components/ProjectHeader.tsx +328 -0
  145. package/src/kit/components/ReactCanvasControls.tsx +358 -0
  146. package/src/kit/components/RootSelectionOverlay.tsx +3688 -0
  147. package/src/kit/components/RootTextEditor.tsx +79 -0
  148. package/src/kit/components/SaveStatus.tsx +70 -0
  149. package/src/kit/components/SurfaceCrashBoundary.tsx +105 -0
  150. package/src/kit/components/SurfaceStateOverlay.tsx +24 -0
  151. package/src/kit/components/ToolContributionSurfaces.tsx +49 -0
  152. package/src/kit/components/ToolHost.tsx +380 -0
  153. package/src/kit/components/Toolbar.tsx +811 -0
  154. package/src/kit/components/TransientHint.tsx +44 -0
  155. package/src/kit/components/VersionControlSection.tsx +470 -0
  156. package/src/kit/components/ViewportOverlaysMenu.tsx +177 -0
  157. package/src/kit/components/VolterLogo.tsx +18 -0
  158. package/src/kit/components/WorktreeSwitcher.tsx +712 -0
  159. package/src/kit/components/account-documents.tsx +1162 -0
  160. package/src/kit/components/asset-documents.tsx +794 -0
  161. package/src/kit/components/asset-editor-persistence.ts +216 -0
  162. package/src/kit/components/asset-selection-section.tsx +545 -0
  163. package/src/kit/components/asset-thumbnails.tsx +307 -0
  164. package/src/kit/components/asset-viewers/AudioViewer.tsx +201 -0
  165. package/src/kit/components/asset-viewers/GenericJsonViewer.tsx +102 -0
  166. package/src/kit/components/asset-viewers/ImageViewer.tsx +300 -0
  167. package/src/kit/components/asset-viewers/JsonAssetDocument.tsx +98 -0
  168. package/src/kit/components/asset-viewers/OnlineAssetDetail.tsx +426 -0
  169. package/src/kit/components/asset-viewers/SourceAssetViewer.tsx +356 -0
  170. package/src/kit/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
  171. package/src/kit/components/asset-viewers/VideoViewer.tsx +101 -0
  172. package/src/kit/components/asset-viewers/shader-source.ts +144 -0
  173. package/src/kit/components/board-guides.ts +150 -0
  174. package/src/kit/components/canvas-scene-hotkeys.ts +37 -0
  175. package/src/kit/components/canvas-temporary-pivot.ts +34 -0
  176. package/src/kit/components/core-utilities.tsx +94 -0
  177. package/src/kit/components/inspector-preview-section.tsx +223 -0
  178. package/src/kit/components/inspector-revert-label.ts +20 -0
  179. package/src/kit/components/inspector-selection.ts +42 -0
  180. package/src/kit/components/inspector-stories-gating.ts +171 -0
  181. package/src/kit/components/inspector-transform-subject.ts +11 -0
  182. package/src/kit/components/inspector-transform.ts +88 -0
  183. package/src/kit/components/kind-documents.tsx +544 -0
  184. package/src/kit/components/primitives/DraftColorInput.tsx +74 -0
  185. package/src/kit/components/project-tool-documents.tsx +402 -0
  186. package/src/kit/components/scene-documents.tsx +221 -0
  187. package/src/kit/components/status-contributions.tsx +407 -0
  188. package/src/kit/components/tool-documents.tsx +302 -0
  189. package/src/kit/components/tool-schema-form.tsx +262 -0
  190. package/src/kit/components/use-after-paint.ts +41 -0
  191. package/src/kit/components/use-project-image-assets.ts +86 -0
  192. package/src/kit/components/workspace-history.ts +32 -0
  193. package/src/kit/components/world-documents.tsx +570 -0
  194. package/src/kit/components/world-overlay-gestures.ts +1939 -0
  195. package/src/kit/composite-screenshot.ts +2238 -0
  196. package/src/kit/content-entry-source-registry.ts +184 -0
  197. package/src/kit/contribution-surfaces.ts +48 -0
  198. package/src/kit/coverage/canvas-reveal.ts +192 -0
  199. package/src/kit/coverage/design-time-surfaces.ts +101 -0
  200. package/src/kit/coverage/ontology-invariants.ts +466 -0
  201. package/src/kit/coverage/session-vitals.ts +503 -0
  202. package/src/kit/crash-null-boundary.ts +36 -0
  203. package/src/kit/creation-site-edit.ts +1491 -0
  204. package/src/kit/creation-site-registry.ts +160 -0
  205. package/src/kit/delegate-harness-registry.ts +134 -0
  206. package/src/kit/document-areas.ts +70 -0
  207. package/src/kit/document-context-registry.ts +193 -0
  208. package/src/kit/document-open-registry.ts +200 -0
  209. package/src/kit/document-play-extension.ts +221 -0
  210. package/src/kit/document-preview-source.ts +20 -0
  211. package/src/kit/document-renderer-session.ts +138 -0
  212. package/src/kit/document-stage-sessions.ts +26 -0
  213. package/src/kit/document-viewports.ts +120 -0
  214. package/src/kit/editor-api.ts +46 -0
  215. package/src/kit/editor-chrome-capture.ts +136 -0
  216. package/src/kit/editor-commands.ts +176 -0
  217. package/src/kit/editor-console.ts +580 -0
  218. package/src/kit/editor-current-view.ts +56 -0
  219. package/src/kit/editor-document-probe.ts +1166 -0
  220. package/src/kit/editor-git-client.ts +115 -0
  221. package/src/kit/editor-hotkeys.ts +728 -0
  222. package/src/kit/editor-lease-view.ts +39 -0
  223. package/src/kit/editor-lease.ts +415 -0
  224. package/src/kit/editor-mode.ts +19 -0
  225. package/src/kit/editor-notifications.ts +140 -0
  226. package/src/kit/editor-presence.ts +563 -0
  227. package/src/kit/editor-presentation-activity.ts +58 -0
  228. package/src/kit/editor-presentation-notice.ts +42 -0
  229. package/src/kit/editor-runtime.tsx +147 -0
  230. package/src/kit/editor-server-response.ts +86 -0
  231. package/src/kit/editor-session-attribution.ts +85 -0
  232. package/src/kit/editor-session-mode.ts +54 -0
  233. package/src/kit/editor-state-facets.ts +74 -0
  234. package/src/kit/editor-view-presentation.ts +777 -0
  235. package/src/kit/environment-images.ts +58 -0
  236. package/src/kit/eyedropper-session.ts +60 -0
  237. package/src/kit/files/file-provider.ts +62 -0
  238. package/src/kit/files/project-files.ts +270 -0
  239. package/src/kit/finders/index.ts +137 -0
  240. package/src/kit/finders/scenes-from-entrypoint-selection.ts +387 -0
  241. package/src/kit/frame/frame-parts.ts +30 -0
  242. package/src/kit/framed-document-capture.ts +34 -0
  243. package/src/kit/game-globals-prelude.ts +143 -0
  244. package/src/kit/game-surface-defaults.ts +33 -0
  245. package/src/kit/gameplay-dom-recording.ts +318 -0
  246. package/src/kit/gameplay-export-state.ts +14 -0
  247. package/src/kit/gameplay-replay.ts +417 -0
  248. package/src/kit/gameplay-session-time.ts +9 -0
  249. package/src/kit/gameplay-sessions.ts +204 -0
  250. package/src/kit/hierarchy-component-marks.ts +298 -0
  251. package/src/kit/hierarchy-internals.ts +197 -0
  252. package/src/kit/hierarchy-kind-icon.ts +217 -0
  253. package/src/kit/hierarchy-menu-registry.ts +67 -0
  254. package/src/kit/hierarchy-node-rows.ts +307 -0
  255. package/src/kit/hierarchy-panel-view.ts +280 -0
  256. package/src/kit/hierarchy-projection.ts +76 -0
  257. package/src/kit/hierarchy-row-media.ts +45 -0
  258. package/src/kit/hierarchy-row-model.ts +308 -0
  259. package/src/kit/hierarchy-rows.ts +11 -0
  260. package/src/kit/hierarchy-walk.ts +86 -0
  261. package/src/kit/history/editor-session.ts +25 -0
  262. package/src/kit/history/history-commands.ts +147 -0
  263. package/src/kit/history/history-delegate.ts +187 -0
  264. package/src/kit/history/history-limit-notices.ts +43 -0
  265. package/src/kit/history/history-service.ts +1189 -0
  266. package/src/kit/history/persistence-coordinator.ts +35 -0
  267. package/src/kit/history/project-file-history.ts +386 -0
  268. package/src/kit/history/project-root-history-backends.ts +139 -0
  269. package/src/kit/history/resource-registry.ts +209 -0
  270. package/src/kit/history/snapshot-store.ts +103 -0
  271. package/src/kit/history/source-history-backend.ts +546 -0
  272. package/src/kit/history-types.ts +124 -0
  273. package/src/kit/hmr-registration-group.ts +67 -0
  274. package/src/kit/hmr-stable-react-context.ts +23 -0
  275. package/src/kit/hotkeys.ts +190 -0
  276. package/src/kit/inference-diagnostics.ts +69 -0
  277. package/src/kit/initial-project.ts +80 -0
  278. package/src/kit/inspection/active-subject.ts +571 -0
  279. package/src/kit/inspection/active-surface.ts +142 -0
  280. package/src/kit/inspection/compose-subject.ts +1055 -0
  281. package/src/kit/inspection/compose.ts +7 -0
  282. package/src/kit/inspection/display.ts +171 -0
  283. package/src/kit/inspection/document-subject.ts +109 -0
  284. package/src/kit/inspection/game-subject.ts +85 -0
  285. package/src/kit/inspection/null-subject.ts +119 -0
  286. package/src/kit/inspection/serialize.ts +357 -0
  287. package/src/kit/inspection/use-active-inspection.ts +180 -0
  288. package/src/kit/inspection-model.ts +542 -0
  289. package/src/kit/inspection-node-media.ts +58 -0
  290. package/src/kit/inspector-presentation.ts +203 -0
  291. package/src/kit/inspector-property-grouping.ts +64 -0
  292. package/src/kit/inspector-section-registry.ts +221 -0
  293. package/src/kit/instance-source-actions.ts +163 -0
  294. package/src/kit/js-heap.ts +71 -0
  295. package/src/kit/key-actions.ts +91 -0
  296. package/src/kit/keymap-presets.ts +428 -0
  297. package/src/kit/layout-policy.ts +31 -0
  298. package/src/kit/light-explorer-model.ts +134 -0
  299. package/src/kit/live-canvas-frame.ts +55 -0
  300. package/src/kit/live-document.ts +296 -0
  301. package/src/kit/live-gesture-lock.ts +50 -0
  302. package/src/kit/live-seam-evidence.ts +11 -0
  303. package/src/kit/live-session-registry.ts +220 -0
  304. package/src/kit/live-transition.ts +391 -0
  305. package/src/kit/manifest-project.ts +107 -0
  306. package/src/kit/module-fetch-diagnosis.ts +192 -0
  307. package/src/kit/mount-failure-report.ts +154 -0
  308. package/src/kit/native-selection-style.ts +497 -0
  309. package/src/kit/object3d-document-write-policy.ts +137 -0
  310. package/src/kit/packaged-runtime.ts +108 -0
  311. package/src/kit/palettes/maya.palette.json +57 -0
  312. package/src/kit/palettes/substance.palette.json +57 -0
  313. package/src/kit/performance-profiler.ts +367 -0
  314. package/src/kit/performance-sources.ts +69 -0
  315. package/src/kit/photograph-notice.ts +141 -0
  316. package/src/kit/play-boot-phase.ts +166 -0
  317. package/src/kit/play-camera-flight.ts +35 -0
  318. package/src/kit/png-encode.worker.ts +26 -0
  319. package/src/kit/presentation-surface.ts +248 -0
  320. package/src/kit/product-command.ts +90 -0
  321. package/src/kit/project-adapter.ts +1140 -0
  322. package/src/kit/project-asset-refresh.ts +23 -0
  323. package/src/kit/project-asset-roots.ts +68 -0
  324. package/src/kit/project-local-state.ts +151 -0
  325. package/src/kit/project-manager.ts +243 -0
  326. package/src/kit/project-module-changes.ts +201 -0
  327. package/src/kit/project-module-split.ts +270 -0
  328. package/src/kit/project-play-layers.ts +25 -0
  329. package/src/kit/project-provenance.ts +115 -0
  330. package/src/kit/project-ready.ts +42 -0
  331. package/src/kit/project-shape.ts +68 -0
  332. package/src/kit/project-tools.ts +107 -0
  333. package/src/kit/projection-types.ts +44 -0
  334. package/src/kit/readiness.ts +113 -0
  335. package/src/kit/renderer-resource-counts.ts +27 -0
  336. package/src/kit/reported-play-state.ts +90 -0
  337. package/src/kit/resolve-contributed-command.ts +14 -0
  338. package/src/kit/resolve-relative-specifier.ts +33 -0
  339. package/src/kit/retained-document-states.ts +91 -0
  340. package/src/kit/scene-document-plan.ts +320 -0
  341. package/src/kit/scene-live-open.ts +210 -0
  342. package/src/kit/scoped-game-css.ts +152 -0
  343. package/src/kit/served-url.ts +5 -0
  344. package/src/kit/session-close.ts +17 -0
  345. package/src/kit/session-tombstone.ts +127 -0
  346. package/src/kit/settings/settings-provider.ts +82 -0
  347. package/src/kit/settings-store.ts +348 -0
  348. package/src/kit/shell-document-state.ts +27 -0
  349. package/src/kit/shell-store-door.ts +45 -0
  350. package/src/kit/shell-store.ts +722 -0
  351. package/src/kit/source-conflict.ts +122 -0
  352. package/src/kit/stage-context.ts +377 -0
  353. package/src/kit/stage-invalidation.ts +25 -0
  354. package/src/kit/stage-store-registry.ts +69 -0
  355. package/src/kit/startup-failure.ts +80 -0
  356. package/src/kit/state-report-deferral.ts +73 -0
  357. package/src/kit/storage/host-files-storage.ts +97 -0
  358. package/src/kit/storage/http-storage.ts +174 -0
  359. package/src/kit/storage/index.ts +75 -0
  360. package/src/kit/storage/mem-storage.ts +158 -0
  361. package/src/kit/storage/path-lock.ts +44 -0
  362. package/src/kit/storage/paths.ts +26 -0
  363. package/src/kit/storage-types.ts +127 -0
  364. package/src/kit/stories/StoryPreviewMount.tsx +306 -0
  365. package/src/kit/stories/compose-project-stories.ts +255 -0
  366. package/src/kit/stories/prefabs-finder.ts +54 -0
  367. package/src/kit/stories/prefabs-from-stories.ts +182 -0
  368. package/src/kit/stories/project-story-regions.ts +24 -0
  369. package/src/kit/stories/story-capture.ts +579 -0
  370. package/src/kit/stories/story-declared-medium.ts +126 -0
  371. package/src/kit/stories/story-discovery.ts +176 -0
  372. package/src/kit/stories/story-dom-runtime.ts +78 -0
  373. package/src/kit/stories/story-grouping.ts +111 -0
  374. package/src/kit/stories/story-mount-turn.ts +27 -0
  375. package/src/kit/stories/story-presentation.ts +215 -0
  376. package/src/kit/stories/story-preview-component.ts +7 -0
  377. package/src/kit/stories/story-registry.ts +530 -0
  378. package/src/kit/stories-scope.ts +35 -0
  379. package/src/kit/story-document-openers.ts +36 -0
  380. package/src/kit/story-thumbnails.ts +47 -0
  381. package/src/kit/surface-keyboard.ts +101 -0
  382. package/src/kit/surface-state.ts +135 -0
  383. package/src/kit/system-seam-evidence.ts +72 -0
  384. package/src/kit/tab-census.ts +202 -0
  385. package/src/kit/tab-lifecycle-client.ts +227 -0
  386. package/src/kit/theme-library.ts +897 -0
  387. package/src/kit/theme-preference.ts +429 -0
  388. package/src/kit/three-viewport-presentation.ts +23 -0
  389. package/src/kit/tool-contribution-play.ts +74 -0
  390. package/src/kit/tool-loader.ts +1918 -0
  391. package/src/kit/transform-mode-request.ts +66 -0
  392. package/src/kit/transient-hint.ts +78 -0
  393. package/src/kit/transport-strip.tsx +174 -0
  394. package/src/kit/ui-source/adapter-region-includes.ts +238 -0
  395. package/src/kit/ui-source/file-region-resolver.ts +302 -0
  396. package/src/kit/ui-source/inspect.ts +775 -0
  397. package/src/kit/ui-source/source-write-backend.ts +605 -0
  398. package/src/kit/ui-source/tier-source-write-backend.ts +279 -0
  399. package/src/kit/user-local-state.ts +105 -0
  400. package/src/kit/viewport-activation-timings.ts +840 -0
  401. package/src/kit/viewport-editor-controls.ts +22 -0
  402. package/src/kit/viewport-presentation.ts +668 -0
  403. package/src/kit/viewport-surface-status.tsx +55 -0
  404. package/src/kit/wait-until.ts +37 -0
  405. package/src/kit/worker-call-metrics.ts +166 -0
  406. package/src/kit/workspace-areas.ts +191 -0
  407. package/src/kit/workspace-aux-commands.ts +11 -0
  408. package/src/kit/workspace-available-documents.ts +142 -0
  409. package/src/kit/workspace-core-utilities.ts +31 -0
  410. package/src/kit/workspace-document-ids.ts +59 -0
  411. package/src/kit/workspace-document-registry.ts +624 -0
  412. package/src/kit/workspace-document-restore.ts +146 -0
  413. package/src/kit/workspace-host-commands.ts +141 -0
  414. package/src/kit/workspace-persistence-gate.ts +40 -0
  415. package/src/kit/workspace-play-utilities.ts +44 -0
  416. package/src/kit/workspace-presets.ts +446 -0
  417. package/src/kit/workspace-regions.ts +276 -0
  418. package/src/kit/workspace-static-panels.ts +73 -0
  419. package/src/kit/workspace-status-registry.ts +121 -0
  420. package/src/kit/workspace-storage.ts +35 -0
  421. package/src/kit/workspace-style.ts +226 -0
  422. package/src/kit/workspace-utility-commands.ts +74 -0
  423. package/src/kit/workspace-utility-registry.ts +263 -0
  424. package/src/kit/world-adoption-event.ts +23 -0
  425. package/src/kit/world-adoption.ts +115 -0
  426. package/src/kit/world-canvas-viewport-state.ts +35 -0
  427. package/src/kit/world-document-routing.ts +104 -0
  428. package/src/kit/world-pan-state.ts +198 -0
  429. package/src/kit/write-pipe.ts +173 -0
  430. package/src/layout-arrangements.ts +5 -0
  431. package/src/layouts.tsx +108 -0
  432. package/src/looks.ts +16 -0
  433. package/src/project/output-roots.ts +73 -0
  434. package/src/project/tab-census.ts +155 -0
  435. package/src/project-tool-catalog.ts +104 -0
  436. package/src/selection.tsx +107 -0
  437. package/src/services.ts +18 -0
  438. package/src/session/build-report.ts +22 -0
  439. package/src/session/collaboration-types.ts +262 -0
  440. package/src/session/command-table.ts +327 -0
  441. package/src/session/discovery.ts +100 -0
  442. package/src/session/editor-brand.ts +48 -0
  443. package/src/session/editor-compatibility.ts +317 -0
  444. package/src/session/editor-control-lifecycle.ts +68 -0
  445. package/src/session/editor-control-protocol.ts +5 -0
  446. package/src/session/entrypoint-selection-readers.ts +66 -0
  447. package/src/session/entrypoint-selection-source.ts +120 -0
  448. package/src/session/game-css-scope.ts +30 -0
  449. package/src/session/hosted-attachment.ts +225 -0
  450. package/src/session/limited-view.ts +82 -0
  451. package/src/session/product-create.ts +24 -0
  452. package/src/session/product-locator.ts +478 -0
  453. package/src/session/project-module-url.ts +242 -0
  454. package/src/session/project-serving.ts +164 -0
  455. package/src/session/project-upgrade.ts +403 -0
  456. package/src/session/registry-format.ts +210 -0
  457. package/src/session/relative-path-guard.ts +56 -0
  458. package/src/session/scoped-game-css.ts +461 -0
  459. package/src/session/source-glob.ts +15 -0
  460. package/src/session/tool-contribution-convention.ts +123 -0
  461. package/src/session/workbench-locator.ts +712 -0
  462. package/src/session.ts +41 -0
  463. package/src/share.ts +160 -0
  464. package/src/source-analysis.ts +28 -0
  465. package/src/source-authoring.ts +439 -0
  466. package/src/tools/errors.ts +91 -0
  467. package/src/tools/provider-execution.ts +70 -0
  468. package/src/tools/registry.ts +341 -0
  469. package/src/tools/types.ts +159 -0
  470. package/src/transport.ts +100 -0
  471. package/src/types.ts +1693 -0
  472. package/src/views.ts +164 -0
  473. package/src/widgets/design-system.ts +93 -0
  474. package/src/widgets/editor-appearance.ts +151 -0
  475. package/src/widgets/editor-material.ts +83 -0
  476. package/src/widgets/icon-set-registry.ts +105 -0
  477. package/src/widgets/index.ts +71 -0
  478. package/src/widgets/inspector-widgets/AlignmentGrid.tsx +182 -0
  479. package/src/widgets/inspector-widgets/AssetSlotPicker.tsx +123 -0
  480. package/src/widgets/inspector-widgets/BorderEditor.tsx +309 -0
  481. package/src/widgets/inspector-widgets/ColorPicker.tsx +549 -0
  482. package/src/widgets/inspector-widgets/CurveEditor.tsx +359 -0
  483. package/src/widgets/inspector-widgets/FilterEditor.tsx +108 -0
  484. package/src/widgets/inspector-widgets/FontPicker.tsx +191 -0
  485. package/src/widgets/inspector-widgets/GradientEditor.tsx +623 -0
  486. package/src/widgets/inspector-widgets/ScrubbableInput.tsx +180 -0
  487. package/src/widgets/inspector-widgets/ShadowEditor.tsx +319 -0
  488. package/src/widgets/inspector-widgets/color-utils.ts +201 -0
  489. package/src/widgets/inspector-widgets/curve-utils.ts +212 -0
  490. package/src/widgets/inspector-widgets/index.ts +25 -0
  491. package/src/widgets/inspector-widgets/shared.tsx +140 -0
  492. package/src/widgets/interactive-edit-scope.ts +33 -0
  493. package/src/widgets/patterns/Dialog.tsx +140 -0
  494. package/src/widgets/patterns/Fields.tsx +44 -0
  495. package/src/widgets/patterns/List.tsx +25 -0
  496. package/src/widgets/patterns/StateSurface.tsx +40 -0
  497. package/src/widgets/patterns/Surfaces.tsx +122 -0
  498. package/src/widgets/patterns/Tabs.tsx +80 -0
  499. package/src/widgets/patterns/Toolbar.tsx +72 -0
  500. package/src/widgets/patterns/Tree.tsx +72 -0
  501. package/src/widgets/primitives/AnchoredMenu.tsx +260 -0
  502. package/src/widgets/primitives/Button.tsx +62 -0
  503. package/src/widgets/primitives/ColorInput.tsx +78 -0
  504. package/src/widgets/primitives/DraftTextInput.tsx +63 -0
  505. package/src/widgets/primitives/EditorIcon.tsx +157 -0
  506. package/src/widgets/primitives/FormControls.tsx +88 -0
  507. package/src/widgets/primitives/HoverPreview.tsx +96 -0
  508. package/src/widgets/primitives/JsonInput.tsx +113 -0
  509. package/src/widgets/primitives/Layout.tsx +100 -0
  510. package/src/widgets/primitives/Menu.tsx +161 -0
  511. package/src/widgets/primitives/NumberInput.tsx +169 -0
  512. package/src/widgets/primitives/Panel.tsx +80 -0
  513. package/src/widgets/primitives/SectionHeader.tsx +77 -0
  514. package/src/widgets/primitives/Text.tsx +54 -0
  515. package/src/widgets/primitives/ThemeRootPortal.tsx +52 -0
  516. package/src/widgets/primitives/Tooltip.tsx +204 -0
  517. package/src/widgets/primitives/Vec3Input.tsx +70 -0
  518. package/src/widgets/primitives/banner-tones.ts +32 -0
  519. package/src/widgets/primitives/clamp-to-viewport.ts +44 -0
  520. package/src/widgets/primitives/editor-icons.ts +254 -0
  521. package/src/widgets/primitives/panel-header-styles.ts +42 -0
  522. package/src/widgets/theme.ts +2841 -0
  523. package/src/widgets/z-index.ts +25 -0
package/src/types.ts ADDED
@@ -0,0 +1,1693 @@
1
+ /**
2
+ * The two viewport tabs — the editor's two MODES, named for what the user is
3
+ * doing rather than for what happens to be mounted (ARCHITECTURE-CORE
4
+ * §Vocabulary, decided 2026-08-02). Deliberately not `'scene' | 'game'`:
5
+ * `'scene'` names a document format (a three root is TSX, and
6
+ * asset/story/tool documents live on that tab too), and `'game'`
7
+ * names the mounted artifact rather than the mode. Not to be confused with the
8
+ * PLAY TRANSPORT (the five stable controls) — this is which tab is showing.
9
+ */
10
+ export type ViewportTab = 'edit' | 'play';
11
+
12
+ export type AssetKind =
13
+ | 'model'
14
+ | 'image'
15
+ | 'video'
16
+ | 'audio'
17
+ | 'animation'
18
+ | 'json'
19
+ | 'prefab'
20
+ | 'source';
21
+
22
+ /**
23
+ * The editor's NAMED WORKSPACES — task-named layout memories over the one
24
+ * physical dock (ARCHITECTURE-CORE §Editor chrome). Named for the TASK, each
25
+ * borrowing the arrangement of the tool that does it best; `game` is the
26
+ * default and is the editor's standing arrangement. Restated here (rather than
27
+ * imported from the editor) for the same reason every other id union in this
28
+ * file is: the SDK is a wire client and must not depend on the editor bundle.
29
+ */
30
+ /** One entry of the resolved document table, as `EditorState['adapter']['scenes']`
31
+ * and `documentTable()` carry it over the wire. */
32
+ export interface DocumentTableEntryProjection {
33
+ id: string;
34
+ label: string;
35
+ /** OPEN: `scene`, `prefab`, `page`, `model`, `shot`, `take`, … */
36
+ kind: string;
37
+ region: string | null;
38
+ authorable: boolean;
39
+ reach: { kind: string; [field: string]: unknown };
40
+ source?: { path: string; export?: string };
41
+ finder?: string;
42
+ }
43
+
44
+ export interface DocumentTableProjection {
45
+ default: string | null;
46
+ entries: DocumentTableEntryProjection[];
47
+ /** True while a declared finder has no registration yet — a contribution's
48
+ * finder before the contribution pass — so the table is NOT final. */
49
+ pending: boolean;
50
+ }
51
+
52
+ /** A registered workspace's id: one of the editor's own (`look`, `animate`,
53
+ * `design`) or one a declared package contributes (`game`, `model`,
54
+ * `sculpt`, `texture`, `stage`, …). Validated by the editor against its
55
+ * live registry, the way a utility id is — not a closed set here. */
56
+ export type EditorWorkspaceName = string;
57
+
58
+ export interface HelperVisibility {
59
+ bounds: boolean;
60
+ lights: boolean;
61
+ cameras: boolean;
62
+ colliders: boolean;
63
+ joints: boolean;
64
+ particles: boolean;
65
+ lod: boolean;
66
+ audio: boolean;
67
+ splines: boolean;
68
+ navmesh: boolean;
69
+ constraints: boolean;
70
+ reflectionProbes: boolean;
71
+ triggerVolumes: boolean;
72
+ skeletons: boolean;
73
+ /**
74
+ * The WEIGHT display — a mesh coloured by its active vertex group
75
+ * (`@volter/editor-blender`'s `blender-runtime-weights.ts`). Added 2026-09-19 (I4)
76
+ * because nothing in this set stood for it: `skeletons` is the bones, and
77
+ * Blender's own viewport overlay has a Bones checkbox but reaches weight
78
+ * colours through Weight Paint MODE, which an inspection surface has no
79
+ * brushes to enter. OFF by default, the way Blender shows no weights until
80
+ * you ask for them.
81
+ */
82
+ weights: boolean;
83
+ /**
84
+ * Blender's 3D CURSOR (`View3DOverlay.show_cursor`, the overlay popover's
85
+ * "3D Cursor" checkbox): the point `Scene.cursor` names, where the "to 3D
86
+ * Cursor" operators place and snap. ON by default, as Blender draws it.
87
+ */
88
+ cursor: boolean;
89
+ /** EMPTIES — a scene's objects that are only a place (Blender's empties, drawn by its
90
+ * overlay's extras as axes, arrows or a shape). ON by default, as Blender draws them. */
91
+ empties: boolean;
92
+ }
93
+
94
+ export interface Vec3Value {
95
+ x: number;
96
+ y: number;
97
+ z: number;
98
+ }
99
+
100
+ export interface EditorCameraState {
101
+ position: Vec3Value;
102
+ target: Vec3Value;
103
+ fov?: number;
104
+ }
105
+
106
+ export interface EditorEntitySummary {
107
+ id: string;
108
+ name: string;
109
+ childIds: string[];
110
+ }
111
+
112
+ export interface ViewportCapture {
113
+ base64: string;
114
+ mimeType: 'image/png';
115
+ }
116
+
117
+ /**
118
+ * How an animated look move on the open Object3D document ended. Never an
119
+ * error: the camera is SHARED with the person watching, so "they grabbed it
120
+ * mid-orbit" is an outcome to read, not a failure to handle.
121
+ */
122
+ export interface DocumentLookOutcome {
123
+ /** True only when the whole move was drawn. */
124
+ completed: boolean;
125
+ /** Why it stopped early: a human drag, a later look verb, or a closed document. */
126
+ cancelledBy?: 'human' | 'superseded' | 'closed';
127
+ /** Where the camera ended up, radians around the framed subject. */
128
+ azimuth: number;
129
+ /** Radians above the subject's horizon. */
130
+ elevation: number;
131
+ /** Seconds of the move that were actually drawn. */
132
+ seconds: number;
133
+ }
134
+
135
+ /**
136
+ * What a frame of a 3D document's stage costs when nothing caps it (`document-frame-cost`): the
137
+ * stage's own frame run back to back, each waited out on the GPU, the first one warming.
138
+ */
139
+ export interface StageFrameCostReading {
140
+ /** The stage measured, by the document id it draws. */
141
+ readonly stage: string;
142
+ /** Frames measured, after one that warms. */
143
+ readonly frames: number;
144
+ /** Full stationary drawing, or a small temporary camera turn with the
145
+ * stage's navigation presentation. The original camera is restored. */
146
+ readonly quality?: 'full' | 'navigation';
147
+ readonly medianMs: number;
148
+ readonly p95Ms: number;
149
+ readonly minMs: number;
150
+ /** Frame submission, including driver backpressure; not pure CPU time. */
151
+ readonly submitMedianMs?: number;
152
+ /** Remaining GPU completion wait after submission (one-pixel readback). */
153
+ readonly completionWaitMedianMs?: number;
154
+ /** The canvas as drawn: device pixels, its CSS size and the ratio between them. */
155
+ readonly width: number;
156
+ readonly height: number;
157
+ readonly cssWidth: number;
158
+ readonly cssHeight: number;
159
+ readonly devicePixelRatio: number;
160
+ /** What the last frame DREW, after culling, every pass counted (`renderer.info.render`)... */
161
+ readonly drawn: { readonly calls: number; readonly triangles: number };
162
+ /** ...and what the scene holds visible, drawn or not. */
163
+ readonly scene: { readonly meshes: number; readonly triangles: number };
164
+ /** The view's draw mode (`solid`, `rendered`, …). */
165
+ readonly drawMode: string;
166
+ }
167
+
168
+ /**
169
+ * One entry of Model Play's log (`model-play-log`, written by a play script's `play.log` and by
170
+ * the runner; `@volter/play`'s `play-log.ts` is the writer and documents it).
171
+ */
172
+ export interface ModelPlayLogEntry {
173
+ /** The entry's place in its run, from 0; a gap is entries the bounded log dropped. */
174
+ readonly seq: number;
175
+ /** Wall-clock ms. */
176
+ readonly t: number;
177
+ /** Seconds of simulation since Play started. */
178
+ readonly simT: number;
179
+ /** The update the entry was written in (the first is 1); 0 before the first. */
180
+ readonly tick: number;
181
+ readonly kind: string;
182
+ /** `script` for the play script's entries; `play` for `play-start`, `script-reload`,
183
+ * `script-error` and `play-stop`. */
184
+ readonly source: 'script' | 'play';
185
+ readonly facts?: Record<string, unknown>;
186
+ }
187
+
188
+ /** One model document's Model Play log as `model-play-log` reads it: the current run's, or
189
+ * the last one's. */
190
+ export interface ModelPlayLogReading {
191
+ readonly playing: boolean;
192
+ readonly documentId: string | null;
193
+ /** Every model document with a log; a read names one, or takes the active Play's. */
194
+ readonly documents: readonly string[];
195
+ readonly script: string | null;
196
+ readonly startedAt: number | null;
197
+ /** The run's clock at the read. */
198
+ readonly simT: number;
199
+ readonly tick: number;
200
+ /** Entries the log keeps; `dropped` of the run's `total` fell off its front. */
201
+ readonly capacity: number;
202
+ readonly total: number;
203
+ readonly dropped: number;
204
+ readonly entries: readonly ModelPlayLogEntry[];
205
+ }
206
+
207
+ /** Where the open document's camera is standing and what it is aimed at. */
208
+ export interface DocumentCameraPose {
209
+ position: [number, number, number];
210
+ target: [number, number, number];
211
+ }
212
+
213
+ /**
214
+ * Unit 4 (live-front-door wave) — the RUNNING GAME's pixels, as captured by
215
+ * the `bridge-screenshot` relay op (`command-listener.ts`'s
216
+ * `handleBridgeScreenshot`): the full play-mode game stack — canvas(es) PLUS
217
+ * the DOM adapter layers (for example React roots) composited by
218
+ * `capturePlayComposite`. Distinct from {@link ViewportCapture}, which is the
219
+ * EDITOR viewport's own canvas (`capture-viewport`) and knows nothing about
220
+ * play mode or the HUD.
221
+ *
222
+ * `composite` reports honestly whether the DOM-layer composite leg actually
223
+ * ran, or whether the capture degraded to a canvas-only `toDataURL` frame (no
224
+ * container, no DOM `Image`/`XMLSerializer`, or a rasterization failure) —
225
+ * never a silently HUD-less image passed off as the whole game. `layers` is
226
+ * only present on the composite leg.
227
+ *
228
+ * `flatness` and `loopRecoveryFrame` are the same honesty contract applied to
229
+ * the PIXELS: how much of the frame is one flat surface (a near-blank capture
230
+ * carries its own "weak evidence" warning), and whether the frame exists only
231
+ * because capture recovered a starved host loop with one deterministic tick.
232
+ */
233
+ export interface GameCapture {
234
+ base64: string;
235
+ mimeType: 'image/png';
236
+ composite: boolean;
237
+ layers?: { canvases: number; domOverlays: number };
238
+ /** Degeneracy measure — `packages/sdk/src/kit/composite-screenshot.ts`'s
239
+ * `measureFlatness`. Absent when pixel readback was unavailable. */
240
+ flatness?: {
241
+ dominantFraction: number;
242
+ dominantColor: string;
243
+ distinctRegions: number;
244
+ degenerate: boolean;
245
+ /** Present iff `degenerate`; the sentence to show the caller verbatim. */
246
+ warning?: string;
247
+ };
248
+ /** True when the host loop was starved and the runtime rendered one
249
+ * deterministic tick to produce this frame. */
250
+ loopRecoveryFrame?: boolean;
251
+ /** Present when this frame came out of a RECORDED run (every the editor's `play` command
252
+ * records). The still is delivered either way; `notice` is the sentence
253
+ * naming the clip, its offset-0 wall clock, and what a still cannot answer —
254
+ * shown verbatim, never re-derived by the caller. */
255
+ recording?: {
256
+ path: string;
257
+ startedAt: string;
258
+ notice: string;
259
+ };
260
+ }
261
+
262
+ /** Options for recording the clean running-game composite. */
263
+ export interface GameplayRecordingOptions {
264
+ /** Requested real-time capture cadence. Defaults to 30; range 1–60. */
265
+ fps?: number;
266
+ /** `composite-webm` is the compatible one-file artifact. `canvas-dom`
267
+ * records the world canvas directly and writes the HUD to a synchronized
268
+ * replay sidecar, avoiding live DOM rasterization. */
269
+ format?: 'composite-webm' | 'canvas-dom';
270
+ /** Names the file under `.volter/recordings/`. During Play, absence names the
271
+ * clip after its durable Gameplay Session. */
272
+ name?: string | null;
273
+ }
274
+
275
+ /** Facts fixed when a gameplay recording starts. */
276
+ export interface GameplayRecordingStarted {
277
+ format: 'composite-webm' | 'canvas-dom';
278
+ startedAt: string;
279
+ mimeType: string;
280
+ width: number;
281
+ height: number;
282
+ fps: number;
283
+ audio: boolean;
284
+ layers: { canvases: number; domOverlays: number };
285
+ /** Where the bytes are landing — known at START, so a caller can say where
286
+ * the evidence for a run still in progress will be. */
287
+ path: string;
288
+ /** Replay sidecar directory for `canvas-dom`; null for a composite. */
289
+ replayPath: string | null;
290
+ /** True only for the out-of-session rotating fallback. */
291
+ rotates: boolean;
292
+ /** This run's `logs/play-*.jsonl`, whose event timestamps map to clip offsets. */
293
+ logFile: string | null;
294
+ }
295
+
296
+ /** Final browser recording. Media chunks stream directly to the project while
297
+ * recording, so the command result stays small even for a long playthrough. */
298
+ export interface GameplayRecordingCapture extends GameplayRecordingStarted {
299
+ durationMs: number;
300
+ droppedFrames: number;
301
+ frameErrors: number;
302
+ /** The cadence ACHIEVED (frames over wall duration), which diverges from the
303
+ * requested `fps` on a throttled hidden tab. Read this before reasoning
304
+ * about a clip's timing. */
305
+ effectiveFps: number;
306
+ /** True when the tab was hidden for any part of the recording. */
307
+ hidden: boolean;
308
+ }
309
+
310
+ /** A position on the active recorder's authoritative monotonic timeline.
311
+ * `startedAt` identifies the recording; `elapsedMs` shares the exact origin
312
+ * used to calculate the finalized capture's `durationMs`. */
313
+ export interface GameplayRecordingTimeline {
314
+ startedAt: string;
315
+ elapsedMs: number;
316
+ }
317
+
318
+ /** A PNG reconstructed on demand from canvas video plus its DOM timeline. */
319
+ export interface GameplayReplayCapture {
320
+ replayPath: string;
321
+ positionMs: number;
322
+ width: number;
323
+ height: number;
324
+ base64: string;
325
+ mimeType: 'image/png';
326
+ layers: { canvases: number; domOverlays: number };
327
+ flatness?: GameCapture['flatness'];
328
+ }
329
+
330
+ /** What Play can report while its recording is still open. Geometry, layers, media cadence, and
331
+ * final health are deliberately absent: those facts are only authoritative after Stop finalizes
332
+ * the capture. */
333
+ export interface PlayRecordingStatus {
334
+ path: string;
335
+ format: 'composite-webm' | 'canvas-dom';
336
+ replayPath: string | null;
337
+ startedAt: string;
338
+ rotates: boolean;
339
+ logFile: string | null;
340
+ idleAutoStopMs: number;
341
+ }
342
+
343
+ /** What `play` reports back once the run is up and recording. */
344
+ export interface PlayStarted {
345
+ /** Absent only when the recorder could not start; play is up either way and
346
+ * the editor console carries the reason. */
347
+ recording?: PlayRecordingStatus;
348
+ }
349
+
350
+ export type AssetPreviewView = 'front' | 'right' | 'top' | 'perspective';
351
+ export type AssetPreviewBackground = 'neutral' | 'transparent';
352
+
353
+ /**
354
+ * What the Asset Lab is being asked to photograph: a same-origin project
355
+ * model path, a live scene entity, or RAW GLB BYTES that travel with the
356
+ * command.
357
+ *
358
+ * The bytes form exists because rasterization only happens where there is a
359
+ * GPU. A Node host that built a model in memory — the module-look lane's
360
+ * `project.bake.preview`, which compiles a project TS module's `Object3D` and
361
+ * exports it to an in-memory GLB without writing anything — has pixels
362
+ * nowhere until the live editor session renders them. It is four-view,
363
+ * lab-stage only: bytes stand nowhere, so `stage: 'scene'`, the shot-set /
364
+ * source-review modes and compare are all refused by name at the relay.
365
+ */
366
+ export type AssetPreviewSource =
367
+ | { assetPath: string; entityId?: never; glbBase64?: never }
368
+ | { entityId: string; assetPath?: never; glbBase64?: never }
369
+ | { glbBase64: string; assetPath?: never; entityId?: never };
370
+
371
+ /**
372
+ * Where an ENTITY capture is staged.
373
+ *
374
+ * `'lab'` (the default on every surface) is the editor's neutral Asset Lab
375
+ * stage: an isolated, yaw-normalized snapshot under fixed studio lighting,
376
+ * identical whatever the entity's surroundings are. `'scene'` photographs the
377
+ * entity where it stands in the live scene, under the scene's own lighting,
378
+ * with the editor's own grid/gizmos/helpers excluded — see
379
+ * `packages/editor-threejs/src/kit/asset-preview.ts`'s `captureSceneStageAssetPreview`.
380
+ *
381
+ * `'scene'` is an ENTITY-only, four-view option: the relay refuses it by name
382
+ * for an `assetPath` source (a model loaded from disk stands nowhere) and for
383
+ * the shot-set / source-review / compare modes (each stages its own subject).
384
+ */
385
+ export type AssetPreviewStage = 'lab' | 'scene';
386
+
387
+ /**
388
+ * A free capture camera for the Asset Lab legs (the editor's `screenshot` command's
389
+ * `--azimuth/--elevation/--distance`): ONE view from a chosen angle instead
390
+ * of the fixed four. Angles are relative to the subject's AUTHORED front —
391
+ * azimuth 0 photographs the declared front, 90 walks toward the side the
392
+ * turntable's yaw-90 shot shows; elevation raises the camera (degrees above
393
+ * level); `distance` is meters from the framing center, auto-fit when
394
+ * omitted.
395
+ */
396
+ export interface AssetPreviewCameraChoice {
397
+ azimuthDegrees: number;
398
+ elevationDegrees: number;
399
+ distance?: number;
400
+ }
401
+
402
+ /**
403
+ * Pose an animated subject before capturing (the editor's `screenshot` command's
404
+ * `--clip <name> --time <t>`): the named clip is sampled at `timeSeconds`
405
+ * on the capture's disposable snapshot — the source is never mutated. The
406
+ * capture fails loudly (naming the clips that DO exist) when the subject
407
+ * carries no clip by this name.
408
+ */
409
+ export interface AssetPreviewPose {
410
+ clip: string;
411
+ timeSeconds: number;
412
+ }
413
+
414
+ export interface AssetPreviewOptions {
415
+ width?: number;
416
+ height?: number;
417
+ background?: AssetPreviewBackground;
418
+ stage?: AssetPreviewStage;
419
+ camera?: AssetPreviewCameraChoice;
420
+ pose?: AssetPreviewPose;
421
+ }
422
+
423
+ /**
424
+ * How the captured subject was oriented relative to its AUTHORED coordinates.
425
+ * The editor yaw-normalizes a subject so the front camera photographs its
426
+ * declared front; a 180-degree yaw maps authored +X to screen-LEFT in the
427
+ * front view. The yaw is reported here (and stamped onto the images' own
428
+ * pixels as `+X>` / `<+X` markers) so it is never applied silently.
429
+ */
430
+ export interface AssetPreviewOrientation {
431
+ /** The subject's declared forward, `[0,0,1]` when it declares none. */
432
+ forward: [number, number, number];
433
+ /** Yaw applied to face the front camera; 0 means authored axes = world axes. */
434
+ yawDegrees: number;
435
+ }
436
+
437
+ export interface AssetPreviewCapture {
438
+ width: number;
439
+ height: number;
440
+ /** Absent from editors that predate orientation reporting. */
441
+ orientation?: AssetPreviewOrientation;
442
+ views: Array<ViewportCapture & { view: AssetPreviewView }>;
443
+ contactSheet: ViewportCapture & { width: number; height: number };
444
+ }
445
+
446
+ /**
447
+ * The STORY lane (the editor's `screenshot <file>.stories.tsx` command): a project CSF file's
448
+ * exports rendered in the live session's DOM and captured through the same
449
+ * composite leg the game lane uses, as ONE variant sheet per file. `story`
450
+ * narrows to a single export. See
451
+ * `packages/sdk/src/kit/stories/story-capture.ts`.
452
+ */
453
+ export interface StoryCaptureOptions {
454
+ /** Narrow the sheet to one CSF export name (`--story <export>`). */
455
+ story?: string;
456
+ /** Per-variant cell size in CSS pixels; defaults to a 960x540 rectangle,
457
+ * because a UI story is not the Asset Lab's square 3D view. */
458
+ width?: number;
459
+ height?: number;
460
+ /** Free capture camera for THREE stories (same contract as the Asset Lab's
461
+ * {@link AssetPreviewCameraChoice}). A selected story that renders on the
462
+ * DOM leg refuses these BY NAME rather than silently ignoring them. */
463
+ camera?: AssetPreviewCameraChoice;
464
+ /** Clip pose for THREE stories (same contract as the Asset Lab's
465
+ * {@link AssetPreviewPose}); DOM-leg stories refuse it by name. */
466
+ pose?: AssetPreviewPose;
467
+ }
468
+
469
+ export interface StoryVariantImage extends ViewportCapture {
470
+ /** The CSF export name — the file name each variant PNG is written under. */
471
+ name: string;
472
+ /** Storybook's human-facing story name. */
473
+ label: string;
474
+ }
475
+
476
+ export interface StoryVariantCapture {
477
+ /** Project-relative path of the CSF module that was photographed. */
478
+ modulePath: string;
479
+ width: number;
480
+ height: number;
481
+ variants: StoryVariantImage[];
482
+ contactSheet: ViewportCapture & { width: number; height: number };
483
+ }
484
+
485
+ /**
486
+ * B8.4 — the Asset Lab compare mode (the editor's `screenshot <model.glb>
487
+ * --compare <ref.glb>` command): the asset and a caller-supplied reference GLB rendered with
488
+ * matched orthographic front + side framing (equal-height bounding-box
489
+ * normalization, both yaw-normalized to face the camera), scored by
490
+ * silhouette IoU with per-view overlay evidence (orange asset / cyan
491
+ * reference / near-white agreement). See
492
+ * `packages/editor-threejs/src/kit/asset-compare.ts`.
493
+ */
494
+ export type AssetCompareView = 'front' | 'side';
495
+
496
+ export interface AssetCompareOptions {
497
+ width?: number;
498
+ height?: number;
499
+ /** Override the reference GLB's ground-plane forward vector; defaults to
500
+ * its `userData.forward` extras when present, else glTF's +Z. */
501
+ refForward?: [number, number, number];
502
+ }
503
+
504
+ export interface AssetCompareCapture {
505
+ width: number;
506
+ height: number;
507
+ views: Array<{
508
+ view: AssetCompareView;
509
+ /** Silhouette intersection-over-union in [0, 1]. */
510
+ iou: number;
511
+ overlay: ViewportCapture;
512
+ asset: ViewportCapture;
513
+ ref: ViewportCapture;
514
+ }>;
515
+ }
516
+
517
+ /**
518
+ * THE shot-set contract. This block is the ONE declaration of it.
519
+ *
520
+ * A project-defined labeled shot set (the editor's `screenshot <target> --shots <set>` command).
521
+ * The DEFINITION is project data: a registered project tool named
522
+ * `project.<set>.previewShots` returns it (installed capabilities register
523
+ * theirs — the bird, humanoid and walking-castle capabilities each contribute
524
+ * their canonical verify set), and the editor's generic capture engine renders
525
+ * it — turntable yaw angles and skeleton-anchored zoom crops, optionally under
526
+ * a named pose applied to a disposable snapshot. Every shot is labeled so a
527
+ * flat directory of PNGs is self-describing without a manifest file.
528
+ *
529
+ * The contract lives HERE, in the SDK, because it crosses the editor relay:
530
+ * the capability tool that authors a set, the CLI that ships it across, and
531
+ * `packages/editor-threejs/src/kit/asset-preview.ts`'s capture engine that renders it are
532
+ * three different programs. Each of those used to declare its own copy — five
533
+ * declarations in total — and the copies had already drifted on what a pose
534
+ * step's `radians` is measured FROM. Every side now type-checks against this
535
+ * one: the capture engine annotates its parser's return with
536
+ * `AssetPreviewShotSetDefinition`, and each capability tool annotates its
537
+ * exported set with it, so a Zod schema that stops matching this shape is a
538
+ * compile error rather than a runtime surprise at the relay.
539
+ */
540
+ export interface ShotSetPoseRotation {
541
+ bone: string;
542
+ axis: 'x' | 'y' | 'z';
543
+ /**
544
+ * A DELTA about the bone's own local axis, in radians, relative to the
545
+ * loaded GLB's baked rest pose — NOT an absolute local rotation.
546
+ *
547
+ * That is what the capture engine does with it: `bone.rotateX/Y/Z(radians)`,
548
+ * which composes onto the bone's existing local quaternion. The consequence
549
+ * to watch for is that "rest pose" means whatever the GLB baked, not
550
+ * identity — a rig baked mid-gait needs `pose(t) - pose(0)` here, while a
551
+ * rig baked at identity can pass its absolute angle unchanged. Getting that
552
+ * backwards silently double-applies the baked pose.
553
+ */
554
+ radians: number;
555
+ }
556
+
557
+ /** The other half of a pose: a named morph target driven to an influence.
558
+ * Morphs are how a rig expresses what a joint cannot — an eyelid sliding over
559
+ * an eyeball, a brow band tilting — so a set that poses a FACE needs both
560
+ * halves or it can only ever show the jaw. A rig whose meshes carry no such
561
+ * morph is posed by the rotations alone: the same degrade-don't-throw rule
562
+ * the rotations follow for a missing joint. */
563
+ export interface ShotSetPoseMorph {
564
+ morph: string;
565
+ influence: number;
566
+ }
567
+
568
+ /** The TRANSLATION channel of a pose: a joint displaced along its own local
569
+ * axis. Rotations alone cannot state a gait's vertical truth — a crouch, a
570
+ * jump apex, the hip dip that makes a walk read as weighted — because those
571
+ * are the root/hips MOVING, not a joint bending (round-4 finding: shot sets
572
+ * could not photograph a gait). Same delta semantics as the rotation: the
573
+ * capture engine applies `bone.translateX/Y/Z(meters)`, composing onto
574
+ * whatever local position the GLB baked. */
575
+ export interface ShotSetPoseTranslation {
576
+ bone: string;
577
+ axis: 'x' | 'y' | 'z';
578
+ /** A DELTA along the bone's own local axis, in meters, relative to the
579
+ * loaded GLB's baked rest position. */
580
+ meters: number;
581
+ }
582
+
583
+ /** One step of a named pose. A flat union rather than parallel lists: a
584
+ * single expression is normally one rotation AND one morph (a jaw ROTATION
585
+ * plus a brow MORPH), so keeping them in one ordered list means a definition
586
+ * never has to zip them. Discriminated by field name: `radians` is a
587
+ * rotation, `meters` a translation, `influence` a morph. */
588
+ export type ShotSetPoseStep = ShotSetPoseRotation | ShotSetPoseMorph | ShotSetPoseTranslation;
589
+
590
+ export type ShotSetShot =
591
+ | { label: string; view: 'turntable'; yaw: number; pose?: string | undefined }
592
+ | {
593
+ label: string;
594
+ view: 'bone-zoom';
595
+ bones: string[];
596
+ spanFraction: number;
597
+ /**
598
+ * The angle the crop is taken FROM, in the same convention and units as
599
+ * a turntable shot's `yaw` (radians; 0 is the front camera, -PI/2 the
600
+ * subject's left, +PI/2 its right). Omitted means 0 — the front-camera
601
+ * framing every bone-zoom shot had before this field existed, so an
602
+ * existing shot set renders unchanged.
603
+ *
604
+ * It exists because the front camera is not a general answer for a long
605
+ * subject: on an 8 m quadruped a crop anchored on the tail root
606
+ * photographs the hind legs standing between the camera and the tail. A
607
+ * junction whose axis runs down the body's length is inspectable only
608
+ * from the side.
609
+ */
610
+ yaw?: number | undefined;
611
+ pose?: string | undefined;
612
+ };
613
+
614
+ export interface AssetPreviewShotSetDefinition {
615
+ /** The set's name (the CLI's `--shots <name>`), echoed in error messages. */
616
+ name: string;
617
+ /** Joints that must exist on the loaded GLB's OWN skeleton — zoom anchors
618
+ * plus a loud failure naming the missing joints (never a silent
619
+ * bounding-box fallback for a set that promised skeleton anchoring). */
620
+ requiredBones?: string[];
621
+ /** Optional clause appended to rig-requirement errors,
622
+ * e.g. "a Mixamo-named humanoid skeleton". */
623
+ rigRequirementHint?: string;
624
+ /** Named poses (bone rotations and morph influences applied to a disposable
625
+ * snapshot only); shots opt in via their `pose` field. */
626
+ poses?: Record<string, ShotSetPoseStep[]>;
627
+ shots: ShotSetShot[];
628
+ }
629
+
630
+ /**
631
+ * A shot the capture engine rendered but does not vouch for.
632
+ *
633
+ * `'empty-frame'` — the shot's frame contained no renderable geometry, so
634
+ * the PNG is background only. It is reported rather than thrown because one
635
+ * mis-aimed crop must not kill a 20-shot render; it is reported LOUDLY
636
+ * because a background tile on a contact sheet otherwise reads as coverage.
637
+ */
638
+ export interface AssetPreviewShotWarning {
639
+ label: string;
640
+ reason: 'empty-frame';
641
+ /** Already names the shot, its anchor joints and its pose — surfaces print
642
+ * this string rather than re-composing one. */
643
+ message: string;
644
+ bones?: string[];
645
+ pose?: string;
646
+ }
647
+
648
+ export interface LabeledShotSetCapture {
649
+ width: number;
650
+ height: number;
651
+ shots: Array<ViewportCapture & { label: string }>;
652
+ /** Empty when every shot framed geometry. Absent against an editor that
653
+ * predates the empty-frame guard. */
654
+ warnings: AssetPreviewShotWarning[];
655
+ contactSheet: ViewportCapture & { width: number; height: number };
656
+ }
657
+
658
+ export interface EditorState {
659
+ /** Last announced work, held by the server; not a stack trace or causal claim. */
660
+ pageWork?: { label: string | null; reportedAgoMs: number } | null;
661
+ /**
662
+ * The Code-OSS workbench this session is running, or `null` when it is
663
+ * running none. The session's children are the session's to report, exactly
664
+ * as its run configurations are — so this is SERVER-computed on every read,
665
+ * never part of the browser-POSTed snapshot.
666
+ *
667
+ * `kind` is how the bytes were obtained: a `release` is an extracted
668
+ * `vscode-reh-web-*` package (its `BUILD.json` carries the commit), `sources`
669
+ * is a fork checkout (`git rev-parse HEAD` is the commit). `dir` is what the
670
+ * project's `.volter/workbench.json` — or the editor's `edit --workbench` command — named.
671
+ * `product` is the product whose workbench half is overlaid on those bytes
672
+ * (P3): a workbench is built for ONE product, and the session refuses one
673
+ * built for another than this project's before it spawns.
674
+ * Absent against an older server that predates the field.
675
+ */
676
+ workbench?: {
677
+ kind: 'release' | 'sources';
678
+ dir: string;
679
+ commit: string;
680
+ product: string;
681
+ } | null;
682
+ /**
683
+ * The PRODUCT this session is serving — `@volter/game-editor` or
684
+ * `@volter/cyclotron` — or `null` when it is serving none. SERVER-computed
685
+ * on every read, beside {@link workbench}, for the same reason: what a
686
+ * session is running is its own fact, not something the page reports about
687
+ * itself.
688
+ *
689
+ * `id` is the package name, `dir` where it resolved from, `version` what it
690
+ * is. Which product runs is never a switch (ARCHITECTURE-CORE §The target
691
+ * shape, rule 4) — it is what the project's dependencies resolved to — so
692
+ * this is a REPORT and never a thing to branch on. Absent against an older
693
+ * server that predates the field.
694
+ */
695
+ product?: { id: string; dir: string; version: string; command: string; displayName: string; upgrade?: boolean } | null;
696
+ playState: 'stopped' | 'playing' | 'paused';
697
+ /** Latest successful live lane's run window. Independent of video recording;
698
+ * null when no registered lane reports a run, absent on older editors. */
699
+ liveRunWindow?: import('./host').LiveRunWindow | null;
700
+ /**
701
+ * Issue #175 — the REAL engine `GameLoop.liveness` behind the current play
702
+ * session, distinct from `playState` above (editor UI state — a store
703
+ * flag that never reflected whether the loop was actually ticking).
704
+ * `'loop-starved'` means the host loop has observed no recent rAF progress:
705
+ * either the current `document.hidden` gate deliberately parked it, or an
706
+ * armed visible-page callback has not arrived within the starvation
707
+ * interval. It is explicitly NOT a conclusion that the tab is hidden.
708
+ * `null` while not in play mode (no loop to report on) or against an older
709
+ * server that predates this field.
710
+ */
711
+ loopLiveness?: 'running' | 'loop-starved' | 'stopped' | null;
712
+ /**
713
+ * The connected editor tab's OWN reported visibility/focus
714
+ * (`command-listener.ts`'s `collectPresence`, straight off
715
+ * `document.visibilityState`/`document.hasFocus()`). This is the only field
716
+ * that distinguishes a usable session from a merely attached one:
717
+ * `connected` answers "is an SSE client holding the session open", which a
718
+ * BACKGROUNDED tab satisfies perfectly while the engine hidden-pauses its
719
+ * loop underneath. `null` when the page has no `document` at all; absent
720
+ * against an older server that predates the field.
721
+ *
722
+ * P21 — `reportedAt` is `Date.now()` in the TAB at the moment those two
723
+ * values were read. It is what makes this snapshot readable as a
724
+ * measurement rather than a fact: the tab re-POSTs state on
725
+ * visibilitychange/focus/blur, after commands and on store changes, so
726
+ * between those moments this ages, and a reader with no age has no way to
727
+ * tell a 40ms-old reading from a 40s-old one. Absent against an editor
728
+ * page that predates the field — never fabricated from the read time.
729
+ */
730
+ presence?: {
731
+ visibility: 'visible' | 'hidden' | 'prerender';
732
+ focused: boolean;
733
+ reportedAt?: number;
734
+ } | null;
735
+ /**
736
+ * The pending-restart reason when source changed while the game was
737
+ * RUNNING and the running session is now stale (e.g. an R3F entry-file
738
+ * write-back during play, a registry.ts edit). The editor's Restart button
739
+ * surfaces the same reason; one restart (the editor's `play` command, or the button)
740
+ * remounts every root from fresh source and clears it. `null` when the
741
+ * running session is fresh; absent against an older server that predates
742
+ * the field.
743
+ */
744
+ restartRequired?: string | null;
745
+ selectedEntityId: string | null;
746
+ selectedEntityIds: string[];
747
+ activeViewportTab: ViewportTab;
748
+ /** The actual active center document, including tool/source documents. */
749
+ activeDocumentId?: string | null;
750
+ /** The visible bottom utility, or null when that drawer is collapsed. */
751
+ activeUtilityId?: string | null;
752
+ /** The shared Analytics rail's current durable-session selection. */
753
+ gameplaySession?: {
754
+ selectedSessionId: string | null;
755
+ latestSessionId: string | null;
756
+ selectedStatus: 'live' | 'completed' | null;
757
+ cursorMs: number;
758
+ liveEdgeMs: number;
759
+ };
760
+ /** Every center document the editor currently has open, by stable registry id. */
761
+ openDocumentIds?: string[];
762
+ /** Every document a package has made available to open, and which one the
763
+ * session opens by default when nothing is restored. */
764
+ availableDocuments?: { id: string; category: string; default: boolean }[];
765
+ /** Live editor-owned WebGL renderers, split by resource owner. */
766
+ rendererResources?: {
767
+ hostLive: number;
768
+ interactive: { active: number; idle: number };
769
+ inspectorPreview: { active: number; idle: number };
770
+ };
771
+ activeTabKey: string;
772
+ showGrid: boolean;
773
+ showHelpers: boolean;
774
+ showStats: boolean;
775
+ shadingMode: ShadingMode;
776
+ helperVisibility: HelperVisibility;
777
+ transformMode: 'translate' | 'rotate' | 'scale';
778
+ transformSpace: 'world' | 'local';
779
+ snapEnabled: boolean;
780
+ entityCount: number;
781
+ savePath: string | null;
782
+ /** Live editor persistence state. Wait for `saved` before an external scene-file write. */
783
+ saveState: 'saved' | 'unsaved' | 'failed';
784
+ /** Current editor camera pose when the viewport has bound a camera. */
785
+ camera?: EditorCameraState;
786
+ /** Flattened live authoring hierarchy, useful for agent entity discovery. */
787
+ entities?: EditorEntitySummary[];
788
+ /**
789
+ * How many browser tabs are PRESENT for this session right now, read from
790
+ * the server's tab table (`server/tab-presence.ts`) rather than from a
791
+ * socket count — so a tab mid-reload still counts (it is beating), and a
792
+ * socket with no tab behind it does not.
793
+ *
794
+ * The rest of this object is the last snapshot a browser POSTed and
795
+ * persists even after every tab goes — so a `0` here means the other fields
796
+ * are stale cache and commands (`play`, `scene`, …) will refuse with the
797
+ * table's own reason. Added by the server on every `/__editor/state` read.
798
+ */
799
+ editorsConnected?: number;
800
+ /** Convenience: `editorsConnected > 0`. */
801
+ connected?: boolean;
802
+ /**
803
+ * ONE ROW PER PRESENT TAB — the whole table, because the owner's rule for
804
+ * this seam is "if they DO get disconnected, make it clear that it
805
+ * happened" and a single boolean can never say that.
806
+ *
807
+ * `lastBeatAgo` is the heartbeat age (null for a tab that cannot beat, e.g.
808
+ * one bridged through the share tunnel); a number climbing past a second or
809
+ * two is a gap in progress. `epochCount` counts page-loads, so a number
810
+ * that keeps rising is a reload loop. `channel: 'down'` with a fresh beat
811
+ * is a tab mid-reload — present, and briefly unable to take a command.
812
+ * `unresponsive` is the zombie: beating, but its page has never opened a
813
+ * command channel this page-load. Absent against an older server.
814
+ *
815
+ * `commandListener` is the standing verdict on the DOCUMENT, and it is a
816
+ * different question from all of the above: the control channel is opened by
817
+ * the tiny pre-React entry before any module loads, so a page that dies
818
+ * during boot beats, holds a channel, gets blessed, and executes nothing. It
819
+ * reads `'not attached'` for that page, `'silent since <t>'` for one whose
820
+ * listener stopped acknowledging relayed commands, and `'ready'` otherwise —
821
+ * each from a timestamp the server already stamps (the listener's own
822
+ * attach/detach report, and the command receipts). Absent against an older
823
+ * server, and absent for a tab the server cannot measure.
824
+ *
825
+ * `pageErrors` is the OTHER half of that verdict — WHY. Uncaught errors and
826
+ * unhandled rejections captured by the page shell's inline bootstrap, which
827
+ * runs before the module graph, so the boot failure that leaves no listener
828
+ * is exactly the one they explain. They come back on a plain GET and need no
829
+ * cooperation from the page beyond the handler itself. `[]` means the page
830
+ * reported none; absent means the server cannot measure this tab.
831
+ *
832
+ * `census` is the tab's RESOURCE PROFILE, sampled by the page every five
833
+ * seconds and carried on the heartbeat: what a browser-level renderer death
834
+ * would otherwise leave unexplained. It is `@volter/sdk/tab-census`'s
835
+ * {@link RecordedTabCensus} — the census MINUS the `mountEpochs` guarantee,
836
+ * because this is a response and the server that answered it may be older
837
+ * than that field; every other absence (`heapUsedMB` off Chromium, renderer
838
+ * counts with no mounted adapter) is documented on the type itself.
839
+ * `censusAgeMs` says how stale the profile is — a hidden tab is not sampled.
840
+ * The census's `blender` block is the OTHER question it carries: how long the
841
+ * tab's Blender worker has been holding a call and how long this thread has
842
+ * stalled, absent in a tab with no Blender session.
843
+ */
844
+ tabs?: Array<{
845
+ /**
846
+ * WHAT IS TRUE OF THIS TAB, in one word — `ended`, `closed`, `reloading`,
847
+ * `crashed`, `suspended`, `hung`, `busy` or `present` — derived
848
+ * server-side by ONE function over the fields below
849
+ * (`server/tab-presence.ts`'s `tabState`), never re-derived by a reader.
850
+ *
851
+ * It exists because every other field on this row answers a NARROWER
852
+ * question than the one that gets asked. Measured 2026-09-17: one symptom
853
+ * ("the battery stopped") had four causes in one night — a renderer killed
854
+ * by a dev-server reload, a worker that never finished booting, a main
855
+ * thread wedged inside a 46 MB encode, and a twin call genuinely running
856
+ * for sixteen minutes — and each of them is a different instruction to
857
+ * whoever is reading. `stateMs` is THE number that goes with the word, and
858
+ * it is a different measurement per state (time since the goodbye, beat
859
+ * age, the gap that was resumed, census age, the outstanding call's age);
860
+ * `stateBecause` is the evidence in a sentence, so a reader never has to
861
+ * know which field the verdict came from. Absent against an older server.
862
+ */
863
+ state?:
864
+ | 'ended'
865
+ | 'closed'
866
+ | 'reloading'
867
+ | 'crashed'
868
+ | 'suspended'
869
+ | 'hung'
870
+ | 'busy'
871
+ | 'present';
872
+ stateMs?: number | null;
873
+ stateBecause?: string;
874
+ tabId8: string;
875
+ presentFor: number;
876
+ lastBeatAgo: number | null;
877
+ epochCount: number;
878
+ /** Age of the DOCUMENT running in this tab (this page-load), as opposed
879
+ * to `presentFor`, which is the age of the tab and survives its
880
+ * reloads. Absent against an older server. */
881
+ epochAgeMs?: number;
882
+ visibility: 'visible' | 'hidden';
883
+ route: 'project' | 'no-project' | 'unknown';
884
+ /**
885
+ * WHAT KIND OF PAGE this tab is: the editor's own page, or a Code-OSS
886
+ * workbench window running it through the frame (docs/CODE-OSS.md §Boot,
887
+ * DESKTOP). A reader prints it because "a VS Code window that beats" and
888
+ * "a browser tab that beats" are the same health and different places to
889
+ * look when they are not. Absent against an older server.
890
+ */
891
+ surface?: 'editor' | 'vscode';
892
+ blessed: boolean;
893
+ channel: 'open' | 'down';
894
+ unresponsive: boolean;
895
+ commandListener?: 'ready' | 'not attached' | (string & {});
896
+ pageErrors?: string[];
897
+ /**
898
+ * The control-plane generation handshake for each page load currently
899
+ * associated with this physical tab. Anything other than `aligned` is a
900
+ * lifecycle contradiction or a handshake still in progress.
901
+ */
902
+ controlLifecycles?: Array<{
903
+ status: 'aligned' | 'awaiting-heartbeat' | 'unconfirmed' | 'mismatch';
904
+ serverGeneration8: string;
905
+ connectionGeneration8: string;
906
+ pageGeneration8: string;
907
+ clientId8: string;
908
+ }>;
909
+ census?: RecordedTabCensus | null;
910
+ censusAgeMs?: number | null;
911
+ }>;
912
+ /**
913
+ * TABS THAT ARE GONE — the tab table's short departure memory, same row
914
+ * shape as {@link tabs} above.
915
+ *
916
+ * `ended`, `closed` and `crashed` are verdicts about a tab that is no longer present,
917
+ * so this is the only array they can appear in, and they are exactly the two
918
+ * answers the product could not give before: a tab whose beats stopped left
919
+ * the table and took its explanation with it. Kept separate from `tabs`
920
+ * because `editorsConnected` counts that one — a dead row inside it would
921
+ * make a crashed tab read as a connected one. Bounded by age and count
922
+ * (a memory, not a log; the session journal is the archive). Absent against
923
+ * an older server.
924
+ */
925
+ departedTabs?: EditorState['tabs'];
926
+ /**
927
+ * The auto-open runaway guard. `stopped` means this session opened
928
+ * `attempts` tabs, none of them ever appeared in the table, and it has
929
+ * stopped trying — the browser, not the editor, is what to check.
930
+ */
931
+ tabAutoOpen?: { attempts: number; stopped: boolean };
932
+ /**
933
+ * Epoch ms of the last genuine HTML page load this server served — i.e.
934
+ * when the editor tab last did a FULL document load (first open, reload,
935
+ * self-heal). Server-observed (`editor-server.ts`'s response-finish
936
+ * middleware), never browser-reported. `null` until the first page load.
937
+ *
938
+ * P20 reads this against {@link publicAssets} to answer "did bytes under
939
+ * `public/` change since the running document loaded", which is when
940
+ * module-scope loaders and the page-lifetime asset caches (Pixi `Assets`,
941
+ * three's loader caches) can still be serving the OLD bytes.
942
+ */
943
+ lastIndexRequestAt?: number | null;
944
+ /**
945
+ * P20 — what the server has OBSERVED land under this project's `public/`
946
+ * during this server lifetime, from the same chokidar watcher that
947
+ * broadcasts `assets-changed`. `lastChangedAt` is `null` when nothing has
948
+ * changed since the server started. Absent against an older server.
949
+ *
950
+ * This is a divergence signal, not a cache verdict: nothing here knows
951
+ * whether the running page actually holds a stale copy of those bytes,
952
+ * only that they changed after it loaded.
953
+ */
954
+ publicAssets?: {
955
+ lastChangedAt: number | null;
956
+ lastPath: string | null;
957
+ changedCount: number;
958
+ };
959
+ /**
960
+ * Epoch ms when the server last received a state POST from a browser tab —
961
+ * i.e. the age of the cached snapshot above. Omitted if no tab has ever
962
+ * posted state this server lifetime (or since the last project switch,
963
+ * which clears the cache). Present regardless of `connected`, but only
964
+ * meaningful for interpreting staleness when `connected` is `false`.
965
+ */
966
+ stateUpdatedAt?: number;
967
+ /**
968
+ * Validate-on-change (#103): per-file validation status for every
969
+ * Project `src/**` source / `volter.project.json` the dev
970
+ * server has seen
971
+ * change since it booted (or since the last project switch). Server-
972
+ * computed — unlike the rest of `EditorState`, it is NOT part of the
973
+ * browser-POSTed snapshot, so it is always current. A file appears here
974
+ * ONLY while it is currently failing; a clean write removes its entry
975
+ * (absence means "not known to be invalid", not "never checked"). Always
976
+ * present (`{}` when nothing is failing) so the editor's `status` command consumers can
977
+ * read it unconditionally.
978
+ */
979
+ projectValidation?: Record<string, { errors: string[]; at: number }>;
980
+ /**
981
+ * PD-13: the WARNING half of the same server-computed validation pass —
982
+ * authoring-convention findings (the R3F00x codes, OID surface conflicts)
983
+ * that do not make a file invalid but do make it unauthorable. Same shape,
984
+ * same lifecycle and same freshness guarantee as `projectValidation` above:
985
+ * whole-project (not just the open document), keyed by project-relative
986
+ * path, present only while the file currently warns, `{}` when clean.
987
+ *
988
+ * The server has sent this since the R3F authoring diagnostics landed; it
989
+ * was missing from this interface, so every typed consumer — the editor's `status` command
990
+ * included — could only reach it through a cast. Declared here so a caller
991
+ * that wants to react to authoring warnings can see they exist.
992
+ */
993
+ projectWarnings?: Record<string, { warnings: string[]; at: number }>;
994
+ /**
995
+ * PD-14: whether the SOURCE half of the validation pass above is running at
996
+ * all. `'active'` is the normal state; `'awaiting-src'` means the project
997
+ * has no `src/` directory yet, so nothing under `src/` is being validated —
998
+ * an empty `projectValidation`/`projectWarnings` says nothing about source
999
+ * files while this reads `'awaiting-src'`. It is not terminal: the watcher
1000
+ * is armed on the not-yet-existing path and flips to `'active'` (running
1001
+ * the boot-equivalent scan) the moment `src/` appears, with no restart.
1002
+ * `'no-project'` when no project is open. Server-computed, like its
1003
+ * neighbors above.
1004
+ */
1005
+ sourceValidation?: 'active' | 'awaiting-src' | 'no-project';
1006
+ /**
1007
+ * The compatibility verdict between this editor and the project it serves —
1008
+ * `null` when they agree, otherwise the refusal the browser renders when it
1009
+ * declines to activate the project, with its recovery guidance.
1010
+ *
1011
+ * Server-computed per read like its neighbors above. It is here because the
1012
+ * gate was previously reported ONLY in the browser: an editor started on an
1013
+ * incompatible project serves happily (it activates nothing), so the tab
1014
+ * showed "This project is pinned to @volter/project X, but this editor is
1015
+ * running Y" while the editor's `status` command reported a connected session with empty
1016
+ * validation and the editor's `play` command timed out into a retry message about the tab
1017
+ * reloading. An agent drives this editor through the CLI, so a gate visible
1018
+ * only in pixels is invisible by construction.
1019
+ */
1020
+ projectCompatibility?: {
1021
+ error: string;
1022
+ recovery?: { kind: string; title: string; guidance: string; command?: string };
1023
+ } | null;
1024
+ /**
1025
+ * #124: the absolute path of the project this editor server currently has
1026
+ * open — server-computed (never part of the browser-POSTed snapshot,
1027
+ * exactly like `projectValidation` above), so it is always current. Added
1028
+ * so a watcher/relay holding only a port number (e.g. an agent that
1029
+ * printed a the editor's `edit` command URL earlier and lost track of which project it
1030
+ * belongs to) can identify which project that port serves without also
1031
+ * reading the `~/.volter/editor-sessions.json` registry file. `null` when no
1032
+ * project is open (the in-repo "no project selected" default server
1033
+ * state — mirrors `/__editor/project`'s own `{ project: null }` shape).
1034
+ */
1035
+ projectRoot?: string | null;
1036
+ /**
1037
+ * #124: the open project's declared name, alongside `projectRoot` above
1038
+ * (`volter.project.json`'s `name`). `null` when no project is open, or the open
1039
+ * project has no readable manifest name.
1040
+ */
1041
+ projectName?: string | null;
1042
+ /**
1043
+ * The open project's ADAPTER, resolved (ARCHITECTURE-CORE §The editor
1044
+ * protocol). `source` names WHOSE declaration is running: `'project'` = the
1045
+ * project's own `editor/volter.adapter.ts` supplied the binding table (and it always
1046
+ * outranks the registry); `'registry'` = the HOST's in-tree ingest registry
1047
+ * supplied it, matched on this project's ingest root id, with `modulePath`
1048
+ * naming the repo file — a binding the project did not ship, stated rather
1049
+ * than inferred; `'native'` = it declared none and got `nativeAdapter()` —
1050
+ * the declared native default, not a silent fallback.
1051
+ *
1052
+ * `null`/absent means NOBODY HAS LOOKED YET (no project open, or the load
1053
+ * has not finished), which is deliberately distinct from a loaded adapter
1054
+ * whose `scenes.entries` is empty — that is a real, gradable answer. A
1055
+ * non-null `error` means the project's own module did NOT load and the
1056
+ * table below is the native default standing in, with the failure named.
1057
+ *
1058
+ * Structurally declared here rather than imported from
1059
+ * `@volter/project/adapter/adapter-module` because this interface is the WIRE
1060
+ * contract: everything in it has already been through JSON.
1061
+ */
1062
+ adapter?: {
1063
+ source: 'project' | 'registry' | 'native';
1064
+ modulePath: string | null;
1065
+ regions: {
1066
+ id: string;
1067
+ surface: string;
1068
+ projector: string;
1069
+ dialect: string | null;
1070
+ anchors: string[];
1071
+ }[];
1072
+ scenes: {
1073
+ default: string | null;
1074
+ entries: {
1075
+ id: string;
1076
+ label: string;
1077
+ /** OPEN (ARCHITECTURE-CORE §The project model, "Documents, not
1078
+ * scenes"): `scene` and `prefab` are the first two kinds; a page,
1079
+ * a model, a shot, a take are kinds the same way. */
1080
+ kind: string;
1081
+ region: string | null;
1082
+ authorable: boolean;
1083
+ reach: { kind: string; [field: string]: unknown };
1084
+ source?: { path: string; export?: string };
1085
+ finder?: string;
1086
+ }[];
1087
+ };
1088
+ notes: string[];
1089
+ error: string | null;
1090
+ } | null;
1091
+ }
1092
+
1093
+ export type ViewPreset = 'top' | 'front' | 'right' | 'perspective';
1094
+ export type ShadingMode =
1095
+ | 'solid'
1096
+ /** Authored materials, lit by a preview environment (Blender's Material Preview). */
1097
+ | 'preview'
1098
+ /** Authored materials, lit as a render is: the scene's own lights (Blender's Rendered). */
1099
+ | 'rendered'
1100
+ | 'clay'
1101
+ | 'unlit'
1102
+ | 'wireframe'
1103
+ | 'matcap'
1104
+ | 'normals'
1105
+ | 'overdraw';
1106
+
1107
+ export type TransformMode = 'translate' | 'rotate' | 'scale';
1108
+ export type TransformSpace = 'world' | 'local';
1109
+
1110
+ /**
1111
+ * Stable editor-owned workspace documents that may appear in a shareable
1112
+ * view. One runtime list owns both URL parsing and the public id type.
1113
+ *
1114
+ * This SDK cannot import the editor — the dependency runs the other way — so
1115
+ * nothing links this list to the documents the editor actually registers, and
1116
+ * an id added there is silently unaddressable here (a view carrying it
1117
+ * round-trips to nothing). The link is asserted from the side that CAN see
1118
+ * both: `packages/editor/test/editor-view-address-space.test.ts`. Adding a
1119
+ * document id means adding it here too.
1120
+ */
1121
+ export const EDITOR_VIEW_WORKSPACE_DOCUMENT_IDS = [
1122
+ 'workspace:scene',
1123
+ 'workspace:canvas-scene',
1124
+ 'workspace:game',
1125
+ 'workspace:3d-components',
1126
+ 'workspace:2d-components',
1127
+ 'workspace:ui-components',
1128
+ 'account',
1129
+ 'project-tools',
1130
+ ] as const;
1131
+
1132
+ export type EditorViewWorkspaceDocumentId = (typeof EDITOR_VIEW_WORKSPACE_DOCUMENT_IDS)[number];
1133
+
1134
+ /**
1135
+ * The editor's OWN bottom-drawer instruments, as named in a shareable view.
1136
+ * One runtime list owns both URL parsing and the public id type — the same
1137
+ * discipline as {@link EDITOR_VIEW_WORKSPACE_DOCUMENT_IDS}, and for the same
1138
+ * reason: a separate hand-written union and parser allowlist drift silently
1139
+ * (`behavior` was in the union and missing from the allowlist, so a view
1140
+ * carrying it round-tripped to nothing).
1141
+ *
1142
+ * This must equal the editor's live `BUILT_IN_WORKSPACE_UTILITIES`
1143
+ * (`packages/sdk/src/kit/workspace-core-utilities.ts`). It had drifted to five
1144
+ * of thirteen — every id from `generations` onward was unaddressable in a
1145
+ * shared view. As above, the SDK cannot import the editor to derive this, so
1146
+ * `packages/editor/test/editor-view-address-space.test.ts` asserts the
1147
+ * equality from the side that sees both.
1148
+ */
1149
+ export const EDITOR_VIEW_BUILT_IN_UTILITY_IDS = [
1150
+ 'animation',
1151
+ 'behavior',
1152
+ 'generations',
1153
+ 'console',
1154
+ 'light-explorer',
1155
+ 'story-actions',
1156
+ 'story-interactions',
1157
+ 'story-accessibility',
1158
+ ] as const;
1159
+
1160
+ export type EditorViewBuiltInUtilityId = (typeof EDITOR_VIEW_BUILT_IN_UTILITY_IDS)[number];
1161
+
1162
+ /** The prefix a PROJECT-contributed utility's id carries. The editor's tool
1163
+ * loader namespaces every `workspace.utility` contribution as
1164
+ * `tool:<contribution-id>`, exactly as a contributed center document is
1165
+ * addressed by `{ kind: 'tool', id }`. */
1166
+ export const EDITOR_VIEW_TOOL_UTILITY_PREFIX = 'tool:';
1167
+
1168
+ /**
1169
+ * Which bottom-drawer utility a view reveals: one of the editor's own
1170
+ * instruments, or a utility the OPEN PROJECT contributes.
1171
+ *
1172
+ * The project half cannot be an enum — which tabs exist depends entirely on
1173
+ * the game that is open — so the address space admits the namespace and the
1174
+ * EDITOR validates the id against its live utility registry when the view is
1175
+ * presented, refusing an unregistered one by naming what IS registered.
1176
+ */
1177
+ export type EditorViewUtility =
1178
+ | EditorViewBuiltInUtilityId
1179
+ | `${typeof EDITOR_VIEW_TOOL_UTILITY_PREFIX}${string}`;
1180
+
1181
+ /** Whether `value` is addressable as a view's utility. Shape only — existence
1182
+ * is the editor's answer at present-time, not the URL's. */
1183
+ export function isEditorViewUtility(value: string): value is EditorViewUtility {
1184
+ return (
1185
+ (EDITOR_VIEW_BUILT_IN_UTILITY_IDS as readonly string[]).includes(value) ||
1186
+ (value.startsWith(EDITOR_VIEW_TOOL_UTILITY_PREFIX) &&
1187
+ value.length > EDITOR_VIEW_TOOL_UTILITY_PREFIX.length)
1188
+ );
1189
+ }
1190
+
1191
+ /**
1192
+ * A durable, intentionally small projection of what an editor is presenting.
1193
+ * This is not workspace persistence: panel sizes, transient tool state, and
1194
+ * authored document contents remain outside the URL.
1195
+ */
1196
+ export type EditorViewDocument =
1197
+ | { kind: 'scene'; path: string }
1198
+ | { kind: 'asset'; path: string; entityId?: never; assetKind?: AssetKind }
1199
+ | { kind: 'asset'; entityId: string; path?: never; assetKind?: 'model' }
1200
+ | { kind: 'tool'; id: string }
1201
+ /** A document the adapter's table lists (a model, a page) — `id` is the
1202
+ * table entry's id — opened in the editor registered for its kind. */
1203
+ | { kind: 'document'; id: string }
1204
+ | { kind: 'world'; id: string }
1205
+ | { kind: 'story'; modulePath: string; storyName: string; mode?: 'preview' | 'docs' }
1206
+ | { kind: 'project-tool'; name: string }
1207
+ | { kind: 'generation'; id: string }
1208
+ | {
1209
+ kind: 'workspace';
1210
+ id: EditorViewWorkspaceDocumentId;
1211
+ /**
1212
+ * For a COMPONENT BOARD document: the portable story frame to open on
1213
+ * — a story id, or a unique CSF export name or label (an ambiguous or
1214
+ * unknown value refuses loudly, listing candidates). Emitted back by
1215
+ * the board's own presentation so a captured view round-trips. This is
1216
+ * the design ledger's "board story selector": without it only the
1217
+ * derived default story could ever be addressed.
1218
+ */
1219
+ story?: string;
1220
+ };
1221
+
1222
+ /**
1223
+ * The document ADDRESS KINDS an `EditorView` can carry — the same published,
1224
+ * finite vocabulary `EDITOR_VIEW_KEYS` is for the view's own keys, and for the
1225
+ * same reason: a kind this list does not hold must refuse by name rather than
1226
+ * fall off the end of a switch. `{kind: 'model', path}` used to present `ok`,
1227
+ * move nothing, and echo the caller's own mistake back in `view.doc=undefined`.
1228
+ *
1229
+ * A document KIND (`model`, `page`) is not one of these: it is the kind of a
1230
+ * row in the project's own document table, and its address is
1231
+ * `{kind: 'document', id}` — "a document opens in the editor registered for its
1232
+ * KIND" (ARCHITECTURE-CORE). The refusal says exactly that, reading the kinds
1233
+ * from the project's resolved table rather than from a list written here.
1234
+ */
1235
+ export const EDITOR_VIEW_DOCUMENT_KINDS = [
1236
+ 'scene',
1237
+ 'asset',
1238
+ 'tool',
1239
+ 'document',
1240
+ 'world',
1241
+ 'story',
1242
+ 'project-tool',
1243
+ 'generation',
1244
+ 'workspace',
1245
+ ] as const satisfies ReadonlyArray<EditorViewDocument['kind']>;
1246
+
1247
+ /** Narrow an arbitrary string to one of {@link EDITOR_VIEW_DOCUMENT_KINDS}. */
1248
+ export function isEditorViewDocumentKind(value: string): value is EditorViewDocument['kind'] {
1249
+ return (EDITOR_VIEW_DOCUMENT_KINDS as readonly string[]).includes(value);
1250
+ }
1251
+
1252
+ export interface EditorView {
1253
+ version: 1;
1254
+ /** The workspace (a layout: `model`, `sculpt`, `game`, …) the view is in —
1255
+ * the host's own or a package's `workspace.layout` contribution. Presenting
1256
+ * one the open project does not offer REFUSES and names the vocabulary, the
1257
+ * same answer as `set-workspace`; `currentView` always reports it. */
1258
+ workspace?: string;
1259
+ /** The style bundle (`classic`, `glass`, `blender`, …) the chrome wears —
1260
+ * the host's own or a package's `workspace.style` contribution. Presenting
1261
+ * one the open project does not offer REFUSES and names the vocabulary,
1262
+ * the same answer as `set-style`; `currentView` reports it when the four
1263
+ * appearance axes match a bundle, and omits it for a custom mix. */
1264
+ style?: string;
1265
+ /**
1266
+ * The keymap (`volter`, `blender`, …) whose bindings the chrome is printing
1267
+ * and dispatching — the editor's own or a package's `workspace.keymap`
1268
+ * contribution, as the project's adapter declares it or its settings
1269
+ * override it. REPORTED, never presented: `currentView` always answers it,
1270
+ * and `present-view` WARNS on a keymap it was handed rather than switching,
1271
+ * because which bindings a project uses is that project's declaration and a
1272
+ * person's preference, not a property of a shared link.
1273
+ */
1274
+ keymap?: string;
1275
+ /**
1276
+ * The static PANEL the dock is focused on — `hierarchy`, `asset-library`,
1277
+ * `inspector`, and whatever else the editor's panel registry holds. Shape
1278
+ * only here, exactly like `workspace`: the vocabulary is the open editor's
1279
+ * registry, so presenting a key it does not hold REFUSES and names the ones
1280
+ * it does. `currentView` reports it while a panel (rather than a document or
1281
+ * a drawer utility) holds the dock's focus.
1282
+ */
1283
+ panel?: string;
1284
+ document?: EditorViewDocument;
1285
+ selection?: { ids: string[]; focus?: boolean };
1286
+ viewport?: {
1287
+ camera?: ViewPreset | 'isometric' | EditorCameraState;
1288
+ diagnostic?: ShadingMode | 'uv' | 'vertex-colors' | 'bounds' | 'skeleton';
1289
+ frame?: 'document' | 'selection';
1290
+ grid?: boolean;
1291
+ };
1292
+ utility?: EditorViewUtility;
1293
+ }
1294
+
1295
+ /**
1296
+ * THE ADDRESS SPACE, AS DATA — every key {@link EditorView} carries, so the
1297
+ * presenter can REFUSE one it does not (`editor-view-presentation.ts`).
1298
+ *
1299
+ * It lives beside the interface because that is the only placement where a
1300
+ * drift is visible in one screen: adding a field above without adding its key
1301
+ * here is the whole failure mode, and the row order is the interface's.
1302
+ *
1303
+ * WHY IT EXISTS (found live, 2026-09-19, unit 17's proof run): a view with the
1304
+ * document's `kind` spelled at the TOP level — `{version: 1, kind: 'story',
1305
+ * modulePath, storyName}` instead of `{version: 1, document: {kind: 'story',
1306
+ * …}}` — presented `ok`, changed nothing, and ECHOED THE BOGUS KEY BACK in
1307
+ * `PresentedEditorView.view`, so the caller read its own mistake as
1308
+ * confirmation. Three `present` proofs were recorded against it before
1309
+ * `currentView()` showed the document had never moved. Every NAMED field here
1310
+ * already refuses by name (`style`, `workspace`, `panel`); an unnamed one was
1311
+ * the silent hole, which is the case CLAUDE.md's "unknown input must REJECT
1312
+ * LOUDLY rather than be partially read" is about.
1313
+ */
1314
+ export const EDITOR_VIEW_KEYS = [
1315
+ 'version',
1316
+ 'workspace',
1317
+ 'style',
1318
+ 'keymap',
1319
+ 'panel',
1320
+ 'document',
1321
+ 'selection',
1322
+ 'viewport',
1323
+ 'utility',
1324
+ ] as const satisfies ReadonlyArray<keyof EditorView>;
1325
+
1326
+ export interface PresentedEditorView {
1327
+ view: EditorView;
1328
+ /** Shareable URL for the durable projection that was applied. */
1329
+ url: string;
1330
+ /** Honest degradations; an unsupported requested view is never silent. */
1331
+ warnings: string[];
1332
+ }
1333
+
1334
+ /**
1335
+ * How big a capture comes back.
1336
+ *
1337
+ * A NUMBER is a square of that size, and square stays the default — an
1338
+ * unstaged look at a model is a square question. `{width, height}` is for the
1339
+ * shaped answer: a video-aspect frame that needs no crop afterwards, which is
1340
+ * what the model/module lanes' looks are actually for.
1341
+ *
1342
+ * Both are bounded by the EDITOR's own ceiling — 64..1024 per side, plus a
1343
+ * total no larger than a 1024 square. That is the relay budget, not a taste:
1344
+ * the pixels cross the editor relay as base64 JSON, and 1024 is where even
1345
+ * incompressible RGBA still fits its 50 MB request limit (`asset-preview.ts`'s
1346
+ * `MIN_SIZE`/`MAX_SIZE`). For more picture, take more views, not bigger ones.
1347
+ */
1348
+ export type CaptureDimensions = number | { readonly width: number; readonly height: number };
1349
+
1350
+ /** The pixels of the active center document, with enough provenance for an
1351
+ * agent to prove which user-visible subject it captured. Editor chrome is
1352
+ * deliberately excluded. */
1353
+ /** A photograph of the editor PAGE — every panel as the person sees it
1354
+ * (`capture-editor-chrome`; the door that lets a skin, a workspace or a
1355
+ * contributed panel be judged sighted through the product). */
1356
+ export interface EditorChromeCapture extends ViewportCapture {
1357
+ view: EditorView;
1358
+ /** The frame's own size, in OUTPUT pixels. */
1359
+ size: { width: number; height: number };
1360
+ /** Output pixels per CSS pixel — what one pixel of {@link size} is. */
1361
+ scale: number;
1362
+ layers: { canvases: number; domOverlays: number };
1363
+ flatness?: { degenerate: boolean; warning?: string };
1364
+ }
1365
+
1366
+ /** What an editor-chrome capture photographs, and at what scale. */
1367
+ export interface EditorChromeCaptureOptions {
1368
+ /**
1369
+ * Output pixels per CSS pixel, at most 4. Defaults to the page's own
1370
+ * `devicePixelRatio`, so the default frame is the pixels the display holds.
1371
+ *
1372
+ * A stroke weight, a 1 px border or a glyph edge cannot be judged below the
1373
+ * resolution it is being compared against — on a DPR-1 monitor the default
1374
+ * is 1 and a reference captured at 2x is only comparable if this is asked
1375
+ * for explicitly. It scales the DOM leg by rasterizing it through a viewBox
1376
+ * (crisp at any factor); canvases are limited by their own backing store and
1377
+ * are upscaled past it.
1378
+ */
1379
+ readonly scale?: number;
1380
+ /** `page` (the default): the whole editor. `document`: the active document's own box as the
1381
+ * person sees it, overlays included (a viewport's navigation gizmo, its readouts). The door
1382
+ * a stage is judged through; `captureActiveDocument` is the document's render alone.
1383
+ * `play`: the active document's live world and UI frame, excluding authoring chrome
1384
+ * and surrounding letterboxing. Refused when the document has no live frame. */
1385
+ readonly region?: 'page' | 'document' | 'play';
1386
+ /** What the photograph is of, in a few words ("aim up"): the corner picture's caption and the
1387
+ * CLI's file name. It does not change the pixels. */
1388
+ readonly name?: string;
1389
+ }
1390
+
1391
+ export interface ActiveDocumentCapture extends ViewportCapture {
1392
+ document: {
1393
+ id: string;
1394
+ title: string;
1395
+ kind: string;
1396
+ sourcePath?: string;
1397
+ rootId?: string;
1398
+ };
1399
+ view: EditorView;
1400
+ source: 'scene-viewport' | 'game-composite' | 'object3d-document' | 'document-composite';
1401
+ layers?: { canvases: number; domOverlays: number };
1402
+ }
1403
+
1404
+ /** Template ids accepted by the editor's create-project endpoint. */
1405
+ export type ProjectTemplate = 'default' | '2d' | 'react' | 'example';
1406
+
1407
+ export interface ProjectInfo {
1408
+ path: string;
1409
+ config: { name: string; [key: string]: unknown };
1410
+ }
1411
+
1412
+ export interface RecentProject {
1413
+ name: string;
1414
+ path: string;
1415
+ lastOpened: string;
1416
+ thumbnail?: string;
1417
+ }
1418
+
1419
+ export type ProjectToolOutcome =
1420
+ | { ok: true; data: unknown; generation?: GenerationJob; generationWarning?: string }
1421
+ | {
1422
+ ok: false;
1423
+ error: {
1424
+ code: string;
1425
+ message: string;
1426
+ data?: unknown;
1427
+ issues?: Array<{ path?: PropertyKey[]; message?: string; [key: string]: unknown }>;
1428
+ };
1429
+ };
1430
+
1431
+ import type { GenerationJob } from '@volter/sdk/generations';
1432
+ import type { RecordedTabCensus } from '@volter/sdk/tab-census';
1433
+
1434
+ export type {
1435
+ ProjectToolCatalog,
1436
+ ProjectToolCatalogEntry,
1437
+ ProjectToolContribution,
1438
+ ToolContributionPoint,
1439
+ } from '@volter/sdk/project-tool-catalog';
1440
+
1441
+ // ---------------------------------------------------------------------------
1442
+ // Inspection — the serialized inspection subject (`EditorClient.inspect`)
1443
+ // ---------------------------------------------------------------------------
1444
+ //
1445
+ // The wire mirror of the editor's own `SerializedInspectionSubject`
1446
+ // (`packages/sdk/src/kit/inspection/serialize.ts`, which owns the contract and
1447
+ // carries the reasoning). `command-listener.ts` annotates its `inspect`
1448
+ // payload with this type, so `tsc` checks the two sides against each other on
1449
+ // every build rather than letting them drift silently.
1450
+
1451
+ /** Where the inspector's subject lives; `asset-lab` is an open asset
1452
+ * document — the three paradigm scoped to a subtree, inspected in the same
1453
+ * box as the scene. */
1454
+ export type InspectionSurface = 'three' | 'canvas' | 'dom' | 'asset-lab';
1455
+
1456
+ /** Which LAYOUT the one inspector box is in: the compact box over the
1457
+ * viewport, the same sections stacked in the dock column, or that column
1458
+ * with the sections tabbed behind a vertical rail (`properties`). */
1459
+ export type InspectionPresentationKind = 'card' | 'column' | 'properties';
1460
+
1461
+ /** One inspected field: a stable scriptable `path` and the value at it. */
1462
+ export interface InspectedField {
1463
+ path: string;
1464
+ label: string;
1465
+ type: 'string' | 'number' | 'boolean' | 'vec3' | 'color' | 'enum' | 'asset' | 'json';
1466
+ /** Absent when nothing is at that address, or when `mixed` is set. */
1467
+ value?: unknown;
1468
+ /** The inspected subjects disagree about this field. */
1469
+ mixed?: true;
1470
+ /** The value shown is the declared default — the document does not carry it. */
1471
+ defaulted?: boolean;
1472
+ readonly?: boolean;
1473
+ /** The same reason shown by the Inspector and returned by a refused write. */
1474
+ readonlyReason?: string;
1475
+ resettable?: boolean;
1476
+ revertsTo?: string;
1477
+ group?: string;
1478
+ options?: readonly unknown[];
1479
+ }
1480
+
1481
+ /** A section's content. Custom RENDERING remains opaque — the wire never
1482
+ * introspects React — while any ordinary descriptor channel that chrome owns
1483
+ * IS a `fields` body here, including its write-refusal reasons: nothing is
1484
+ * rendered on this wire, so naming chrome a reader cannot see while hiding
1485
+ * the fields it can use is the wrong half. (Measured: the whole react/DOM
1486
+ * lane draws its own widgets over the style descriptors, so every one of its
1487
+ * sections reported `custom` and `inspect()` enumerated zero fields for a DOM
1488
+ * element.) The two opaque kinds are distinguished because "this subject has
1489
+ * a live preview" is a real fact about it: `custom` is a contributed block
1490
+ * with no descriptor channel of its own, `preview` is the subject's own
1491
+ * square view of itself.
1492
+ *
1493
+ * A custom body carries `data` when it can say what it DISPLAYS — the keys
1494
+ * are the section's own vocabulary, not a shared schema. The shipped case is
1495
+ * `transform`: `{position, rotation, scale}`, three numbers each, with
1496
+ * rotation in Euler XYZ DEGREES exactly as the rotation inputs show it (the
1497
+ * quaternion behind them is not on this wire). */
1498
+ export type InspectedSectionBody =
1499
+ | { kind: 'fields'; fields: readonly InspectedField[] }
1500
+ | {
1501
+ kind: 'custom';
1502
+ id: string;
1503
+ title: string;
1504
+ data?: Record<string, unknown>;
1505
+ }
1506
+ | { kind: 'preview'; id: string; title: string };
1507
+
1508
+ export interface InspectedSection {
1509
+ id: string;
1510
+ title: string;
1511
+ order: number;
1512
+ description?: string;
1513
+ body: InspectedSectionBody;
1514
+ }
1515
+
1516
+ /** A verb on the subject (the visibility eye, the Asset Editor jump). */
1517
+ export interface InspectedAction {
1518
+ id: string;
1519
+ title: string;
1520
+ label?: string;
1521
+ /** Toggle state, for verbs that have one — how visibility is read. */
1522
+ pressed?: boolean;
1523
+ disabled?: boolean;
1524
+ }
1525
+
1526
+ export interface InspectedSubjectLink {
1527
+ id: string;
1528
+ title: string;
1529
+ }
1530
+
1531
+ /** The whole inspection subject, as data — what a human sees in the
1532
+ * inspector, for an agent (the editor's `eval 'editor.inspect()'` command). */
1533
+ export interface InspectedSubject {
1534
+ id: string;
1535
+ title: string;
1536
+ kindLabel?: string;
1537
+ /** The quiet line a subject with nothing to edit explains itself with. */
1538
+ hint?: string;
1539
+ presentation: {
1540
+ preferred: InspectionPresentationKind;
1541
+ resolved?: InspectionPresentationKind;
1542
+ surface?: InspectionSurface;
1543
+ };
1544
+ quickActions: readonly InspectedAction[];
1545
+ /** Agent-visible counterparts of the inspector's related-document buttons. */
1546
+ related: readonly InspectedSubjectLink[];
1547
+ /** Already in display order. */
1548
+ sections: readonly InspectedSection[];
1549
+ }
1550
+
1551
+ /** NOTHING is being inspected: the inspector is unmounted, so the honest
1552
+ * answer is not an empty subject but the absence of one. Distinct from a
1553
+ * missing reply, which means nobody answered
1554
+ * (`editor.inspection.get`'s `INSPECTION_UNAVAILABLE`). */
1555
+ export interface InspectedNothing {
1556
+ none: true;
1557
+ }
1558
+
1559
+ /** What `editor.inspect()` answers: the subject showing, or nothing at all.
1560
+ * Narrow with `'none' in result`. */
1561
+ export type InspectedInspection = InspectedSubject | InspectedNothing;
1562
+
1563
+ /**
1564
+ * WHERE THIS WRITE WENT — carried by every `editor.setField()` ack.
1565
+ *
1566
+ * A write with no persistence route still succeeds: it lands on the live
1567
+ * object and journals live-only, exactly as designed. Without this the ack was
1568
+ * indistinguishable from one that reached a file, so a caller could only find
1569
+ * out by diffing the tree — and a healthy consent-off session read as a silent
1570
+ * no-op. `persisted: false` with `destination: "live-only (not saved)"` is the
1571
+ * honest floor: never silence, and never a fabricated file name.
1572
+ *
1573
+ * IT IS PER-EDIT, produced by the component that performed the write and
1574
+ * returned through the editor's persistence pipe — never a property of the
1575
+ * session, the surface or the adapter. A composite holding a live-only three
1576
+ * root beside a source-backed DOM root has no single true answer, and the
1577
+ * adapter-wide one it used to give was the DOM root's (measured on the
1578
+ * vendored racing game: a three-root edit acked `persisted: true` against a
1579
+ * file it never touched). The ack is also AWAITED: it resolves after the bytes
1580
+ * have landed, so a caller holding it can diff the tree immediately.
1581
+ */
1582
+ export interface InspectedWriteDestination {
1583
+ /** Where THIS edit's bytes landed, in the writer's own words — a source
1584
+ * file, the game's own JSX, or a named non-target like
1585
+ * `"live-only (not saved)"`. */
1586
+ destination: string;
1587
+ /** Whether a byte actually moved for THIS edit. */
1588
+ persisted: boolean;
1589
+ }
1590
+
1591
+ /** What `editor.setField()` answers: the subject after the write, plus where
1592
+ * the write went. */
1593
+ export interface InspectedFieldWrite {
1594
+ subject: InspectedInspection;
1595
+ write: InspectedWriteDestination;
1596
+ }
1597
+
1598
+ /**
1599
+ * The STRUCTURE verbs — the hierarchy context menu's own ops, addressable.
1600
+ *
1601
+ * The names are the menu's, not the provider's, because the menu is the
1602
+ * surface a human uses and an agent is doing the same thing through a
1603
+ * different door (`delete` covers the provider's `remove`/`removeMany`: a
1604
+ * multi-id delete is ONE undoable op when the adapter can batch it).
1605
+ */
1606
+ export type StructureOp =
1607
+ | 'create'
1608
+ | 'delete'
1609
+ | 'duplicate'
1610
+ | 'reparent'
1611
+ | 'reorder'
1612
+ | 'wrap'
1613
+ | 'unwrap'
1614
+ | 'group'
1615
+ | 'ungroup'
1616
+ | 'copy'
1617
+ | 'cut'
1618
+ | 'paste';
1619
+
1620
+ /** Arguments for one {@link StructureOp}. Everything is optional: `id`/`ids`
1621
+ * default to the current selection, the menu's own subject. */
1622
+ export interface StructureOpOptions {
1623
+ id?: string;
1624
+ ids?: readonly string[];
1625
+ /** `create`: which creatable kind (see the adapter's `creatableKinds`). */
1626
+ kind?: string;
1627
+ /** `create`/`reparent`/`paste`: the destination; `null`/absent = document root. */
1628
+ parentId?: string;
1629
+ /** `reorder`: move immediately before this sibling; absent = to the end. */
1630
+ beforeSiblingId?: string;
1631
+ /** `wrap`: the wrapper tag; absent = the adapter's own default. */
1632
+ tag?: string;
1633
+ }
1634
+
1635
+ /** What one structure op answers. `write` is the same per-edit ack
1636
+ * `editor.setField()` carries — `persisted: false` means the tree moved and
1637
+ * no byte did. `id`/`ids` name what the op produced, when it produces one. */
1638
+ export interface StructureOpResult {
1639
+ id?: string | null;
1640
+ ids?: readonly string[];
1641
+ write?: InspectedWriteDestination;
1642
+ /** `copy` only: whether the clipboard actually took the payload. */
1643
+ copied?: boolean;
1644
+ }
1645
+
1646
+ // ---------------------------------------------------------------- hierarchy
1647
+ //
1648
+ // The wire mirror of the editor's own `SerializedHierarchyPanel`
1649
+ // (`packages/sdk/src/kit/hierarchy-panel-view.ts`, which owns the contract and
1650
+ // carries the reasoning). `command-listener.ts` annotates its `hierarchy`
1651
+ // payload with this type, so `tsc` checks the two sides against each other on
1652
+ // every build.
1653
+ //
1654
+ // This is NOT `EditorState.entities`: that facet is the raw adapter tree, with
1655
+ // no marks, no internals folding and no document promotion. This one is what
1656
+ // the hierarchy PANEL rendered — the rows a human is looking at.
1657
+
1658
+ /** One row of the hierarchy panel, as data. */
1659
+ export interface InspectedHierarchyRow {
1660
+ id: string;
1661
+ label: string;
1662
+ /** The dim type suffix the row prints (`Coin1 ·Coin`). */
1663
+ typeLabel?: string;
1664
+ role?: string;
1665
+ depth: number;
1666
+ /** Children the row's view has — what opening the caret reveals. Folded
1667
+ * implementation children are NOT counted here. */
1668
+ childCount: number;
1669
+ /** Children folded away as implementation, behind "Reveal Internals". */
1670
+ internalChildCount: number;
1671
+ /** Whether the panel renders a disclosure control. A row with children of
1672
+ * any kind and `expandable: false` is a subtree the UI cannot reach. */
1673
+ expandable: boolean;
1674
+ expanded?: boolean;
1675
+ internal?: true;
1676
+ componentRoot?: true;
1677
+ /** A synthetic "… N more" cap stub rather than a real node. */
1678
+ more?: { hidden: number };
1679
+ children?: readonly InspectedHierarchyRow[];
1680
+ }
1681
+
1682
+ /** What `editor.hierarchy()` answers. */
1683
+ export interface InspectedHierarchy {
1684
+ rowCount: number;
1685
+ /** The slice in the DOM; a smaller span than `rowCount` means the rest is
1686
+ * scrolled out, not absent. */
1687
+ window: { start: number; end: number };
1688
+ search?: string;
1689
+ scopeId?: string;
1690
+ playState: string;
1691
+ activeViewportTab: string;
1692
+ roots: readonly InspectedHierarchyRow[];
1693
+ }