@volter/sdk 0.0.0-stage → 0.5.204

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 +1168 -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 +329 -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 +669 -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,328 @@
1
+ import {
2
+ Divider,
3
+ EditorIcon,
4
+ editorIcons,
5
+ Inline,
6
+ Menu,
7
+ MenuItem,
8
+ MenuSeparator,
9
+ MenuTrigger,
10
+ Text,
11
+ } from '@volter/sdk/widgets';
12
+ import {
13
+ type ReactNode,
14
+ useEffect,
15
+ useReducer,
16
+ useRef,
17
+ useState,
18
+ useSyncExternalStore,
19
+ } from 'react';
20
+ import { activeProduct } from '@volter/sdk/kit/active-product';
21
+ import { BUNDLED_EDITOR_VERSION } from '../build-identity';
22
+ import {
23
+ contributedChromeVersion,
24
+ contributedHeaderItems,
25
+ subscribeContributedChrome,
26
+ } from '@volter/sdk/kit/chrome-registry';
27
+ import { revealInFinder } from '@volter/sdk/kit/editor-api';
28
+ import { getCurrentProject, onProjectChange } from '@volter/sdk/kit/project-manager';
29
+ import {
30
+ activeEditorWorkspace,
31
+ editorWorkspaces,
32
+ editorWorkspacesVersion,
33
+ setEditorWorkspace,
34
+ subscribeEditorWorkspace,
35
+ subscribeEditorWorkspaces,
36
+ workspaceApplies,
37
+ } from '@volter/sdk/kit/workspace-presets';
38
+ import {
39
+ activeChromeRegions,
40
+ chromeRegionsVersion,
41
+ subscribeChromeRegions,
42
+ } from '@volter/sdk/kit/workspace-regions';
43
+ import { ApplicationMenus } from './ApplicationMenus';
44
+ import { ChromeSlot } from './ChromeSlot';
45
+ import { WorktreeSwitcher } from './WorktreeSwitcher';
46
+
47
+ export function ProjectHeader({
48
+ transport,
49
+ }: {
50
+ /** Controlled production transport for bounded hosts; the application
51
+ * defaults to the connected PlayBar. */
52
+ transport?: ReactNode | undefined;
53
+ }) {
54
+ const [, forceUpdate] = useReducer((x: number) => x + 1, 0);
55
+ useEffect(() => onProjectChange(forceUpdate), []);
56
+ const [menuOpen, setMenuOpen] = useState(false);
57
+ const menuRef = useRef<HTMLDivElement>(null);
58
+
59
+ const project = getCurrentProject();
60
+ const projectName = project?.config.name ?? 'No Project';
61
+ const projectVersion = project?.config.version ?? null;
62
+ const engineVersion = project?.config.engine.version ?? null;
63
+ const manifestVersion = project?.config.manifestVersion ?? null;
64
+ const projectPath = project?.rootPath ?? '';
65
+ // Close menu on outside click
66
+ useEffect(() => {
67
+ if (!menuOpen) return;
68
+ const handler = (e: MouseEvent) => {
69
+ if (menuRef.current && !menuRef.current.contains(e.target as Node)) {
70
+ setMenuOpen(false);
71
+ }
72
+ };
73
+ document.addEventListener('mousedown', handler);
74
+ return () => document.removeEventListener('mousedown', handler);
75
+ }, [menuOpen]);
76
+
77
+ return (
78
+ <header className="volter-project-header">
79
+ {/* W5 (W4-reviewer candidate — transport narrow-width overlap guard):
80
+ the header is now THREE flex columns — name (shrinkable, ellipsized)
81
+ | transport (fixed) | right spacer — replacing the W4 absolute
82
+ centering, which let the transport cluster overlap the project name
83
+ at narrow widths. Equal-flex side columns keep the transport
84
+ visually centered whenever space allows; flex items cannot overlap
85
+ when it doesn't. */}
86
+ {/* Project name — clickable dropdown trigger. The cluster wrapper is
87
+ layout-neutral under bars chrome and becomes the LEFT glass island
88
+ under islands chrome (P6-U3); the dropdown Menu stays a SIBLING of
89
+ the island so the island's backdrop-filter never captures it. */}
90
+ {activeProduct()?.nativeMenus ? (
91
+ <div className="volter-project-header-left" />
92
+ ) : (
93
+ <div ref={menuRef} className="volter-project-header-left">
94
+ <div className="volter-project-header-cluster volter-chrome-island volter-glass-island">
95
+ {/* THE LEADING ITEM IS A BARE GLYPH, and that is transcription, not
96
+ economy. Blender opens its top bar with an app mark whose INK
97
+ starts at the band's own inset and whose click opens a MENU —
98
+ the mark IS the app menu's trigger. Measured on `topbar.png` at
99
+ native 2x: device x 12..43, i.e. CSS 6.0..21.5 — 16.0 wide, 13.0
100
+ tall — and the first menu word's ink follows at CSS 32.0. (The
101
+ whole-frame read's "32 CSS px app mark" was that 32-DEVICE-px
102
+ width read as CSS, and its "menu words start at 60" followed
103
+ from it; the frame says 16 and 32.)
104
+
105
+ Ours used to spend 67 px here on a padded pill drawing the
106
+ project NAME, which ellipsized to `prob…` at 1728 even for an
107
+ eight-character name, and the switcher beside it another 204.
108
+ The project's identity is not lost by the glyph: it is the
109
+ browser TAB's title (`AppRoot.tsx` writes `document.title` from
110
+ the project name — the tab strip IS our OS title bar, the place
111
+ Blender puts the file name), this button's tooltip and
112
+ accessible name, and the first row of the menu it opens. */}
113
+ <MenuTrigger
114
+ data-testid="project-menu-trigger"
115
+ aria-label={`${projectName} project menu`}
116
+ title={projectPath ? `${projectName} — ${projectPath}` : projectName}
117
+ onClick={() => setMenuOpen((v) => !v)}
118
+ onKeyDown={(event) => {
119
+ if (event.key !== 'ArrowDown') return;
120
+ event.preventDefault();
121
+ setMenuOpen(true);
122
+ }}
123
+ className="volter-project-trigger"
124
+ >
125
+ <EditorIcon icon={editorIcons.content.project} />
126
+ {/* The name stays in the DOM at zero pixels: the accessible name
127
+ above is a LABEL, this is the element's CONTENT, and it is what
128
+ `data-project` has always carried for the tooling that reads
129
+ which project a session has open. */}
130
+ <Text
131
+ data-testid="project-name"
132
+ data-project={projectName}
133
+ data-project-version={projectVersion ?? undefined}
134
+ variant="label"
135
+ className="volter-sr-only"
136
+ >
137
+ {projectName}
138
+ </Text>
139
+ </MenuTrigger>
140
+ <WorktreeSwitcher />
141
+ <Inline onPointerDown={() => setMenuOpen(false)} className="volter-project-app-menus">
142
+ <ApplicationMenus />
143
+ </Inline>
144
+ </div>
145
+ <WorkspaceTabs />
146
+
147
+ {menuOpen && (
148
+ <Menu
149
+ autoFocusFirst
150
+ onDismiss={() => {
151
+ const trigger = menuRef.current?.querySelector<HTMLButtonElement>(
152
+ '[data-testid="project-menu-trigger"]',
153
+ );
154
+ setMenuOpen(false);
155
+ requestAnimationFrame(() => trigger?.focus());
156
+ }}
157
+ className="volter-project-menu"
158
+ >
159
+ {/* IDENTITY'S READABLE HOME. The trigger is a glyph, so the name
160
+ is the first thing this menu says — the row Blender's OS title
161
+ bar carries and a browser window does not. */}
162
+ <Text
163
+ data-testid="project-menu-name"
164
+ title={projectName}
165
+ variant="label"
166
+ truncate
167
+ className="volter-project-menu-name"
168
+ >
169
+ {projectName}
170
+ </Text>
171
+ {projectPath && (
172
+ <Text
173
+ data-testid="project-menu-path"
174
+ title={projectPath}
175
+ variant="caption"
176
+ tone="dim"
177
+ selectable
178
+ truncate
179
+ className="volter-project-path"
180
+ >
181
+ {projectPath}
182
+ </Text>
183
+ )}
184
+ {projectVersion && engineVersion && manifestVersion !== null && (
185
+ <>
186
+ {projectPath && <MenuSeparator />}
187
+ <div data-testid="project-version-summary" className="volter-project-versions">
188
+ <span>Editor</span>
189
+ <Text variant="code" tone="muted">
190
+ v{BUNDLED_EDITOR_VERSION}
191
+ </Text>
192
+ <span>Project</span>
193
+ <Text variant="code" tone="muted">
194
+ v{projectVersion}
195
+ </Text>
196
+ <span>Engine</span>
197
+ <Text variant="code" tone="muted">
198
+ v{engineVersion}
199
+ </Text>
200
+ <span>Format</span>
201
+ <Text variant="code" tone="muted">
202
+ v{manifestVersion}
203
+ </Text>
204
+ </div>
205
+ <MenuSeparator />
206
+ </>
207
+ )}
208
+ {projectPath && (
209
+ <MenuItem
210
+ onSelect={() => {
211
+ setMenuOpen(false);
212
+ revealInFinder(projectPath);
213
+ }}
214
+ >
215
+ Show in Finder
216
+ </MenuItem>
217
+ )}
218
+ <MenuItem
219
+ data-testid="project-menu-history"
220
+ onSelect={() => {
221
+ setMenuOpen(false);
222
+ window.dispatchEvent(new CustomEvent('editor:show-history-tab'));
223
+ }}
224
+ >
225
+ {/* §8 naming: `Undo History`, never plain `History` (git/version
226
+ confusion). */}
227
+ Undo History…
228
+ </MenuItem>
229
+ </Menu>
230
+ )}
231
+ </div>
232
+ )}
233
+ {/* W4 (§4.1/§9 W4, inventory row V4 + the recorded always-visible-
234
+ transport work item): the global TRANSPORT cluster lives in the
235
+ command bar — reachable regardless of which center document is
236
+ active (asset/story docs used to hide play/pause/stop entirely).
237
+ Centered in the row, matching the §4.1 mock's `> || >`. */}
238
+ <Inline
239
+ data-testid="header-transport"
240
+ className="volter-project-transport volter-chrome-island volter-glass-island"
241
+ align="center"
242
+ >
243
+ {transport ?? <ProjectTransport />}
244
+ </Inline>
245
+ {/* Conversation entry moved to the global bottom shell row. Keep this
246
+ equal-width spacer so transport remains visually centered. */}
247
+ <Inline className="volter-project-header-right" justify="end" align="center">
248
+ {/* WHO IS IN THIS SESSION. A chrome slot, not a component: the host
249
+ owns the place and a package owns what sits there
250
+ (`chrome-slot-registry.ts`). the collaboration package fills it; a
251
+ build without that package renders nothing here, which is the
252
+ honest answer for an editor nobody else is in. */}
253
+ <ChromeSlot slot="header-trailing" />
254
+ </Inline>
255
+ {/* SaveStatus moved OUT of this command header (H3): status is status,
256
+ not a command — it renders in the W4 status bar
257
+ (`status-contributions.tsx`). */}
258
+ </header>
259
+ );
260
+ }
261
+
262
+ /**
263
+ * The WORKSPACE TAB STRIP — Blender's top bar, shown only when the active
264
+ * chrome regions say so (`workspaceTabs`, a workspace's region or the person's;
265
+ * Blender's workspaces turn it on). A click is the explicit act the workspace
266
+ * ruling requires (`workspace-presets.ts`); the strip derives nothing.
267
+ */
268
+ function WorkspaceTabs() {
269
+ useSyncExternalStore(subscribeChromeRegions, chromeRegionsVersion, chromeRegionsVersion);
270
+ useSyncExternalStore(subscribeEditorWorkspaces, editorWorkspacesVersion, editorWorkspacesVersion);
271
+ const active = useSyncExternalStore(
272
+ subscribeEditorWorkspace,
273
+ activeEditorWorkspace,
274
+ activeEditorWorkspace,
275
+ );
276
+ if (activeChromeRegions().workspaceTabs !== 'shown') return null;
277
+ const workspaces = editorWorkspaces().filter((entry) => workspaceApplies(entry.id));
278
+ return (
279
+ <>
280
+ {/* Blender closes its menu words with a 1x20 rule before the workspace
281
+ tabs begin (measured `topbar.png`, halved: a #2e2e2e rule sitting 16px
282
+ past `Help`'s ink). Without it the two regions read as one
283
+ undifferentiated run of words, which is what ours did. It belongs to
284
+ the strip, so it appears and disappears with it. */}
285
+ <Divider orientation="vertical" className="volter-project-menu-rule" />
286
+ <div
287
+ role="tablist"
288
+ aria-label="Workspaces"
289
+ data-testid="workspace-tabs"
290
+ className="volter-workspace-tabs"
291
+ >
292
+ {workspaces.map((entry) => (
293
+ <button
294
+ key={entry.id}
295
+ type="button"
296
+ role="tab"
297
+ aria-selected={entry.id === active}
298
+ title={entry.description}
299
+ className="volter-workspace-tab"
300
+ onClick={() => setEditorWorkspace(entry.id)}
301
+ >
302
+ {entry.title}
303
+ </button>
304
+ ))}
305
+ </div>
306
+ </>
307
+ );
308
+ }
309
+
310
+ /** The global transport cluster: whatever the project's packages contribute
311
+ * to the header (`@volter/sdk/chrome`, `.header`) — the Play transport
312
+ * ships with `@volter/editor-game`; a folder of models contributes nothing here. */
313
+ function ProjectTransport() {
314
+ useSyncExternalStore(
315
+ subscribeContributedChrome,
316
+ contributedChromeVersion,
317
+ contributedChromeVersion,
318
+ );
319
+ const items = contributedHeaderItems();
320
+ if (items.length === 0) return null;
321
+ return (
322
+ <>
323
+ {items.map(({ id, Component }) => (
324
+ <Component key={id} documentId="" />
325
+ ))}
326
+ </>
327
+ );
328
+ }
@@ -0,0 +1,358 @@
1
+ import {
2
+ faExpand,
3
+ faMagnifyingGlassMinus,
4
+ faMagnifyingGlassPlus,
5
+ faRotate,
6
+ } from '@fortawesome/free-solid-svg-icons';
7
+ import {
8
+ AnchoredMenu,
9
+ Button,
10
+ EditorIcon,
11
+ FloatingToolbar,
12
+ IconButton,
13
+ MenuItem,
14
+ MenuSeparator,
15
+ Select,
16
+ TextInput,
17
+ } from '@volter/sdk/widgets';
18
+ import { type RefObject, useCallback, useRef, useState, useSyncExternalStore } from 'react';
19
+ import {
20
+ fitReactStoryBoard,
21
+ getReactStoryBoardViewport,
22
+ type ReactStoryBoardViewportState,
23
+ setActiveReactStoryBoardViewport,
24
+ subscribeReactStoryBoardViewport,
25
+ toggleActiveReactStoryBoardViewportRotation,
26
+ } from '@volter/sdk/kit/authoring/react-story-board';
27
+ import type { ViewportToolContext } from '@volter/sdk/kit/authoring/viewport-tool-context';
28
+ import {
29
+ getRootCanvasViewport,
30
+ setRootCanvasViewport,
31
+ subscribeRootCanvasViewport,
32
+ } from '@volter/sdk/kit/world-canvas-viewport-state';
33
+ import {
34
+ getRootPan,
35
+ panTransformValue,
36
+ setRootView,
37
+ subscribeRootPan,
38
+ } from '@volter/sdk/kit/world-pan-state';
39
+ import { useEditorStore } from '@volter/sdk/kit/editor-runtime';
40
+
41
+ const VIEWPORT_PRESETS = {
42
+ fill: null,
43
+ desktop: { width: 1440, height: 900 },
44
+ tablet: { width: 768, height: 1024 },
45
+ mobile: { width: 390, height: 844 },
46
+ } as const;
47
+
48
+ function ResponsiveViewportControls({
49
+ boardViewport,
50
+ containerRef,
51
+ selectViewport,
52
+ viewportValue,
53
+ }: {
54
+ boardViewport: ReactStoryBoardViewportState | null;
55
+ containerRef: RefObject<HTMLDivElement | null>;
56
+ selectViewport(preset: string): void;
57
+ viewportValue: string;
58
+ }) {
59
+ return (
60
+ <>
61
+ <Select
62
+ aria-label="Responsive viewport"
63
+ title={
64
+ boardViewport?.locked
65
+ ? 'Viewport is locked by this story’s globals.viewport'
66
+ : 'Responsive viewport'
67
+ }
68
+ value={viewportValue}
69
+ disabled={boardViewport?.locked === true}
70
+ onChange={(event) => selectViewport(event.target.value)}
71
+ className="volter-react-viewport-select"
72
+ >
73
+ <option value="fill">Responsive</option>
74
+ {boardViewport ? (
75
+ boardViewport.choices.map((choice) => (
76
+ <option key={choice.id} value={choice.id}>
77
+ {choice.label} · {choice.width}×{choice.height}
78
+ </option>
79
+ ))
80
+ ) : (
81
+ <>
82
+ <option value="desktop">Desktop · 1440×900</option>
83
+ <option value="tablet">Tablet · 768×1024</option>
84
+ <option value="mobile">Mobile · 390×844</option>
85
+ </>
86
+ )}
87
+ </Select>
88
+ {boardViewport && (
89
+ <IconButton
90
+ aria-label="Rotate viewport"
91
+ title={
92
+ boardViewport.locked
93
+ ? 'Viewport is locked by this story’s globals.viewport'
94
+ : 'Rotate viewport'
95
+ }
96
+ size="comfortable"
97
+ disabled={boardViewport.locked}
98
+ onClick={() => toggleActiveReactStoryBoardViewportRotation(containerRef.current)}
99
+ >
100
+ <EditorIcon icon={faRotate} size="md" />
101
+ </IconButton>
102
+ )}
103
+ </>
104
+ );
105
+ }
106
+
107
+ export function ReactCanvasControls({
108
+ containerRef,
109
+ context,
110
+ boardOnly = false,
111
+ }: {
112
+ containerRef: RefObject<HTMLDivElement | null>;
113
+ context: ViewportToolContext | null;
114
+ /** Fixed-pixel component boards need only the common Figma-style camera
115
+ * controls. Responsive presets and entity framing belong to DOM authoring. */
116
+ boardOnly?: boolean;
117
+ }) {
118
+ const store = useEditorStore();
119
+ useSyncExternalStore(store.subscribe, store.getSnapshot);
120
+ const view = useSyncExternalStore(subscribeRootPan, getRootPan);
121
+ const viewport = useSyncExternalStore(subscribeRootCanvasViewport, getRootCanvasViewport);
122
+ const boardViewport = useSyncExternalStore(
123
+ subscribeReactStoryBoardViewport,
124
+ () => getReactStoryBoardViewport(containerRef.current),
125
+ () => null,
126
+ );
127
+
128
+ const zoomAroundCenter = useCallback(
129
+ (requestedZoom: number) => {
130
+ const container = containerRef.current;
131
+ if (!container) return;
132
+ const zoom = Math.min(4, Math.max(0.1, requestedZoom));
133
+ const cx = container.clientWidth / 2;
134
+ const cy = container.clientHeight / 2;
135
+ const worldX = (cx - view.x) / view.zoom;
136
+ const worldY = (cy - view.y) / view.zoom;
137
+ setRootView(cx - worldX * zoom, cy - worldY * zoom, zoom);
138
+ },
139
+ [containerRef, view],
140
+ );
141
+
142
+ const zoomToSelection = useCallback(() => {
143
+ const container = containerRef.current;
144
+ const selectedId = [...store.selectedEntityIds].at(-1);
145
+ if (!container || !selectedId) return;
146
+ const rect = context?.adapter.rects?.rect(selectedId);
147
+ if (!rect || rect.width <= 0 || rect.height <= 0) return;
148
+ const availableWidth = Math.max(1, container.clientWidth - 96);
149
+ const availableHeight = Math.max(1, container.clientHeight - 96);
150
+ const zoom = Math.min(
151
+ 4,
152
+ Math.max(0.1, Math.min(availableWidth / rect.width, availableHeight / rect.height)),
153
+ );
154
+ setRootView(
155
+ container.clientWidth / 2 - (rect.x + rect.width / 2) * zoom,
156
+ container.clientHeight / 2 - (rect.y + rect.height / 2) * zoom,
157
+ zoom,
158
+ );
159
+ }, [containerRef, context, store]);
160
+
161
+ const selectViewport = useCallback(
162
+ (preset: string) => {
163
+ const container = containerRef.current;
164
+ if (boardViewport) {
165
+ setActiveReactStoryBoardViewport(container, preset === 'fill' ? null : preset);
166
+ return;
167
+ }
168
+ const size = VIEWPORT_PRESETS[preset as keyof typeof VIEWPORT_PRESETS];
169
+ if (!container || size === null) {
170
+ setRootCanvasViewport(null, null);
171
+ setRootView(0, 0, 1);
172
+ return;
173
+ }
174
+ setRootCanvasViewport(size.width, size.height);
175
+ const zoom = Math.min(
176
+ 1,
177
+ Math.max(
178
+ 0.1,
179
+ Math.min(
180
+ (container.clientWidth - 96) / size.width,
181
+ (container.clientHeight - 96) / size.height,
182
+ ),
183
+ ),
184
+ );
185
+ setRootView(
186
+ (container.clientWidth - size.width * zoom) / 2,
187
+ (container.clientHeight - size.height * zoom) / 2,
188
+ zoom,
189
+ );
190
+ },
191
+ [boardViewport, containerRef],
192
+ );
193
+
194
+ const fitBoard = useCallback(() => {
195
+ const container = containerRef.current;
196
+ if (container) fitReactStoryBoard(container);
197
+ }, [containerRef]);
198
+
199
+ const hasStoryBoard = boardViewport !== null;
200
+ const hasDomAuthoring = context?.kind === 'dom' && Boolean(context.adapter.rects);
201
+ if (boardOnly ? !hasStoryBoard : !hasDomAuthoring) return null;
202
+
203
+ const viewportValue = boardViewport
204
+ ? (boardViewport.selectedViewportId ?? 'fill')
205
+ : (Object.entries(VIEWPORT_PRESETS).find(([, size]) =>
206
+ size === null
207
+ ? viewport.width === null
208
+ : size.width === viewport.width && size.height === viewport.height,
209
+ )?.[0] ?? 'fill');
210
+
211
+ return (
212
+ <>
213
+ {!hasStoryBoard && viewport.width !== null && viewport.height !== null && (
214
+ <div
215
+ data-testid="react-canvas-device-frame"
216
+ style={{
217
+ position: 'absolute',
218
+ left: 0,
219
+ top: 0,
220
+ zIndex: 0,
221
+ width: viewport.width,
222
+ height: viewport.height,
223
+ transform: panTransformValue(view),
224
+ transformOrigin: '0 0',
225
+ pointerEvents: 'none',
226
+ background: '#fff',
227
+ boxShadow: '0 0 0 1px #ffffff40, 0 8px 30px #0009',
228
+ }}
229
+ />
230
+ )}
231
+ <FloatingToolbar
232
+ label={boardOnly ? '2D component board view' : 'React canvas view'}
233
+ data-testid={boardOnly ? 'canvas-board-zoom-controls' : 'react-canvas-zoom-controls'}
234
+ {...(boardOnly ? { 'data-board-zoom': '' } : {})}
235
+ data-volter-canvas-navigation-ignore="true"
236
+ className="volter-react-canvas-controls"
237
+ >
238
+ {!boardOnly && (
239
+ <ResponsiveViewportControls
240
+ boardViewport={boardViewport}
241
+ containerRef={containerRef}
242
+ selectViewport={selectViewport}
243
+ viewportValue={viewportValue}
244
+ />
245
+ )}
246
+ {hasStoryBoard && (
247
+ <Button
248
+ variant="ghost"
249
+ aria-label="Fit story board"
250
+ title="Fit all stories"
251
+ size="comfortable"
252
+ onClick={fitBoard}
253
+ >
254
+ Fit board
255
+ </Button>
256
+ )}
257
+ <IconButton
258
+ aria-label="Zoom out"
259
+ title="Zoom out"
260
+ size="comfortable"
261
+ onClick={() => zoomAroundCenter(view.zoom / 1.2)}
262
+ >
263
+ <EditorIcon icon={faMagnifyingGlassMinus} size="md" />
264
+ </IconButton>
265
+ <ZoomMenu
266
+ zoom={view.zoom}
267
+ zoomTo={zoomAroundCenter}
268
+ {...(hasStoryBoard ? { fit: fitBoard } : {})}
269
+ {...(!boardOnly && store.selectedEntityIds.size > 0 ? { toSelection: zoomToSelection } : {})}
270
+ />
271
+ <IconButton
272
+ aria-label="Zoom in"
273
+ title="Zoom in"
274
+ size="comfortable"
275
+ onClick={() => zoomAroundCenter(view.zoom * 1.2)}
276
+ >
277
+ <EditorIcon icon={faMagnifyingGlassPlus} size="md" />
278
+ </IconButton>
279
+ {!boardOnly && (
280
+ <IconButton
281
+ aria-label="Zoom to selection"
282
+ title="Zoom to selection"
283
+ size="comfortable"
284
+ onClick={zoomToSelection}
285
+ >
286
+ <EditorIcon icon={faExpand} size="md" />
287
+ </IconButton>
288
+ )}
289
+ </FloatingToolbar>
290
+ </>
291
+ );
292
+ }
293
+
294
+ /**
295
+ * Figma's zoom menu, opened from the zoom percentage: the zoom typed as a percentage, Zoom in and
296
+ * out, Zoom to fit, 50%, 100% and 200%, and Zoom to selection when there is one.
297
+ */
298
+ function ZoomMenu({
299
+ zoom,
300
+ zoomTo,
301
+ fit,
302
+ toSelection,
303
+ }: {
304
+ zoom: number;
305
+ zoomTo: (zoom: number) => void;
306
+ fit?: () => void;
307
+ toSelection?: () => void;
308
+ }) {
309
+ const [open, setOpen] = useState(false);
310
+ const triggerRef = useRef<HTMLButtonElement>(null);
311
+ const act = (run: () => void) => () => {
312
+ run();
313
+ setOpen(false);
314
+ };
315
+ return (
316
+ <>
317
+ <Button
318
+ ref={triggerRef}
319
+ variant="ghost"
320
+ aria-label="Zoom"
321
+ aria-haspopup="menu"
322
+ aria-expanded={open}
323
+ title="Zoom"
324
+ size="comfortable"
325
+ onClick={() => setOpen(!open)}
326
+ >
327
+ {Math.round(zoom * 100)}%
328
+ </Button>
329
+ {open && (
330
+ <AnchoredMenu anchorRef={triggerRef} align="end" clamp aria-label="Zoom" onDismiss={() => setOpen(false)}>
331
+ <div style={{ padding: '4px 8px' }}>
332
+ <TextInput
333
+ aria-label="Zoom percentage"
334
+ defaultValue={String(Math.round(zoom * 100))}
335
+ onKeyDown={(event) => {
336
+ if (event.key !== 'Enter') return;
337
+ const percent = Number.parseFloat(event.currentTarget.value);
338
+ if (Number.isFinite(percent) && percent > 0) {
339
+ zoomTo(percent / 100);
340
+ setOpen(false);
341
+ }
342
+ }}
343
+ />
344
+ </div>
345
+ <MenuSeparator />
346
+ <MenuItem onSelect={act(() => zoomTo(zoom * 1.2))}>Zoom in</MenuItem>
347
+ <MenuItem onSelect={act(() => zoomTo(zoom / 1.2))}>Zoom out</MenuItem>
348
+ {fit ? <MenuItem onSelect={act(fit)}>Zoom to fit</MenuItem> : null}
349
+ {toSelection ? <MenuItem onSelect={act(toSelection)}>Zoom to selection</MenuItem> : null}
350
+ <MenuSeparator />
351
+ {[0.5, 1, 2].map((preset) => (
352
+ <MenuItem key={preset} onSelect={act(() => zoomTo(preset))}>{`Zoom to ${preset * 100}%`}</MenuItem>
353
+ ))}
354
+ </AnchoredMenu>
355
+ )}
356
+ </>
357
+ );
358
+ }