@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
@@ -0,0 +1,811 @@
1
+ import {
2
+ faArrowDown,
3
+ faArrowsToDot,
4
+ type faArrowsUpDownLeftRight,
5
+ faBullseye,
6
+ faCaretDown,
7
+ faCheck,
8
+ faCircleDot,
9
+ faCrosshairs,
10
+ faCube,
11
+ faGlobe,
12
+ faMagnet,
13
+ } from '@fortawesome/free-solid-svg-icons';
14
+ import type { StageTransformDoor, StageTransformKind } from '@volter/sdk/contributions';
15
+ import {
16
+ AnchoredMenu,
17
+ Button,
18
+ Checkbox,
19
+ EditorIcon,
20
+ EditorPopover,
21
+ EditorToolbar,
22
+ editorIcons,
23
+ FloatingToolbar,
24
+ IconButton,
25
+ Inline,
26
+ Keycap,
27
+ MenuItem,
28
+ SplitButtonGroup,
29
+ Stack,
30
+ Text,
31
+ TextInput,
32
+ Tooltip,
33
+ } from '@volter/sdk/widgets';
34
+ import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
35
+ import { useEditorStore } from '../editor-runtime';
36
+ import type { GizmoAnchor, PivotMode } from '@volter/sdk/kit/shell-store';
37
+ import type { ShellStore } from '@volter/sdk/kit/shell-store';
38
+ import {
39
+ activeEditorKeymap,
40
+ type EditorKeyActionId,
41
+ shortcutFor,
42
+ subscribeEditorKeymap,
43
+ } from '@volter/sdk/kit/keymap-presets';
44
+ import { requestTransformMode } from '../transform-mode-request';
45
+
46
+ /** WHETHER IT IS LIT AND WHAT IT ARMS ARE THE STAGE'S ANSWER, not this
47
+ * button's: the gizmo arm reads `store.transformMode`, a modal door reads its
48
+ * own `armed()` (see `stageTransformDriver` in `stage-context.ts`). */
49
+ function ModeButton({
50
+ active,
51
+ onArm,
52
+ faIcon,
53
+ action,
54
+ label,
55
+ }: {
56
+ active: boolean;
57
+ onArm: () => void;
58
+ faIcon: typeof faArrowsUpDownLeftRight;
59
+ /** The LOGICAL action — its printed key comes from the active keymap. */
60
+ action: EditorKeyActionId;
61
+ label: string;
62
+ }) {
63
+ return (
64
+ <Tooltip text={label} hotkey={shortcutFor(action)}>
65
+ <IconButton
66
+ aria-label={label}
67
+ aria-pressed={active}
68
+ shape="segment"
69
+ size="comfortable"
70
+ onClick={onArm}
71
+ >
72
+ <EditorIcon icon={faIcon} size="md" />
73
+ </IconButton>
74
+ </Tooltip>
75
+ );
76
+ }
77
+
78
+ function SnapButton({
79
+ store,
80
+ dimensions = '3d',
81
+ size = 'comfortable',
82
+ variant = 'ghost',
83
+ showValues = false,
84
+ }: {
85
+ store: ShellStore;
86
+ dimensions?: '2d' | '3d';
87
+ size?: 'compact' | 'default' | 'comfortable';
88
+ variant?: 'ghost' | 'secondary';
89
+ /** Draw the three steps themselves as the settings' trigger (Unreal's viewport row: the grid
90
+ * step, the angle and the scale step), each opening the same settings. */
91
+ showValues?: boolean;
92
+ }) {
93
+ const [popoverOpen, setPopoverOpen] = useState(false);
94
+ const ref = useRef<HTMLDivElement>(null);
95
+
96
+ useEffect(() => {
97
+ if (!popoverOpen) return;
98
+ const handler = (event: MouseEvent) => {
99
+ if (ref.current && !ref.current.contains(event.target as Node)) setPopoverOpen(false);
100
+ };
101
+ document.addEventListener('mousedown', handler);
102
+ return () => document.removeEventListener('mousedown', handler);
103
+ }, [popoverOpen]);
104
+
105
+ return (
106
+ <div ref={ref} className="volter-viewport-popover-anchor">
107
+ <SplitButtonGroup>
108
+ {dimensions === '2d' && (
109
+ // Godot's 2D toolbar has two snap toggles: smart snapping (alignment to the parent,
110
+ // other nodes and guides) and grid snapping (the step). This is the first.
111
+ <Tooltip text="Smart Snap">
112
+ <IconButton
113
+ aria-label="Toggle smart snap"
114
+ aria-pressed={store.smartSnap.enabled}
115
+ variant={variant}
116
+ size={size}
117
+ onClick={() => store.setSmartSnap({ enabled: !store.smartSnap.enabled })}
118
+ >
119
+ <EditorIcon icon={faArrowsToDot} size="md" />
120
+ </IconButton>
121
+ </Tooltip>
122
+ )}
123
+ <Tooltip
124
+ text={dimensions === '2d' ? 'Grid Snap' : 'Toggle Snap'}
125
+ hotkey={shortcutFor('viewport.toggleSnap')}
126
+ >
127
+ <IconButton
128
+ aria-label="Toggle snap"
129
+ aria-pressed={store.snapEnabled}
130
+ variant={variant}
131
+ size={size}
132
+ onClick={() => store.toggleSnap()}
133
+ >
134
+ <EditorIcon icon={faMagnet} size="md" />
135
+ </IconButton>
136
+ </Tooltip>
137
+ {showValues ? (
138
+ (
139
+ [
140
+ ['translate', editorIcons.tool.move, `${store.snapValues.translate}`],
141
+ ['rotate', editorIcons.tool.rotate, `${store.snapValues.rotate}°`],
142
+ ['scale', editorIcons.tool.scale, `${store.snapValues.scale}`],
143
+ ] as const
144
+ ).map(([channel, icon, value]) => (
145
+ <Tooltip key={channel} text="Snap Settings">
146
+ <Button
147
+ aria-label={`${channel} snap step`}
148
+ aria-haspopup="dialog"
149
+ aria-expanded={popoverOpen}
150
+ variant={variant}
151
+ size={size}
152
+ data-snap-step={channel}
153
+ onClick={() => setPopoverOpen(!popoverOpen)}
154
+ >
155
+ <EditorIcon icon={icon} size="xs" />
156
+ {value}
157
+ </Button>
158
+ </Tooltip>
159
+ ))
160
+ ) : (
161
+ <Tooltip text="Snap Settings">
162
+ <IconButton
163
+ aria-label="Snap settings"
164
+ // A trigger that opens an anchored SETTINGS popover is a menu
165
+ // button, and `theme.css` reads that off the ARIA rather than a
166
+ // marker attribute — the same honest `dialog` the `Studio v`
167
+ // trigger declares. Blender's snap group is exactly this two-tone
168
+ // split: the magnet half in the pushbutton colour, this half in
169
+ // the menu well (measured native 2x `modeling.png` y=62, the
170
+ // toggle x 1411..1445 at #535353 and the dropdown x 1448..1507 at
171
+ // #272727, one 1 CSS px #3c3c3c divider between them).
172
+ aria-haspopup="dialog"
173
+ aria-expanded={popoverOpen}
174
+ variant={variant}
175
+ size={size}
176
+ onClick={() => setPopoverOpen(!popoverOpen)}
177
+ >
178
+ <EditorIcon icon={faCaretDown} size="xs" />
179
+ </IconButton>
180
+ </Tooltip>
181
+ )}
182
+ </SplitButtonGroup>
183
+ {popoverOpen && (
184
+ <EditorPopover className="volter-snap-popover">
185
+ <Stack gap={2}>
186
+ {dimensions === '2d' ? (
187
+ // Godot's Configure Snap: the grid's step and offset, in pixels.
188
+ <>
189
+ <Text variant="caption" tone="muted">
190
+ Grid Step (px)
191
+ </Text>
192
+ <TextInput
193
+ type="number"
194
+ aria-label="Grid step"
195
+ value={store.snap2D.step}
196
+ step={1}
197
+ min={1}
198
+ onChange={(event) => store.setSnap2D({ step: Number(event.target.value) || 8 })}
199
+ />
200
+ <Text variant="caption" tone="muted">
201
+ Grid Offset (px)
202
+ </Text>
203
+ <Inline gap={1}>
204
+ <TextInput
205
+ type="number"
206
+ aria-label="Grid offset x"
207
+ value={store.snap2D.offsetX}
208
+ step={1}
209
+ onChange={(event) => store.setSnap2D({ offsetX: Number(event.target.value) || 0 })}
210
+ />
211
+ <TextInput
212
+ type="number"
213
+ aria-label="Grid offset y"
214
+ value={store.snap2D.offsetY}
215
+ step={1}
216
+ onChange={(event) => store.setSnap2D({ offsetY: Number(event.target.value) || 0 })}
217
+ />
218
+ </Inline>
219
+ <Text variant="caption" tone="muted">
220
+ Primary Line Every (steps)
221
+ </Text>
222
+ <TextInput
223
+ type="number"
224
+ aria-label="Primary line every"
225
+ value={store.snap2D.primaryEvery}
226
+ step={1}
227
+ min={1}
228
+ onChange={(event) => store.setSnap2D({ primaryEvery: Number(event.target.value) || 8 })}
229
+ />
230
+ </>
231
+ ) : (
232
+ <>
233
+ <Text variant="caption" tone="muted">
234
+ Translate
235
+ </Text>
236
+ <TextInput
237
+ type="number"
238
+ value={store.snapValues.translate}
239
+ step={0.25}
240
+ min={0.01}
241
+ onChange={(event) =>
242
+ store.setSnapValues({ translate: Number(event.target.value) || 1 })
243
+ }
244
+ />
245
+ </>
246
+ )}
247
+ {dimensions === '2d' ? (
248
+ // Godot's Configure Snap for a 2D view: its own rotation offset and step, and scale step.
249
+ <>
250
+ <Text variant="caption" tone="muted">
251
+ Rotation Offset (deg)
252
+ </Text>
253
+ <TextInput
254
+ type="number"
255
+ aria-label="Rotation offset"
256
+ value={store.snap2D.rotationOffset}
257
+ step={1}
258
+ onChange={(event) => store.setSnap2D({ rotationOffset: Number(event.target.value) || 0 })}
259
+ />
260
+ <Text variant="caption" tone="muted">
261
+ Rotation Step (deg)
262
+ </Text>
263
+ <TextInput
264
+ type="number"
265
+ aria-label="Rotation step"
266
+ value={store.snap2D.rotationStep}
267
+ step={1}
268
+ min={1}
269
+ onChange={(event) => store.setSnap2D({ rotationStep: Number(event.target.value) || 15 })}
270
+ />
271
+ <Text variant="caption" tone="muted">
272
+ Scale Step
273
+ </Text>
274
+ <TextInput
275
+ type="number"
276
+ aria-label="Scale step"
277
+ value={store.snap2D.scaleStep}
278
+ step={0.05}
279
+ min={0.01}
280
+ onChange={(event) => store.setSnap2D({ scaleStep: Number(event.target.value) || 0.1 })}
281
+ />
282
+ </>
283
+ ) : (
284
+ <>
285
+ <Text variant="caption" tone="muted">
286
+ Rotate (deg)
287
+ </Text>
288
+ <TextInput
289
+ type="number"
290
+ value={store.snapValues.rotate}
291
+ step={5}
292
+ min={1}
293
+ onChange={(event) =>
294
+ store.setSnapValues({ rotate: Number(event.target.value) || 15 })
295
+ }
296
+ />
297
+ <Text variant="caption" tone="muted">
298
+ Scale
299
+ </Text>
300
+ <TextInput
301
+ type="number"
302
+ value={store.snapValues.scale}
303
+ step={0.05}
304
+ min={0.01}
305
+ onChange={(event) =>
306
+ store.setSnapValues({ scale: Number(event.target.value) || 0.25 })
307
+ }
308
+ />
309
+ </>
310
+ )}
311
+ {dimensions === '2d' && (
312
+ <>
313
+ {(
314
+ [
315
+ ['Use Rotation Snap', store.rotationSnap, () => store.setRotationSnap(!store.rotationSnap)],
316
+ ['Use Scale Snap', store.scaleSnap, () => store.setScaleSnap(!store.scaleSnap)],
317
+ ['Snap Relative', store.snap2D.relative, () => store.setSnap2D({ relative: !store.snap2D.relative })],
318
+ ['Use Pixel Snap', store.snap2D.pixel, () => store.setSnap2D({ pixel: !store.snap2D.pixel })],
319
+ ] as const
320
+ ).map(([label, on, toggle]) => (
321
+ <Inline key={label} gap={2} align="center" role="menuitemcheckbox" aria-checked={on} onClick={toggle}>
322
+ <Checkbox checked={on} readOnly />
323
+ <Text>{label}</Text>
324
+ </Inline>
325
+ ))}
326
+ <Text variant="caption" tone="muted">
327
+ Smart Snapping
328
+ </Text>
329
+ {(
330
+ [
331
+ ['parent', 'Snap to Parent'],
332
+ ['others', 'Snap to Other Nodes'],
333
+ ['sides', 'Snap to Node Sides'],
334
+ ['center', 'Snap to Node Center'],
335
+ ['guides', 'Snap to Guides'],
336
+ ] as const
337
+ ).map(([target, label]) => (
338
+ <Inline
339
+ key={target}
340
+ gap={2}
341
+ align="center"
342
+ role="menuitemcheckbox"
343
+ aria-checked={store.smartSnap[target]}
344
+ onClick={() => store.setSmartSnap({ [target]: !store.smartSnap[target] })}
345
+ >
346
+ <Checkbox checked={store.smartSnap[target]} readOnly />
347
+ <Text>{label}</Text>
348
+ </Inline>
349
+ ))}
350
+ </>
351
+ )}
352
+ {dimensions === '3d' && (
353
+ <>
354
+ <Inline gap={2} align="center" onClick={() => store.toggleSnapToSurface()}>
355
+ <Checkbox checked={store.snapToSurface} readOnly />
356
+ <Text>Snap to Surface</Text>
357
+ </Inline>
358
+ <Text variant="caption" tone="dim">
359
+ Hold <Keycap>Ctrl</Keycap> while dragging to snap once ·{' '}
360
+ <Keycap>{shortcutFor('viewport.toggleSnap')}</Keycap> toggles
361
+ </Text>
362
+ <Text variant="caption" tone="dim">
363
+ Hold <Keycap>{shortcutFor('viewport.vertexSnapHold')}</Keycap> during translate
364
+ for vertex snap
365
+ </Text>
366
+ </>
367
+ )}
368
+ </Stack>
369
+ </EditorPopover>
370
+ )}
371
+ </div>
372
+ );
373
+ }
374
+
375
+ const pivotLabels: Record<PivotMode, string> = {
376
+ 'active-element': 'Active Element',
377
+ 'median-point': 'Median Point',
378
+ 'individual-origins': 'Individual Origins',
379
+ };
380
+
381
+ function PivotButton({ store }: { store: ShellStore }) {
382
+ const modes: PivotMode[] = ['active-element', 'median-point', 'individual-origins'];
383
+ const label = `Pivot: ${pivotLabels[store.pivotMode]}`;
384
+ return (
385
+ <Tooltip text={label} hotkey={shortcutFor('viewport.cyclePivot')}>
386
+ <IconButton
387
+ aria-label={label}
388
+ aria-pressed={store.pivotMode !== 'active-element'}
389
+ variant="secondary"
390
+ onClick={() => {
391
+ const index = modes.indexOf(store.pivotMode);
392
+ store.setPivotMode(modes[(index + 1) % modes.length]!);
393
+ }}
394
+ >
395
+ <EditorIcon icon={faCrosshairs} size="md" />
396
+ </IconButton>
397
+ </Tooltip>
398
+ );
399
+ }
400
+
401
+ const anchorLabels: Record<GizmoAnchor, string> = {
402
+ auto: 'Auto',
403
+ pivot: 'Pivot',
404
+ center: 'Center',
405
+ };
406
+
407
+ /**
408
+ * WHERE the gizmo is drawn — the standard DCC pivot/centre affordance, cycled
409
+ * the way its `PivotButton` neighbour cycles its own three states.
410
+ *
411
+ * `Auto` leads because the right answer is a property of the SUBJECT: an
412
+ * ordinary node's pivot already sits in its content and nothing moves, while a
413
+ * world-anchored instanced system's sits at (0,0,0) with everything visible
414
+ * somewhere else (`instanced-presentation.ts`). The two explicit states are the
415
+ * override, in both directions.
416
+ */
417
+ function AnchorButton({ store }: { store: ShellStore }) {
418
+ const modes: GizmoAnchor[] = ['auto', 'pivot', 'center'];
419
+ const label = `Anchor: ${anchorLabels[store.gizmoAnchor]}`;
420
+ return (
421
+ <Tooltip text={label}>
422
+ <IconButton
423
+ aria-label={label}
424
+ aria-pressed={store.gizmoAnchor !== 'auto'}
425
+ variant="secondary"
426
+ onClick={() => {
427
+ const index = modes.indexOf(store.gizmoAnchor);
428
+ store.setGizmoAnchor(modes[(index + 1) % modes.length]!);
429
+ }}
430
+ >
431
+ <EditorIcon icon={store.gizmoAnchor === 'center' ? faCircleDot : faBullseye} size="md" />
432
+ </IconButton>
433
+ </Tooltip>
434
+ );
435
+ }
436
+
437
+ function TransformOptionsButton({ store }: { store: ShellStore }) {
438
+ const [open, setOpen] = useState(false);
439
+ const ref = useRef<HTMLDivElement>(null);
440
+ useEffect(() => {
441
+ if (!open) return;
442
+ const close = (event: MouseEvent) => {
443
+ if (!ref.current?.contains(event.target as Node)) setOpen(false);
444
+ };
445
+ document.addEventListener('mousedown', close);
446
+ return () => document.removeEventListener('mousedown', close);
447
+ }, [open]);
448
+ return (
449
+ <div ref={ref} className="volter-viewport-popover-anchor">
450
+ <Tooltip text="Transform options">
451
+ <IconButton
452
+ aria-label="Transform options"
453
+ // Same reading as the snap group's dropdown half: an anchored
454
+ // settings popover is a menu button, and this is where Blender's
455
+ // proportional-editing dropdown sits in the row.
456
+ aria-haspopup="dialog"
457
+ aria-expanded={open}
458
+ variant="secondary"
459
+ onClick={() => setOpen((value) => !value)}
460
+ >
461
+ <EditorIcon icon={faArrowDown} size="md" />
462
+ </IconButton>
463
+ </Tooltip>
464
+ {open ? (
465
+ <EditorPopover style={{ minWidth: 240 }}>
466
+ <Stack gap={3}>
467
+ <Button
468
+ size="compact"
469
+ variant="ghost"
470
+ onClick={() => {
471
+ store.snapSelectionToFloor();
472
+ setOpen(false);
473
+ }}
474
+ >
475
+ Snap Selection to Floor <Keycap>{shortcutFor('viewport.snapToFloor')}</Keycap>
476
+ </Button>
477
+ <Inline
478
+ gap={2}
479
+ align="center"
480
+ onClick={() => store.togglePreserveChildrenTransform()}
481
+ style={{ cursor: 'pointer' }}
482
+ >
483
+ <Checkbox checked={store.preserveChildrenTransform} readOnly />
484
+ <Text>Preserve Children Transform</Text>
485
+ </Inline>
486
+ <Text variant="caption" tone="dim">
487
+ Keep source-writable children fixed in world space while their parent moves.
488
+ </Text>
489
+ </Stack>
490
+ </EditorPopover>
491
+ ) : null}
492
+ </div>
493
+ );
494
+ }
495
+
496
+ /**
497
+ * THE TRANSFORM ORIENTATION control, transcribed from the frame rather than
498
+ * inferred: Blender's is a LABELLED DROPDOWN, not a cycling toggle. Measured on
499
+ * the native 2x `modeling.png` at row y=70 — the well runs x 1170..1323 (a
500
+ * 1 px #3c3c3c border at each end, #272727 fill between), i.e. 77 CSS px wide,
501
+ * because it carries an orientation glyph, the word `Global`, and a chevron at
502
+ * x 1304..1316 (13x8 device px, 6.5x4 CSS, peak 216). Ours was a 20 px
503
+ * icon-only button that cycled on click, which is also why the widget-colour
504
+ * rule painted it the #535353 pushbutton where the frame shows the menu well:
505
+ * the colour was agreeing with what the control DID.
506
+ *
507
+ * The STORE's vocabulary is unchanged and stays three.js's (`world`/`local`);
508
+ * only the printed word is the reference's, because `Global` is what this
509
+ * control is called in the frame this skew is transcribing.
510
+ */
511
+ function TransformOrientationButton({ store }: { store: ShellStore }) {
512
+ const [open, setOpen] = useState(false);
513
+ const triggerRef = useRef<HTMLButtonElement>(null);
514
+ const label = store.transformSpace === 'world' ? 'Global' : 'Local';
515
+ return (
516
+ <div className="volter-viewport-popover-anchor">
517
+ <Tooltip text={`Transform orientation: ${label}`}>
518
+ <Button
519
+ ref={triggerRef}
520
+ type="button"
521
+ size="compact"
522
+ variant="secondary"
523
+ aria-label={`Transform orientation: ${label}`}
524
+ aria-haspopup="menu"
525
+ aria-expanded={open}
526
+ onClick={() => setOpen((value) => !value)}
527
+ className="volter-transform-orientation-trigger"
528
+ >
529
+ <EditorIcon icon={store.transformSpace === 'world' ? faGlobe : faCube} size="sm" />
530
+ <span>{label}</span>
531
+ <EditorIcon icon={faCaretDown} size="xs" />
532
+ </Button>
533
+ </Tooltip>
534
+ {open && (
535
+ <AnchoredMenu
536
+ anchorRef={triggerRef}
537
+ align="start"
538
+ clamp
539
+ aria-label="Transform orientation"
540
+ onDismiss={() => setOpen(false)}
541
+ >
542
+ {(['world', 'local'] as const).map((space) => (
543
+ <MenuItem
544
+ key={space}
545
+ role="menuitemradio"
546
+ aria-checked={store.transformSpace === space}
547
+ onSelect={() => {
548
+ store.setTransformSpace(space);
549
+ setOpen(false);
550
+ }}
551
+ >
552
+ <span className="volter-menu-check">
553
+ {store.transformSpace === space && <EditorIcon icon={faCheck} size="xs" />}
554
+ </span>
555
+ {space === 'world' ? 'Global' : 'Local'}
556
+ </MenuItem>
557
+ ))}
558
+ </AnchoredMenu>
559
+ )}
560
+ </div>
561
+ );
562
+ }
563
+
564
+ /**
565
+ * THE VIEWPORT HEADER'S TRANSFORM CONTROLS — orientation, pivot, snap and the
566
+ * transform options, drawn in the document's header beside the host's 3D
567
+ * controls (`DocumentHeaderStrip`).
568
+ *
569
+ * WHY HERE AND NOT ON THE SHELF: a control is placed by the panel that owns its
570
+ * job in the reference. In Blender's 3D viewport these four are HEADER
571
+ * controls — transform orientation, transform pivot point, snapping with its
572
+ * dropdown, proportional editing — and the tool shelf (T) holds tools only.
573
+ * They rode the `ToolStrip` here until 2026-09-18, and the magnet is what
574
+ * showed it: a horizontal `SplitButtonGroup` inside a rail one tool wide spent
575
+ * two boxes and hung a half-width caret off the column.
576
+ *
577
+ * THE GROUPING IS TRANSCRIBED, measured on the native 2x `modeling.png`
578
+ * (row y=62, x in device px): Blender lays four separate WELLS with 12 device
579
+ * px (6 CSS px) of bare header between them — orientation x 1171..1322 (76 CSS
580
+ * px, it carries the "Global" label), pivot x 1335..1396 (31), snap
581
+ * x 1409..1510 (51), proportional editing x 1523..1624 (51). The last two are
582
+ * SPLIT: a toggle segment in the pushbutton colour (#535353) then a one-pixel
583
+ * #3c3c3c divider then an icon+caret dropdown in the menu-well colour
584
+ * (#272727) — exactly the `SplitButtonGroup` shape the magnet already had, in
585
+ * the row where it lays out. Each well is 40 device px tall (20 CSS px, the
586
+ * density's control height) inside a 54-device-px header band.
587
+ *
588
+ * Each control is `secondary`, so the estate's widget-colour rule paints it:
589
+ * a toggle takes the pushbutton class and a popover trigger the menu well,
590
+ * read off the trigger's own ARIA (`theme.css`, "A MENU BUTTON"). They drew as
591
+ * ghost icons floating on the header band before that — measured beside the
592
+ * frame at 4x, every one of Blender's carries a well.
593
+ *
594
+ * Three readings where ours differs from the frame, kept as they are because
595
+ * this unit moves placement and not shape: Blender's orientation is a labelled
596
+ * dropdown (`Global v`) where ours cycles world/local on click — so ours takes
597
+ * the pushbutton #535353 where the frame shows the #272727 menu well, which is
598
+ * the colour rule agreeing with what the control DOES; its pivot is one
599
+ * dropdown where we carry two cycling toggles — pivot MODE and gizmo ANCHOR,
600
+ * sharing one well because they are one job (where the gizmo sits), which is
601
+ * how Blender groups them; and our toggles are 20x20 against the frame's 31
602
+ * for pivot, because a cycling toggle carries no caret.
603
+ */
604
+ export function TransformHeaderControls({ store: stage }: { store?: ShellStore } = {}) {
605
+ // THE STAGE'S OWN STORE, when the caller knows it. Every stage owns an
606
+ // `EditorShellStore` (`stage-store-registry.ts`) and these four configure
607
+ // the gizmo in THAT stage's viewport, so writing the shell's from over a
608
+ // document stage set a value nothing on screen reads — MEASURED 2026-09-19
609
+ // on a prefab story: with the shell at `rotate`, the story's stage kept
610
+ // drawing the default COMBINED gizmo, and two captures a mode apart were
611
+ // byte-identical. The world root's stage runs on the shell store itself, so
612
+ // the Scene document is unchanged either way.
613
+ const shell = useEditorStore();
614
+ const store = stage ?? shell;
615
+ useSyncExternalStore(store.subscribe, store.getShellSnapshot ?? store.getSnapshot);
616
+ useSyncExternalStore(subscribeEditorKeymap, activeEditorKeymap, activeEditorKeymap);
617
+ // ON THE STAGE'S BAR (Unreal's row) the snap steps are shown, as Unreal's row shows them. The
618
+ // host decides whether these controls ride the bar (a placed stage whose look puts them there),
619
+ // so where they are mounted is the answer, not the look alone.
620
+ const [mark, setMark] = useState<HTMLSpanElement | null>(null);
621
+ const onBar = mark?.closest('.volter-stage-bar') != null;
622
+ return (
623
+ <EditorToolbar
624
+ compact
625
+ label="Transform controls"
626
+ data-testid="transform-header-controls"
627
+ className="volter-transform-header-controls"
628
+ >
629
+ <span ref={setMark} hidden />
630
+ <TransformOrientationButton store={store} />
631
+ <SplitButtonGroup>
632
+ <PivotButton store={store} />
633
+ <AnchorButton store={store} />
634
+ </SplitButtonGroup>
635
+ <SnapButton store={store} size="default" variant="secondary" showValues={onBar} />
636
+ <TransformOptionsButton store={store} />
637
+ </EditorToolbar>
638
+ );
639
+ }
640
+
641
+ /**
642
+ * THE TOOL SHELF'S STRIP — tools, and nothing else (Blender's T shelf). The
643
+ * four header controls that used to trail it live in
644
+ * {@link TransformHeaderControls} now; the 2D canvas surface keeps its snap
645
+ * control inline because this strip lays out as a ROW over that stage (see
646
+ * `.volter-viewport-toolbar-left` in `workspace-dock.css`) and that surface has
647
+ * no Blender analogue to place it against.
648
+ *
649
+ * THESE FOUR ARE BLENDER'S OWN SECOND TOOLBAR GROUP, and the order is its
650
+ * order. Measured 2026-09-19 on `modeling-edit-none.png`: the Edit Mode
651
+ * toolbar's 21 buttons run in five groups of 2 / 4 / 2 / 1 / 12, and group 2
652
+ * is exactly Move, Rotate, Scale, Transform — the same four verbs this strip
653
+ * has always carried, under Font Awesome drawings and with the combined tool
654
+ * FIRST. So the answer to "do the host's navigation buttons belong at the top
655
+ * of a Blender toolbar at all" is yes and they were already right; what was
656
+ * wrong was the order (Transform leads in Blender's list only as the LAST
657
+ * member, the all-handles fallback after the three single-channel tools) and
658
+ * two of the glyphs (`editorIcons.tool`'s docblock says which and why).
659
+ * BLENDER'S GROUP 1 IS SELECT BOX AND THE 3D CURSOR, and as of 2026-09-21 the
660
+ * FIRST of the two is drawn above them while the second is still not. The
661
+ * earlier note here said neither was, and its reason was right at the time:
662
+ * neither had anything behind it once the Edit Mesh document was deleted (the
663
+ * 21-row table that said what each of the twenty-one does is in git,
664
+ * `packages/mesh/contributions/mesh-edit-document.tsx` before 2026-09-19).
665
+ * Select Box has something behind it NOW — `TransformMode`'s `'select'`, the
666
+ * state in which no transform gizmo is drawn at all, which is what Blender's
667
+ * viewport opens in (`gizmo-select-box.png`; the Blender stage's starting presentation states
668
+ * it, `interaction.bootTool`). The 3D cursor now exists — `Scene.cursor`, drawn
669
+ * and placed with Blender's Shift+Right-click on the Model document
670
+ * (`@volter/blender-engine`'s `blender-runtime-cursor.ts`) — but its toolbar
671
+ * tool, which places it with the LEFT button, is a transform mode this kit does
672
+ * not have, and a button without one would be the inert control the paragraph
673
+ * below removes.
674
+ *
675
+ * WHAT THEY DRIVE IS THE STAGE'S ANSWER, in two parts.
676
+ *
677
+ * `store` — WHICH STAGE'S GIZMO. Every stage owns an `EditorShellStore`
678
+ * (`stage-store-registry.ts`), and these four write `transformMode`, which
679
+ * that stage's `EditorViewport` reads. Writing the SHELL's from over a
680
+ * document stage set a value nothing on screen read: MEASURED 2026-09-19 on
681
+ * a prefab story with its subject selected, the shell store at `rotate`, the
682
+ * stage still drawing the default COMBINED gizmo, and two `capture-active-
683
+ * document` frames a mode apart BYTE-IDENTICAL. That, not a missing gizmo,
684
+ * is why these four were inert over a document stage. The world root's stage
685
+ * runs on the shell store itself, so the Scene document is unchanged.
686
+ *
687
+ * `door` — a stage that transforms MODALLY instead (a Model document's
688
+ * `G`/`R`/`S`, `registerStageTransform` in `@volter/sdk/contributions`,
689
+ * whose note carries that measurement). Then the three single-channel tools
690
+ * arm it and the all-handles tool is NOT DRAWN: Blender's fourth is a gizmo
691
+ * and a modal transform has no twin for it, so drawing it would be the inert
692
+ * control this seam exists to remove. `stageTransformDriver`
693
+ * (`stage-context.ts`) is what chooses between the two.
694
+ */
695
+ const NO_DOOR_SUBSCRIBE = () => () => {};
696
+ const NO_DOOR_ARMED = () => null;
697
+
698
+ export function ToolStrip({
699
+ dimensions = '3d',
700
+ door,
701
+ store: stage,
702
+ otherToolActive = false,
703
+ onToolArmed,
704
+ viewTools,
705
+ trailingTools,
706
+ }: {
707
+ dimensions?: '2d' | '3d';
708
+ door?: StageTransformDoor;
709
+ store?: ShellStore;
710
+ /** Another tool of the same exclusive group is on (a 2D view's List Select, Pivot, Pan or
711
+ * Ruler, which Godot keeps in one radio group with these), so none of these is lit. */
712
+ otherToolActive?: boolean;
713
+ /** Called when one of these is armed, so the group's other tools can let go. */
714
+ onToolArmed?: () => void;
715
+ /** A view's own tools in the same group, drawn after these (a 2D view's List Select, Pivot,
716
+ * Pan and Ruler, in Godot's order). */
717
+ viewTools?: React.ReactNode;
718
+ /** A view's controls after snapping in the same row (a 2D view's Lock, Group and View menu, where
719
+ * Godot's 2D toolbar keeps them). */
720
+ trailingTools?: React.ReactNode;
721
+ } = {}) {
722
+ const shell = useEditorStore();
723
+ const store = stage ?? shell;
724
+ useSyncExternalStore(store.subscribe, store.getShellSnapshot ?? store.getSnapshot);
725
+ // Every hint below is rendered from the active keymap; a live switch must
726
+ // repaint the strip rather than leave it advertising the other table.
727
+ useSyncExternalStore(subscribeEditorKeymap, activeEditorKeymap, activeEditorKeymap);
728
+ // A modal transform's lit state is the DOOR's, and it changes without the
729
+ // shell store moving at all — the same `useSyncExternalStore` pair every
730
+ // out-of-tree panel uses, with the mesh document's own null-session shape
731
+ // for the gizmo arm (hooks cannot be conditional).
732
+ const armed = useSyncExternalStore(
733
+ door?.subscribe ?? NO_DOOR_SUBSCRIBE,
734
+ door?.armed ?? NO_DOOR_ARMED,
735
+ door?.armed ?? NO_DOOR_ARMED,
736
+ );
737
+ const single = [
738
+ {
739
+ mode: 'translate',
740
+ faIcon: editorIcons.tool.move,
741
+ action: 'transform.translate',
742
+ label: 'Move',
743
+ },
744
+ {
745
+ mode: 'rotate',
746
+ faIcon: editorIcons.tool.rotate,
747
+ action: 'transform.rotate',
748
+ label: 'Rotate',
749
+ },
750
+ { mode: 'scale', faIcon: editorIcons.tool.scale, action: 'transform.scale', label: 'Scale' },
751
+ ] as const satisfies readonly {
752
+ mode: StageTransformKind;
753
+ faIcon: typeof faArrowsUpDownLeftRight;
754
+ action: EditorKeyActionId;
755
+ label: string;
756
+ }[];
757
+ return (
758
+ <FloatingToolbar
759
+ label="Transform tools"
760
+ data-testid={dimensions === '2d' ? 'canvas-2d-toolstrip' : 'threejs-toolstrip'}
761
+ className="volter-viewport-toolbar volter-viewport-toolbar-left"
762
+ >
763
+ {/* SELECT BOX, first — Blender's own group 1 (see the docblock). It is
764
+ not offered over a MODAL door: `door.begin` takes a transform kind
765
+ and a modal transform has no "arm nothing" member, so the button
766
+ would have nothing to call. */}
767
+ {door ? null : (
768
+ <ModeButton
769
+ faIcon={editorIcons.tool.select}
770
+ action="transform.select"
771
+ label="Select"
772
+ // On a 2D surface Select is the handle mode, which the store names `combined`.
773
+ active={
774
+ !otherToolActive &&
775
+ (store.transformMode === 'select' || (dimensions === '2d' && store.transformMode === 'combined'))
776
+ }
777
+ onArm={() => {
778
+ onToolArmed?.();
779
+ requestTransformMode(store, 'select');
780
+ }}
781
+ />
782
+ )}
783
+ {single.map((tool) => (
784
+ <ModeButton
785
+ key={tool.mode}
786
+ faIcon={tool.faIcon}
787
+ action={tool.action}
788
+ label={tool.label}
789
+ active={!otherToolActive && (door ? armed === tool.mode : store.transformMode === tool.mode)}
790
+ onArm={() => {
791
+ onToolArmed?.();
792
+ if (door) door.begin(tool.mode);
793
+ else requestTransformMode(store, tool.mode);
794
+ }}
795
+ />
796
+ ))}
797
+ {door || dimensions === '2d' ? null : (
798
+ <ModeButton
799
+ faIcon={editorIcons.tool.transform}
800
+ action="transform.combined"
801
+ label="Transform (all handles)"
802
+ active={store.transformMode === 'combined'}
803
+ onArm={() => requestTransformMode(store, 'combined')}
804
+ />
805
+ )}
806
+ {viewTools}
807
+ {dimensions === '2d' ? <SnapButton store={store} dimensions="2d" /> : null}
808
+ {trailingTools}
809
+ </FloatingToolbar>
810
+ );
811
+ }