@volter/editor-core 0.5.57

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (719) hide show
  1. package/BUNDLED_NOTICES +1527 -0
  2. package/LICENSE +686 -0
  3. package/LICENSE-APACHE +202 -0
  4. package/NOTICE +18 -0
  5. package/README.md +24 -0
  6. package/dist/build/chunk-JS4D4XZ3.js +175 -0
  7. package/dist/build/chunk-JS4D4XZ3.js.map +7 -0
  8. package/dist/build/vite-plugin-product-contributions.js +100 -0
  9. package/dist/build/vite-plugin-product-contributions.js.map +7 -0
  10. package/dist/build/vite-plugin-shared-react.js +25 -0
  11. package/dist/build/vite-plugin-shared-react.js.map +7 -0
  12. package/dist/build/vite-plugin-shared-three.js +86 -0
  13. package/dist/build/vite-plugin-shared-three.js.map +7 -0
  14. package/dist/server/chunk-KMJC7AGO.js +98 -0
  15. package/dist/server/chunk-KMJC7AGO.js.map +7 -0
  16. package/dist/server/chunk-PSILUMGS.js +46 -0
  17. package/dist/server/chunk-PSILUMGS.js.map +7 -0
  18. package/dist/server/frame-proxy.js +255 -0
  19. package/dist/server/frame-proxy.js.map +7 -0
  20. package/dist/server/open-browser.js +53 -0
  21. package/dist/server/open-browser.js.map +7 -0
  22. package/dist/server/process-shutdown.js +127 -0
  23. package/dist/server/process-shutdown.js.map +7 -0
  24. package/dist/server/session-registry.js +141 -0
  25. package/dist/server/session-registry.js.map +7 -0
  26. package/dist/server/spawn-opener.js +11 -0
  27. package/dist/server/spawn-opener.js.map +7 -0
  28. package/dist/server/worktree-identity.js +11 -0
  29. package/dist/server/worktree-identity.js.map +7 -0
  30. package/dist-server/packaged.mjs +88816 -0
  31. package/package.json +118 -0
  32. package/scripts/build-plugins.mjs +14 -0
  33. package/scripts/build-server.mjs +26 -0
  34. package/scripts/build-session.mjs +9 -0
  35. package/server/account-credentials.ts +60 -0
  36. package/server/account-service.ts +1434 -0
  37. package/server/adapter-region-includes.ts +47 -0
  38. package/server/asset-catalog-v2.ts +175 -0
  39. package/server/asset-history-snapshots.ts +196 -0
  40. package/server/asset-ledger-store.ts +195 -0
  41. package/server/asset-library-routes.ts +1595 -0
  42. package/server/blender-wasm-artifact.ts +430 -0
  43. package/server/boot-timings.ts +106 -0
  44. package/server/canonical-path.ts +50 -0
  45. package/server/checkout-workspace-preflight.ts +349 -0
  46. package/server/cloud-asset-catalog.ts +158 -0
  47. package/server/coding-inference-launch.ts +237 -0
  48. package/server/collaboration-account-client.ts +202 -0
  49. package/server/collaboration-attribution.ts +122 -0
  50. package/server/collaboration-session.ts +934 -0
  51. package/server/comfyui-bridge.ts +248 -0
  52. package/server/console-ledger.ts +499 -0
  53. package/server/creation-site-transform.ts +304 -0
  54. package/server/creation-site-write.ts +648 -0
  55. package/server/data-file-serialize.ts +158 -0
  56. package/server/editor-brand-html.ts +72 -0
  57. package/server/editor-control-socket.ts +366 -0
  58. package/server/editor-server-options.ts +190 -0
  59. package/server/editor-server.ts +1483 -0
  60. package/server/editor-sse.ts +352 -0
  61. package/server/engine-provenance.ts +169 -0
  62. package/server/engine-source-restart.ts +117 -0
  63. package/server/frame-bridge.ts +115 -0
  64. package/server/frame-proxy.ts +490 -0
  65. package/server/frame-workbench.ts +259 -0
  66. package/server/frontend-handoff.ts +229 -0
  67. package/server/game-globals-shadow.ts +113 -0
  68. package/server/gameplay-session-retention.ts +149 -0
  69. package/server/gameplay-sessions.ts +123 -0
  70. package/server/generation-jobs.ts +201 -0
  71. package/server/generation-reconciler.ts +137 -0
  72. package/server/generative-execution-context.ts +59 -0
  73. package/server/git-workflow.ts +612 -0
  74. package/server/harness-chat-caller.ts +36 -0
  75. package/server/harness-chat-service.ts +1681 -0
  76. package/server/idle-shutdown.ts +170 -0
  77. package/server/js-profiling-policy.ts +52 -0
  78. package/server/launcher-settings.ts +84 -0
  79. package/server/local-asset-catalog.ts +410 -0
  80. package/server/managed-account-defaults.ts +22 -0
  81. package/server/mock-control-plane.ts +540 -0
  82. package/server/model-import-conversion.ts +345 -0
  83. package/server/native-credential-store.ts +162 -0
  84. package/server/open-browser.ts +118 -0
  85. package/server/packaged.ts +1192 -0
  86. package/server/play-stall.ts +126 -0
  87. package/server/process-shutdown.ts +218 -0
  88. package/server/product-presets.ts +55 -0
  89. package/server/project-build-artifact.ts +95 -0
  90. package/server/project-components.ts +100 -0
  91. package/server/project-dependency-invalidation.ts +114 -0
  92. package/server/project-file-scan.ts +235 -0
  93. package/server/project-hmr-files.ts +215 -0
  94. package/server/project-install-roots.ts +210 -0
  95. package/server/project-kinds.ts +71 -0
  96. package/server/project-mcp-servers.ts +82 -0
  97. package/server/project-module-freshness.ts +109 -0
  98. package/server/project-module-instance.ts +375 -0
  99. package/server/project-optimize-deps-entries.ts +827 -0
  100. package/server/project-output-writer.ts +679 -0
  101. package/server/project-package-origin.ts +63 -0
  102. package/server/project-root-surface.ts +831 -0
  103. package/server/project-scratch-path.ts +47 -0
  104. package/server/project-script-hmr.ts +619 -0
  105. package/server/project-serving-plugins.ts +236 -0
  106. package/server/project-tools.ts +653 -0
  107. package/server/project-validation.ts +250 -0
  108. package/server/project-verbs.ts +127 -0
  109. package/server/project-view.ts +52 -0
  110. package/server/project-watch.ts +1134 -0
  111. package/server/project-work-coordinator.ts +288 -0
  112. package/server/provider-credentials.ts +331 -0
  113. package/server/recent-projects-store.ts +98 -0
  114. package/server/redact-secrets.ts +71 -0
  115. package/server/repository-presence.ts +267 -0
  116. package/server/routes/account.ts +357 -0
  117. package/server/routes/agents.ts +60 -0
  118. package/server/routes/assets.ts +154 -0
  119. package/server/routes/build.ts +13 -0
  120. package/server/routes/collaboration.ts +595 -0
  121. package/server/routes/configurations.ts +419 -0
  122. package/server/routes/context.ts +264 -0
  123. package/server/routes/control-plane.ts +1811 -0
  124. package/server/routes/logs.ts +272 -0
  125. package/server/routes/project-identity.ts +302 -0
  126. package/server/routes/project-open.ts +429 -0
  127. package/server/routes/project-source.ts +686 -0
  128. package/server/routes/project-state.ts +383 -0
  129. package/server/routes/relay.ts +1029 -0
  130. package/server/routes/route-helpers.ts +29 -0
  131. package/server/routes/served-modules.ts +138 -0
  132. package/server/routes/session-tabs.ts +196 -0
  133. package/server/routes/settings.ts +121 -0
  134. package/server/routes/share-control.ts +486 -0
  135. package/server/routes/themes.ts +116 -0
  136. package/server/routes/tools.ts +230 -0
  137. package/server/routes/worktrees.ts +595 -0
  138. package/server/scoped-game-css.ts +461 -0
  139. package/server/server-utils.ts +1194 -0
  140. package/server/session-product.ts +112 -0
  141. package/server/session-registry.ts +230 -0
  142. package/server/share-claims.ts +138 -0
  143. package/server/share-host.ts +526 -0
  144. package/server/share-session-gateway.ts +1362 -0
  145. package/server/share-tunnel.ts +123 -0
  146. package/server/spawn-opener.ts +74 -0
  147. package/server/support/play/log-format.ts +76 -0
  148. package/server/support/play/session-record.ts +199 -0
  149. package/server/support/project/build-discipline.ts +718 -0
  150. package/server/support/project/inspection-node.ts +119 -0
  151. package/server/support/project/log-naming.ts +54 -0
  152. package/server/support/project/provenance.ts +290 -0
  153. package/server/support/project/run-name.ts +18 -0
  154. package/server/support/project/session-journal.ts +830 -0
  155. package/server/support/project/shared.ts +327 -0
  156. package/server/tab-bootstrap.ts +62 -0
  157. package/server/tab-heartbeat.ts +477 -0
  158. package/server/tab-lifecycle.ts +550 -0
  159. package/server/tab-presence.ts +1508 -0
  160. package/server/team-agent-mirror.ts +95 -0
  161. package/server/twin-auth.ts +138 -0
  162. package/server/vendored-lock-recorder.ts +968 -0
  163. package/server/worktree-identity.ts +121 -0
  164. package/server/worktree-management.ts +398 -0
  165. package/server/worktree-retention.d.mts +13 -0
  166. package/server/worktree-retention.mjs +205 -0
  167. package/src/EditorContext.tsx +169 -0
  168. package/src/account.ts +333 -0
  169. package/src/action-registry.ts +532 -0
  170. package/src/active-product.ts +74 -0
  171. package/src/active-project.ts +155 -0
  172. package/src/adapter-editor-config.ts +25 -0
  173. package/src/adapter-observation.ts +49 -0
  174. package/src/animation/stage-transport.ts +379 -0
  175. package/src/animation/three-clips-subject.ts +172 -0
  176. package/src/api/asset-library-wire.ts +45 -0
  177. package/src/api/assets.ts +365 -0
  178. package/src/api/base.ts +10 -0
  179. package/src/api/build.ts +99 -0
  180. package/src/api/git-wire.ts +56 -0
  181. package/src/api/logs.ts +92 -0
  182. package/src/api/project-identity.ts +74 -0
  183. package/src/api/project-open.ts +355 -0
  184. package/src/api/project-source.ts +162 -0
  185. package/src/api/project-state.ts +107 -0
  186. package/src/api/relay.ts +268 -0
  187. package/src/api/settings.ts +36 -0
  188. package/src/api/themes.ts +45 -0
  189. package/src/api/worktrees.ts +257 -0
  190. package/src/asset-compare-core.ts +171 -0
  191. package/src/asset-compare.ts +294 -0
  192. package/src/asset-editor-context.tsx +100 -0
  193. package/src/asset-events.ts +97 -0
  194. package/src/asset-inspector-actions.ts +87 -0
  195. package/src/asset-preview-framing.ts +357 -0
  196. package/src/asset-preview.ts +2802 -0
  197. package/src/asset-selection-viewer-registry.ts +113 -0
  198. package/src/asset-selection.ts +133 -0
  199. package/src/asset-workflow/asset-capabilities.ts +344 -0
  200. package/src/asset-workflow/asset-import-jobs.ts +106 -0
  201. package/src/asset-workflow/asset-ledger-backend.ts +126 -0
  202. package/src/asset-workflow/asset-ledger.ts +156 -0
  203. package/src/asset-workflow/asset-materialization-report.ts +140 -0
  204. package/src/asset-workflow/asset-pack-manifest.ts +320 -0
  205. package/src/asset-workflow/asset-types.ts +142 -0
  206. package/src/asset-workflow/asset-workflow-quality.ts +61 -0
  207. package/src/asset-workflow/audio-preview-player.ts +193 -0
  208. package/src/asset-workflow/audio-waveform.ts +22 -0
  209. package/src/asset-workflow/cloud-asset-client.ts +263 -0
  210. package/src/asset-workflow/folder-preview.ts +345 -0
  211. package/src/asset-workflow/hosted-asset-materialization.ts +236 -0
  212. package/src/asset-workflow/image-view-scale.ts +32 -0
  213. package/src/asset-workflow/import-contract.ts +124 -0
  214. package/src/asset-workflow/ledger-write-lock.ts +244 -0
  215. package/src/asset-workflow/model-inspection.ts +818 -0
  216. package/src/asset-workflow/pixi-spritesheet.ts +197 -0
  217. package/src/asset-workflow/preview-resource-lifetime.ts +44 -0
  218. package/src/asset-workflow/project-asset-commands.ts +23 -0
  219. package/src/asset-workflow/project-asset-health.ts +139 -0
  220. package/src/asset-workflow/project-asset-operations.ts +154 -0
  221. package/src/asset-workflow/project-asset-roots.ts +68 -0
  222. package/src/asset-workflow/project-content.ts +288 -0
  223. package/src/asset-workflow/project-source-index.ts +550 -0
  224. package/src/asset-workflow/thumbnail-system.ts +256 -0
  225. package/src/authoring/active-adapter.ts +199 -0
  226. package/src/authoring/active-systems.ts +422 -0
  227. package/src/authoring/adapter-key.ts +18 -0
  228. package/src/authoring/authoring-asset-url.ts +27 -0
  229. package/src/authoring/bootstrap-state.ts +49 -0
  230. package/src/authoring/boundary-authoring-adapter.ts +184 -0
  231. package/src/authoring/breakpoint-state.ts +43 -0
  232. package/src/authoring/canvas-scene-guides.ts +76 -0
  233. package/src/authoring/component-instance-root.ts +171 -0
  234. package/src/authoring/composite-authoring-adapter.ts +2110 -0
  235. package/src/authoring/consumer-actions.ts +520 -0
  236. package/src/authoring/css-numeric-style.ts +97 -0
  237. package/src/authoring/design-time-layers.ts +849 -0
  238. package/src/authoring/design-time-mount-registry.ts +235 -0
  239. package/src/authoring/design-time-settle.ts +343 -0
  240. package/src/authoring/edit-mode-authoring.ts +619 -0
  241. package/src/authoring/eyedropper-session.ts +60 -0
  242. package/src/authoring/instance-source-menu-register.ts +13 -0
  243. package/src/authoring/instance-source-menu.ts +135 -0
  244. package/src/authoring/layered-pick.ts +183 -0
  245. package/src/authoring/live-gesture-lock.ts +50 -0
  246. package/src/authoring/live-object-transform.ts +62 -0
  247. package/src/authoring/mount-failure-report.ts +154 -0
  248. package/src/authoring/mounted-root-subjects.ts +144 -0
  249. package/src/authoring/no-authoring-adapter.ts +55 -0
  250. package/src/authoring/null-inspection-subjects.tsx +75 -0
  251. package/src/authoring/object3d-document-persistence.ts +106 -0
  252. package/src/authoring/object3d-document-session-registry.ts +120 -0
  253. package/src/authoring/object3d-document-session.ts +1326 -0
  254. package/src/authoring/object3d-gesture-controller.ts +113 -0
  255. package/src/authoring/panel-authoring.ts +121 -0
  256. package/src/authoring/prefab-instance-inspector-section.tsx +237 -0
  257. package/src/authoring/prefab-instance-section-model.ts +55 -0
  258. package/src/authoring/project-authoring-session.ts +105 -0
  259. package/src/authoring/provenance.ts +99 -0
  260. package/src/authoring/quarks-particle-systems.ts +19 -0
  261. package/src/authoring/react-canvas-navigation.ts +255 -0
  262. package/src/authoring/react-design-canvas-style.ts +20 -0
  263. package/src/authoring/react-story-board.ts +937 -0
  264. package/src/authoring/selection-scope.ts +195 -0
  265. package/src/authoring/shell-document-ops.ts +169 -0
  266. package/src/authoring/shell-object3d-document-write-policy.ts +108 -0
  267. package/src/authoring/shell-viewport-policy.ts +48 -0
  268. package/src/authoring/source-object3d-authoring-adapter.ts +526 -0
  269. package/src/authoring/stories-scope.ts +35 -0
  270. package/src/authoring/story-board-chrome-fit.ts +107 -0
  271. package/src/authoring/story-board-presentation.ts +111 -0
  272. package/src/authoring/three-projection-core.ts +226 -0
  273. package/src/authoring/viewport-pick-context.ts +40 -0
  274. package/src/authoring/viewport-raycast.ts +240 -0
  275. package/src/authoring/viewport-tool-context.ts +73 -0
  276. package/src/authoring/world-canvas-viewport-state.ts +35 -0
  277. package/src/authoring/world-hidden-viewport.ts +152 -0
  278. package/src/authoring/world-pan-state.ts +198 -0
  279. package/src/authoring/world-session-state.ts +101 -0
  280. package/src/authoring/write-pipe.ts +173 -0
  281. package/src/availability-tick.ts +66 -0
  282. package/src/bitmap-label.ts +120 -0
  283. package/src/blender-tab-metrics.ts +161 -0
  284. package/src/board-open-actions.ts +20 -0
  285. package/src/boot-routing.ts +391 -0
  286. package/src/build-identity.ts +15 -0
  287. package/src/bytes-codec.ts +62 -0
  288. package/src/camera-authoring.ts +175 -0
  289. package/src/cancellation-reason.ts +58 -0
  290. package/src/canvas-preview-frames.ts +482 -0
  291. package/src/capture-camera-pose.ts +77 -0
  292. package/src/chrome-registry.ts +159 -0
  293. package/src/chrome-slot-registry.ts +91 -0
  294. package/src/collaboration-client.ts +264 -0
  295. package/src/collaboration-presence.ts +41 -0
  296. package/src/command-dispatch.ts +19 -0
  297. package/src/command-listener.ts +2743 -0
  298. package/src/command-registry.ts +70 -0
  299. package/src/component-board-registry.ts +205 -0
  300. package/src/component-states-registry.ts +200 -0
  301. package/src/components/AgentPresentationNotice.tsx +75 -0
  302. package/src/components/AlignToolbar.tsx +204 -0
  303. package/src/components/AppRoot.tsx +394 -0
  304. package/src/components/ApplicationMenus.tsx +415 -0
  305. package/src/components/AssetBrowser.tsx +2843 -0
  306. package/src/components/AssetEditorShell.tsx +216 -0
  307. package/src/components/AssetInspectorToolSection.tsx +124 -0
  308. package/src/components/BoardRulers.tsx +354 -0
  309. package/src/components/CameraInfo.tsx +81 -0
  310. package/src/components/CanvasSceneViewport.tsx +556 -0
  311. package/src/components/CapabilityCoverageSection.tsx +185 -0
  312. package/src/components/CenterDocuments.tsx +22 -0
  313. package/src/components/ChromeSlot.tsx +20 -0
  314. package/src/components/CodeView.tsx +470 -0
  315. package/src/components/CollaborationHeaderControl.css +74 -0
  316. package/src/components/CompactInspectorCard.tsx +190 -0
  317. package/src/components/CompactInspectorShell.tsx +39 -0
  318. package/src/components/ConsolePanel.css +55 -0
  319. package/src/components/ConsolePanel.tsx +270 -0
  320. package/src/components/DefaultEditorLayout.tsx +106 -0
  321. package/src/components/DocumentHeaderStrip.tsx +96 -0
  322. package/src/components/DocumentShelfRail.tsx +45 -0
  323. package/src/components/DocumentThumbnail.tsx +345 -0
  324. package/src/components/EditorLeaseGuard.tsx +372 -0
  325. package/src/components/ErrorBanner.tsx +123 -0
  326. package/src/components/FolderPreviewTile.tsx +159 -0
  327. package/src/components/GameHierarchy.tsx +3842 -0
  328. package/src/components/GameplaySessionTimeline.tsx +295 -0
  329. package/src/components/InspectionProjection.tsx +968 -0
  330. package/src/components/Inspector.tsx +233 -0
  331. package/src/components/InspectorCanvasPreview.tsx +35 -0
  332. package/src/components/InspectorFieldsSection.tsx +290 -0
  333. package/src/components/InspectorObjectPreview.tsx +57 -0
  334. package/src/components/InspectorStoriesSection.tsx +92 -0
  335. package/src/components/InspectorToolSection.tsx +96 -0
  336. package/src/components/InspectorTransformSection.tsx +245 -0
  337. package/src/components/LightExplorerPanel.tsx +433 -0
  338. package/src/components/MediaProperties.tsx +145 -0
  339. package/src/components/NonThreeAuthoringBootstrap.tsx +19 -0
  340. package/src/components/Object3DDocumentToolbar.css +100 -0
  341. package/src/components/Object3DDocumentToolbar.tsx +454 -0
  342. package/src/components/Object3DDocumentViewport.tsx +58 -0
  343. package/src/components/ProjectHeader.css +617 -0
  344. package/src/components/ProjectHeader.tsx +328 -0
  345. package/src/components/ProjectLayout.tsx +249 -0
  346. package/src/components/ReactCanvasControls.tsx +284 -0
  347. package/src/components/RootSelectionOverlay.tsx +3162 -0
  348. package/src/components/RootTextEditor.tsx +79 -0
  349. package/src/components/SaveStatus.tsx +70 -0
  350. package/src/components/StageHost.tsx +1954 -0
  351. package/src/components/StageOverlays.tsx +21 -0
  352. package/src/components/StartupErrorScreen.tsx +76 -0
  353. package/src/components/StartupLoadingScreen.tsx +65 -0
  354. package/src/components/StatsOverlay.tsx +78 -0
  355. package/src/components/SurfaceStateOverlay.tsx +24 -0
  356. package/src/components/ToolContributionSurfaces.tsx +65 -0
  357. package/src/components/ToolHost.tsx +370 -0
  358. package/src/components/ToolObject3DPreview.tsx +39 -0
  359. package/src/components/Toolbar.tsx +595 -0
  360. package/src/components/TransientHint.tsx +44 -0
  361. package/src/components/TransportStrip.tsx +174 -0
  362. package/src/components/VersionControlSection.tsx +470 -0
  363. package/src/components/VgaiLogo.css +83 -0
  364. package/src/components/VgaiLogo.tsx +35 -0
  365. package/src/components/ViewportControlsHint.tsx +60 -0
  366. package/src/components/ViewportFurniture.tsx +401 -0
  367. package/src/components/ViewportOverlay.tsx +145 -0
  368. package/src/components/ViewportOverlaysMenu.tsx +153 -0
  369. package/src/components/ViewportShadingMenu.tsx +300 -0
  370. package/src/components/ViewportViewMenu.tsx +101 -0
  371. package/src/components/WorkspaceDocumentSurface.tsx +204 -0
  372. package/src/components/WorkspaceUtilitySurface.tsx +22 -0
  373. package/src/components/WorktreeSwitcher.css +239 -0
  374. package/src/components/WorktreeSwitcher.tsx +890 -0
  375. package/src/components/account-documents.tsx +1162 -0
  376. package/src/components/asset-documents.tsx +812 -0
  377. package/src/components/asset-editor-persistence.ts +216 -0
  378. package/src/components/asset-selection-section.tsx +535 -0
  379. package/src/components/asset-thumbnails.tsx +312 -0
  380. package/src/components/asset-viewers/AudioViewer.tsx +201 -0
  381. package/src/components/asset-viewers/EntityModelDocument.tsx +122 -0
  382. package/src/components/asset-viewers/EnvironmentAssetDocument.tsx +440 -0
  383. package/src/components/asset-viewers/GenericJsonViewer.tsx +102 -0
  384. package/src/components/asset-viewers/ImageViewer.tsx +300 -0
  385. package/src/components/asset-viewers/JsonAssetDocument.tsx +93 -0
  386. package/src/components/asset-viewers/LiveModuleDocument.tsx +417 -0
  387. package/src/components/asset-viewers/LutAssetDocument.tsx +444 -0
  388. package/src/components/asset-viewers/ModelAssetDocument.tsx +105 -0
  389. package/src/components/asset-viewers/Object3DPreview.tsx +359 -0
  390. package/src/components/asset-viewers/OnlineAssetDetail.tsx +421 -0
  391. package/src/components/asset-viewers/PasteboardModuleDocument.tsx +67 -0
  392. package/src/components/asset-viewers/QuarksAssetDocument.tsx +526 -0
  393. package/src/components/asset-viewers/ShaderAssetDocument.tsx +743 -0
  394. package/src/components/asset-viewers/SourceAssetViewer.tsx +281 -0
  395. package/src/components/asset-viewers/SpritesheetSpriteView.tsx +102 -0
  396. package/src/components/asset-viewers/VideoViewer.tsx +101 -0
  397. package/src/components/asset-viewers/shader-source.ts +144 -0
  398. package/src/components/asset-workflow.css +731 -0
  399. package/src/components/board-guides.ts +150 -0
  400. package/src/components/compact-inspector.css +539 -0
  401. package/src/components/core-utilities.tsx +90 -0
  402. package/src/components/editor-notifications.css +40 -0
  403. package/src/components/engine-workspace.css +274 -0
  404. package/src/components/inspector-preview-section.tsx +238 -0
  405. package/src/components/inspector-property-grouping.ts +64 -0
  406. package/src/components/inspector-revert-label.ts +20 -0
  407. package/src/components/inspector-selection.ts +42 -0
  408. package/src/components/inspector-stories-gating.ts +171 -0
  409. package/src/components/inspector-transform-subject.ts +11 -0
  410. package/src/components/inspector-transform.ts +75 -0
  411. package/src/components/kind-documents.tsx +500 -0
  412. package/src/components/palette-action-publisher.tsx +140 -0
  413. package/src/components/primitives/DraftColorInput.tsx +74 -0
  414. package/src/components/product-shell.css +1829 -0
  415. package/src/components/project-tool-documents.tsx +410 -0
  416. package/src/components/scene-documents.tsx +223 -0
  417. package/src/components/stage-keyboard.tsx +37 -0
  418. package/src/components/stage-overlay-set.tsx +107 -0
  419. package/src/components/stage-presence-markers.ts +478 -0
  420. package/src/components/standard-viewport-dressing.ts +417 -0
  421. package/src/components/status-contributions.tsx +403 -0
  422. package/src/components/tool-documents.tsx +309 -0
  423. package/src/components/tool-schema-form.tsx +262 -0
  424. package/src/components/use-after-paint.ts +41 -0
  425. package/src/components/use-project-image-assets.ts +86 -0
  426. package/src/components/viewport-header-controls.css +90 -0
  427. package/src/components/viewport-surface-status.tsx +55 -0
  428. package/src/components/workspace-history.ts +32 -0
  429. package/src/components/workspace-static-panel-registry.tsx +152 -0
  430. package/src/components/workspace-surfaces.css +621 -0
  431. package/src/components/world-documents.tsx +586 -0
  432. package/src/components/world-overlay-gestures.ts +1694 -0
  433. package/src/composite-screenshot.ts +1641 -0
  434. package/src/console-sync.ts +131 -0
  435. package/src/constraint-helper.ts +338 -0
  436. package/src/content-entry-source-registry.ts +184 -0
  437. package/src/coverage/authoring-seam-evidence.ts +300 -0
  438. package/src/coverage/canvas-reveal.ts +192 -0
  439. package/src/coverage/design-time-surfaces.ts +101 -0
  440. package/src/coverage/live-seam-evidence.ts +11 -0
  441. package/src/coverage/ontology-invariants.ts +466 -0
  442. package/src/coverage/session-vitals.ts +501 -0
  443. package/src/coverage/system-seam-evidence.ts +72 -0
  444. package/src/crash-null-boundary.ts +36 -0
  445. package/src/creation-site-edit.ts +1479 -0
  446. package/src/creation-site-registry.ts +160 -0
  447. package/src/delegate-harness-registry.ts +143 -0
  448. package/src/document-context-registry.ts +177 -0
  449. package/src/document-open-registry.ts +200 -0
  450. package/src/document-preview-source.ts +20 -0
  451. package/src/document-renderer-session.ts +138 -0
  452. package/src/editor-api.ts +46 -0
  453. package/src/editor-chrome-capture.ts +139 -0
  454. package/src/editor-commands.ts +164 -0
  455. package/src/editor-console.ts +563 -0
  456. package/src/editor-current-view.ts +82 -0
  457. package/src/editor-document-probe.ts +881 -0
  458. package/src/editor-git-client.ts +115 -0
  459. package/src/editor-host-door.ts +462 -0
  460. package/src/editor-hotkeys.ts +842 -0
  461. package/src/editor-lease-view.ts +39 -0
  462. package/src/editor-lease.ts +415 -0
  463. package/src/editor-mode.ts +19 -0
  464. package/src/editor-notifications.ts +140 -0
  465. package/src/editor-presence.ts +563 -0
  466. package/src/editor-presentation-activity.ts +50 -0
  467. package/src/editor-presentation-notice.ts +42 -0
  468. package/src/editor-runtime.tsx +145 -0
  469. package/src/editor-server-response.ts +86 -0
  470. package/src/editor-session-attribution.ts +75 -0
  471. package/src/editor-session-mode.ts +54 -0
  472. package/src/editor-shell-store.ts +1304 -0
  473. package/src/editor-state-facets.ts +74 -0
  474. package/src/editor-styles.css +33 -0
  475. package/src/editor-view-presentation.ts +810 -0
  476. package/src/editor-viewport.ts +5302 -0
  477. package/src/entity-lod.ts +31 -0
  478. package/src/entity-object.ts +91 -0
  479. package/src/files/file-provider.ts +62 -0
  480. package/src/files/project-files.ts +264 -0
  481. package/src/finders/index.ts +136 -0
  482. package/src/finders/scenes-from-entrypoint-selection.ts +387 -0
  483. package/src/frame/bridge.tsx +1408 -0
  484. package/src/frame/product.ts +77 -0
  485. package/src/gameplay-dom-recording.ts +319 -0
  486. package/src/gameplay-export-state.ts +14 -0
  487. package/src/gameplay-replay.ts +417 -0
  488. package/src/gameplay-session-time.ts +9 -0
  489. package/src/gameplay-sessions.ts +204 -0
  490. package/src/harness-chat-types.ts +253 -0
  491. package/src/hierarchy-component-marks.ts +298 -0
  492. package/src/hierarchy-drop.ts +91 -0
  493. package/src/hierarchy-expansion-state.ts +80 -0
  494. package/src/hierarchy-header-slot.ts +52 -0
  495. package/src/hierarchy-internals.ts +197 -0
  496. package/src/hierarchy-kind-icon.ts +217 -0
  497. package/src/hierarchy-mark-reader.ts +73 -0
  498. package/src/hierarchy-menu-registry.ts +67 -0
  499. package/src/hierarchy-node-rows.ts +307 -0
  500. package/src/hierarchy-panel-view.ts +280 -0
  501. package/src/hierarchy-projection.ts +76 -0
  502. package/src/hierarchy-row-cache.ts +243 -0
  503. package/src/hierarchy-row-model.ts +308 -0
  504. package/src/hierarchy-rows.ts +11 -0
  505. package/src/hierarchy-walk.ts +86 -0
  506. package/src/history/editor-session.ts +25 -0
  507. package/src/history/history-commands.ts +147 -0
  508. package/src/history/history-delegate.ts +163 -0
  509. package/src/history/history-limit-notices.ts +43 -0
  510. package/src/history/history-service.ts +1173 -0
  511. package/src/history/persistence-coordinator.ts +35 -0
  512. package/src/history/project-file-history.ts +386 -0
  513. package/src/history/project-root-history-backends.ts +139 -0
  514. package/src/history/resource-registry.ts +209 -0
  515. package/src/history/snapshot-store.ts +103 -0
  516. package/src/history/source-history-backend.ts +546 -0
  517. package/src/history/types.ts +124 -0
  518. package/src/hmr-registration-group.ts +67 -0
  519. package/src/hmr-stable-react-context.ts +23 -0
  520. package/src/hotkeys.ts +188 -0
  521. package/src/inference-diagnostics.ts +69 -0
  522. package/src/initial-project.ts +80 -0
  523. package/src/inspection/active-subject.ts +578 -0
  524. package/src/inspection/active-surface.ts +142 -0
  525. package/src/inspection/compose.ts +1064 -0
  526. package/src/inspection/display.ts +170 -0
  527. package/src/inspection/document-subject.ts +109 -0
  528. package/src/inspection/game-subject.ts +85 -0
  529. package/src/inspection/model.ts +542 -0
  530. package/src/inspection/null-subject.ts +115 -0
  531. package/src/inspection/serialize.ts +357 -0
  532. package/src/inspection/use-active-inspection.ts +180 -0
  533. package/src/inspector-presentation.ts +201 -0
  534. package/src/inspector-section-registry.ts +221 -0
  535. package/src/instance-source-actions.ts +163 -0
  536. package/src/instanced-presentation.ts +164 -0
  537. package/src/js-heap.ts +71 -0
  538. package/src/key-actions.ts +91 -0
  539. package/src/keymap-presets.ts +401 -0
  540. package/src/layout-policy.ts +31 -0
  541. package/src/learn-links.ts +73 -0
  542. package/src/light-explorer-model.ts +134 -0
  543. package/src/live-canvas-frame.ts +55 -0
  544. package/src/live-document.ts +279 -0
  545. package/src/live-module-source.ts +230 -0
  546. package/src/live-session-registry.ts +220 -0
  547. package/src/live-transition.ts +633 -0
  548. package/src/manifest-project.ts +107 -0
  549. package/src/model-thumbnail.ts +523 -0
  550. package/src/native-selection-style.ts +202 -0
  551. package/src/object3d-document-write-policy.ts +137 -0
  552. package/src/packaged-runtime.ts +108 -0
  553. package/src/palettes/maya.palette.json +57 -0
  554. package/src/palettes/substance.palette.json +57 -0
  555. package/src/pasteboard-module.ts +81 -0
  556. package/src/performance-profiler.ts +367 -0
  557. package/src/performance-sources.ts +69 -0
  558. package/src/play-boot-phase.ts +145 -0
  559. package/src/presentation-surface.ts +248 -0
  560. package/src/project-adapter.ts +1143 -0
  561. package/src/project-asset-refresh.ts +26 -0
  562. package/src/project-declaration-refresh.ts +31 -0
  563. package/src/project-local-state.ts +118 -0
  564. package/src/project-manager.ts +243 -0
  565. package/src/project-module-changes.ts +163 -0
  566. package/src/project-module-split.ts +266 -0
  567. package/src/project-provenance.ts +115 -0
  568. package/src/project-ready.ts +42 -0
  569. package/src/project-session-reset.ts +67 -0
  570. package/src/project-shape.ts +68 -0
  571. package/src/project-tool-discovery.ts +66 -0
  572. package/src/project-tools.ts +107 -0
  573. package/src/project-work-types.ts +149 -0
  574. package/src/projection/three.ts +898 -0
  575. package/src/projection/types.ts +44 -0
  576. package/src/readiness.ts +113 -0
  577. package/src/reflection-probe-helper.ts +142 -0
  578. package/src/reported-play-state.ts +90 -0
  579. package/src/resolve-relative-specifier.ts +33 -0
  580. package/src/scene-document-plan.ts +315 -0
  581. package/src/scene-framing.ts +315 -0
  582. package/src/scene-live-open.ts +210 -0
  583. package/src/scene-view-fog.ts +89 -0
  584. package/src/scoped-game-css.ts +152 -0
  585. package/src/session-orphan-record.ts +193 -0
  586. package/src/session-tombstone.ts +126 -0
  587. package/src/settings/settings-provider.ts +82 -0
  588. package/src/settings-store.ts +345 -0
  589. package/src/shared-view-restore.ts +42 -0
  590. package/src/shell-store-door.ts +45 -0
  591. package/src/source-conflict.ts +122 -0
  592. package/src/spatial-handle-visuals.ts +332 -0
  593. package/src/stage-context.ts +341 -0
  594. package/src/stage-store-registry.ts +63 -0
  595. package/src/stale-chunk-recovery.ts +34 -0
  596. package/src/state-report-deferral.ts +73 -0
  597. package/src/storage/host-files-storage.ts +97 -0
  598. package/src/storage/http-storage.ts +174 -0
  599. package/src/storage/index.ts +75 -0
  600. package/src/storage/mem-storage.ts +149 -0
  601. package/src/storage/path-lock.ts +44 -0
  602. package/src/storage/paths.ts +26 -0
  603. package/src/storage/types.ts +116 -0
  604. package/src/stories/StoryComponentThumbnail.tsx +184 -0
  605. package/src/stories/StoryPreviewMount.tsx +306 -0
  606. package/src/stories/component-content-source.tsx +106 -0
  607. package/src/stories/component-states-source.ts +78 -0
  608. package/src/stories/compose-project-stories.ts +255 -0
  609. package/src/stories/pixi-story-model.ts +30 -0
  610. package/src/stories/prefabs-finder.ts +54 -0
  611. package/src/stories/prefabs-from-stories.ts +182 -0
  612. package/src/stories/project-story-discovery.ts +78 -0
  613. package/src/stories/project-story-regions.ts +24 -0
  614. package/src/stories/story-actions.ts +54 -0
  615. package/src/stories/story-capture-command.ts +124 -0
  616. package/src/stories/story-capture.ts +584 -0
  617. package/src/stories/story-declared-medium.ts +126 -0
  618. package/src/stories/story-discovery.ts +176 -0
  619. package/src/stories/story-document-openers.ts +36 -0
  620. package/src/stories/story-dom-runtime.ts +78 -0
  621. package/src/stories/story-grouping.ts +111 -0
  622. package/src/stories/story-lane.ts +88 -0
  623. package/src/stories/story-mount-turn.ts +27 -0
  624. package/src/stories/story-opener.ts +260 -0
  625. package/src/stories/story-pixi-preview.ts +408 -0
  626. package/src/stories/story-presentation.ts +215 -0
  627. package/src/stories/story-registry.ts +517 -0
  628. package/src/stories/story-three-preview.ts +807 -0
  629. package/src/stories/three-story-model.ts +96 -0
  630. package/src/story-three-preview-runtime.ts +56 -0
  631. package/src/surface-keyboard.ts +112 -0
  632. package/src/surface-state.ts +124 -0
  633. package/src/tab-bootstrap.js +628 -0
  634. package/src/tab-census.ts +198 -0
  635. package/src/tab-lifecycle-client.ts +196 -0
  636. package/src/theme-library.ts +844 -0
  637. package/src/theme-preference.ts +378 -0
  638. package/src/theme.css +3437 -0
  639. package/src/three-viewport/bone-selection-highlight.ts +119 -0
  640. package/src/three-viewport/camera-fit.ts +41 -0
  641. package/src/three-viewport/interactive-renderer.ts +132 -0
  642. package/src/three-viewport/selection-brackets.ts +310 -0
  643. package/src/three-viewport/selection-outline.ts +183 -0
  644. package/src/three-viewport/skeleton-helper.ts +61 -0
  645. package/src/three-viewport/source-color.ts +167 -0
  646. package/src/three-viewport/studio-environment.ts +96 -0
  647. package/src/three-viewport-presentation.ts +23 -0
  648. package/src/tool-contribution-play.ts +74 -0
  649. package/src/tool-loader.ts +1798 -0
  650. package/src/transform-mode-request.ts +57 -0
  651. package/src/transient-hint.ts +78 -0
  652. package/src/trigger-volume-helper.ts +116 -0
  653. package/src/ui-source/adapter-region-includes.ts +241 -0
  654. package/src/ui-source/ensure-import.ts +132 -0
  655. package/src/ui-source/file-region-resolver.ts +302 -0
  656. package/src/ui-source/inspect.ts +775 -0
  657. package/src/ui-source/oid-transform.ts +1845 -0
  658. package/src/ui-source/plan-csf-story.ts +196 -0
  659. package/src/ui-source/plan-extract-component.ts +421 -0
  660. package/src/ui-source/plan-fork-component.ts +621 -0
  661. package/src/ui-source/plan-named-style.ts +95 -0
  662. package/src/ui-source/plan-source-edit.ts +353 -0
  663. package/src/ui-source/r3f-contract-resolver.ts +327 -0
  664. package/src/ui-source/r3f-diagnostic-index.ts +165 -0
  665. package/src/ui-source/r3f-environment-binding.ts +133 -0
  666. package/src/ui-source/r3f-joint-binding.ts +251 -0
  667. package/src/ui-source/r3f-lod-binding.ts +91 -0
  668. package/src/ui-source/r3f-particle-binding.ts +235 -0
  669. package/src/ui-source/r3f-physics-binding.ts +483 -0
  670. package/src/ui-source/r3f-project-contracts.ts +444 -0
  671. package/src/ui-source/relative-import-specifier.ts +34 -0
  672. package/src/ui-source/reparent-guard.ts +544 -0
  673. package/src/ui-source/source-edit-request.ts +97 -0
  674. package/src/ui-source/source-write-backend.ts +618 -0
  675. package/src/ui-source/syntactic-prop-specs.ts +296 -0
  676. package/src/ui-source/tier-source-write-backend.ts +279 -0
  677. package/src/ui-source/ts-ast.ts +171 -0
  678. package/src/ui-source/utility-class-support.ts +169 -0
  679. package/src/ui-source/write-component-default.ts +133 -0
  680. package/src/ui-source/writer.ts +1989 -0
  681. package/src/viewport-activation-timings.ts +840 -0
  682. package/src/viewport-authoring-policy.ts +186 -0
  683. package/src/viewport-controls-hint.ts +57 -0
  684. package/src/viewport-door.ts +237 -0
  685. package/src/viewport-shading-boundary.ts +12 -0
  686. package/src/vite-error-surface.ts +141 -0
  687. package/src/wait-until.ts +37 -0
  688. package/src/workspace-areas.ts +156 -0
  689. package/src/workspace-aux-commands.ts +11 -0
  690. package/src/workspace-available-documents.ts +127 -0
  691. package/src/workspace-core-utilities.ts +31 -0
  692. package/src/workspace-document-ids.ts +59 -0
  693. package/src/workspace-document-registry.ts +612 -0
  694. package/src/workspace-document-restore.ts +145 -0
  695. package/src/workspace-host-commands.ts +141 -0
  696. package/src/workspace-persistence-gate.ts +40 -0
  697. package/src/workspace-play-utilities.ts +44 -0
  698. package/src/workspace-presets.ts +425 -0
  699. package/src/workspace-regions.ts +294 -0
  700. package/src/workspace-state-persistence.ts +536 -0
  701. package/src/workspace-static-panels.ts +73 -0
  702. package/src/workspace-status-registry.ts +121 -0
  703. package/src/workspace-style.ts +239 -0
  704. package/src/workspace-utility-commands.ts +74 -0
  705. package/src/workspace-utility-registry.ts +263 -0
  706. package/src/workspace-viewport-rect.ts +97 -0
  707. package/src/world-adoption.ts +115 -0
  708. package/src/world-document-routing.ts +104 -0
  709. package/vite-plugin-creation-site-write.ts +184 -0
  710. package/vite-plugin-creation-site.ts +80 -0
  711. package/vite-plugin-game-static.ts +303 -0
  712. package/vite-plugin-module-doorways.ts +336 -0
  713. package/vite-plugin-product-contributions.ts +197 -0
  714. package/vite-plugin-project-game-static.ts +125 -0
  715. package/vite-plugin-project-jsx-js.ts +65 -0
  716. package/vite-plugin-project-root-absolute-assets.ts +359 -0
  717. package/vite-plugin-shared-react.ts +420 -0
  718. package/vite-plugin-shared-three.ts +203 -0
  719. package/vite-plugin-ui-oid.ts +2106 -0
@@ -0,0 +1,1162 @@
1
+ import { faCircleInfo } from '@fortawesome/free-solid-svg-icons';
2
+ import { Button, Checkbox, EditorIcon, Select, TextInput } from '@volter/editor-sdk/widgets';
3
+ import type {
4
+ AccountCatalog,
5
+ AccountPlanId,
6
+ AccountSpendPolicy,
7
+ AccountUsageEntry,
8
+ ProviderCredentialId,
9
+ ProviderCredentialStatus,
10
+ } from '@volter/editor-sdk/account';
11
+ import { useEffect, useState, useSyncExternalStore } from 'react';
12
+ import {
13
+ accountError,
14
+ accountSnapshot,
15
+ accountVersion,
16
+ beginAccountBrowserAuthorization,
17
+ beginAccountDeviceAuthorization,
18
+ checkoutAccountPlan,
19
+ fetchAccountAuthorizationMethods,
20
+ fetchAccountCatalog,
21
+ fetchAccountUsage,
22
+ markAccountAlertsRead,
23
+ openAccountBillingPortal,
24
+ pollAccountBrowserAuthorization,
25
+ pollAccountDeviceAuthorization,
26
+ purchaseAccountCredits,
27
+ quoteCodingInference,
28
+ removeProviderCredential,
29
+ saveProviderCredential,
30
+ signInMockAccount,
31
+ signInTwinAccount,
32
+ signOutAccount,
33
+ subscribeAccount,
34
+ testProviderCredential,
35
+ updateAccountPlan,
36
+ updateAccountSpendPolicy,
37
+ updateCodingInference,
38
+ updatePreferredGenerationRoute,
39
+ } from '../account';
40
+ import { registerDocumentOpener } from '../document-open-registry';
41
+ import { openWorkspaceDocument } from '../workspace-document-registry';
42
+ import { VgaiLogo } from './VgaiLogo';
43
+
44
+ export const ACCOUNT_DOCUMENT_ID = 'account';
45
+
46
+ export function openAccountDocument(): string {
47
+ return openWorkspaceDocument({
48
+ id: ACCOUNT_DOCUMENT_ID,
49
+ title: 'Account',
50
+ kind: 'tool-contribution',
51
+ workspaceRole: 'workspace-task',
52
+ provenance: { origin: 'Volter account' },
53
+ Content: AccountDocument,
54
+ closeable: true,
55
+ presentation: () => ({ kind: 'workspace', id: ACCOUNT_DOCUMENT_ID }),
56
+ });
57
+ }
58
+
59
+ /**
60
+ * The account document's own ADDRESS (`document-open-registry.ts`). Its
61
+ * `presentation()` above already says the address is `{ kind: 'workspace', id:
62
+ * 'account' }`; this is the other half — what turns that address back into
63
+ * this document without the presenter importing this module. Registered at
64
+ * module load, the shape `packages/game/src/story-documents/
65
+ * three-story-documents.tsx:239` uses.
66
+ *
67
+ * `null` for any other workspace id: several modules answer the `workspace`
68
+ * address (the project-tools catalog is the other), and every id this one does
69
+ * not own belongs to the host's own workspace-document registry.
70
+ */
71
+ registerDocumentOpener<{ readonly id: string }>({
72
+ id: 'workspace',
73
+ owner: 'account-documents',
74
+ open: (_store, request) => (request.id === ACCOUNT_DOCUMENT_ID ? openAccountDocument() : null),
75
+ });
76
+
77
+ type AccountActionRunner = (action: () => Promise<unknown>, success?: string) => Promise<void>;
78
+
79
+ function providerSource(provider: ProviderCredentialStatus): string {
80
+ if (provider.source === 'environment') {
81
+ return `Environment · ${provider.environmentVariable}`;
82
+ }
83
+ if (provider.source === 'system') return 'Secure system storage';
84
+ if (provider.source === 'session')
85
+ return provider.configured ? 'Session only' : 'Session storage';
86
+ return 'Not configured';
87
+ }
88
+
89
+ /** Quiet blue-tinted information strip (spec §4) — one icon, one line. */
90
+ function InfoStrip({ children }: { children: React.ReactNode }) {
91
+ return (
92
+ <div className="vgai-account-infostrip">
93
+ <EditorIcon icon={faCircleInfo} className="vgai-account-infostrip-icon" aria-hidden="true" />
94
+ <span>{children}</span>
95
+ </div>
96
+ );
97
+ }
98
+
99
+ interface ProviderRowProps {
100
+ provider: ProviderCredentialStatus;
101
+ busy: boolean;
102
+ run: AccountActionRunner;
103
+ editing: boolean;
104
+ removing: boolean;
105
+ onEdit: () => void;
106
+ onStopEditing: () => void;
107
+ onRemoveIntent: (provider: ProviderCredentialId | null) => void;
108
+ }
109
+
110
+ function ProviderRow({
111
+ provider,
112
+ busy,
113
+ run,
114
+ editing,
115
+ removing,
116
+ onEdit,
117
+ onStopEditing,
118
+ onRemoveIntent,
119
+ }: ProviderRowProps) {
120
+ const [key, setKey] = useState('');
121
+ return (
122
+ <div className="vgai-account-provider" data-configured={provider.configured}>
123
+ <span className="vgai-account-provider-monogram" aria-hidden="true">
124
+ {provider.label.slice(0, 1)}
125
+ </span>
126
+ <div className="vgai-account-provider-main">
127
+ <div className="vgai-account-provider-name">
128
+ <strong>{provider.label}</strong>
129
+ {provider.configured && (
130
+ <>
131
+ <span className="vgai-account-provider-dot" aria-hidden="true" />
132
+ <span className="vgai-account-provider-key">{provider.maskedKey}</span>
133
+ </>
134
+ )}
135
+ </div>
136
+ <span className="vgai-account-provider-source">{providerSource(provider)}</span>
137
+ {provider.source === 'session' && provider.configured && (
138
+ <span className="vgai-account-provider-warning">
139
+ No system credential manager is available. This key will be forgotten when the editor
140
+ stops.
141
+ </span>
142
+ )}
143
+ {provider.problem && (
144
+ <span className="vgai-account-provider-warning" role="alert">
145
+ {provider.problem}
146
+ </span>
147
+ )}
148
+ {provider.source === 'environment' && (
149
+ <span className="vgai-account-provider-source">
150
+ Restart the editor after changing {provider.environmentVariable}.
151
+ </span>
152
+ )}
153
+ </div>
154
+
155
+ {editing ? (
156
+ <form
157
+ className="vgai-account-provider-form"
158
+ onSubmit={(event) => {
159
+ event.preventDefault();
160
+ void run(async () => {
161
+ await saveProviderCredential(provider.provider, key);
162
+ setKey('');
163
+ onStopEditing();
164
+ }, `${provider.label} credential saved.`);
165
+ }}
166
+ >
167
+ <label className="vgai-field">
168
+ <span className="vgai-field-label">{provider.label} API key</span>
169
+ <TextInput
170
+ autoComplete="off"
171
+ autoFocus
172
+ type="password"
173
+ value={key}
174
+ onChange={(event) => setKey(event.target.value)}
175
+ />
176
+ </label>
177
+ <div className="vgai-account-provider-form-actions">
178
+ <Button type="submit" variant="primary" disabled={busy || key.trim() === ''}>
179
+ Save key
180
+ </Button>
181
+ <Button
182
+ type="button"
183
+ variant="ghost"
184
+ disabled={busy}
185
+ onClick={() => {
186
+ setKey('');
187
+ onStopEditing();
188
+ }}
189
+ >
190
+ Cancel
191
+ </Button>
192
+ </div>
193
+ </form>
194
+ ) : (
195
+ <div className="vgai-account-provider-actions">
196
+ {provider.configured && (
197
+ <Button
198
+ size="compact"
199
+ disabled={busy}
200
+ onClick={() =>
201
+ void run(
202
+ () => testProviderCredential(provider.provider),
203
+ `${provider.label} credential is valid.`,
204
+ )
205
+ }
206
+ >
207
+ Test
208
+ </Button>
209
+ )}
210
+ {provider.editable && (
211
+ <Button
212
+ size="compact"
213
+ /* Spec §3: "Add key" (the row's one constructive action) is a
214
+ * bordered secondary; "Replace" on an already-connected row
215
+ * stays quiet. */
216
+ variant={provider.configured ? 'ghost' : 'secondary'}
217
+ disabled={busy}
218
+ onClick={() => {
219
+ /* A row's editor can be dismissed WITHOUT its Cancel/Save path
220
+ * (opening another row's editor closes this one), which would
221
+ * leave half-typed text to reappear on reopen. Opening always
222
+ * starts from an empty field. */
223
+ setKey('');
224
+ onEdit();
225
+ }}
226
+ >
227
+ {provider.configured ? 'Replace' : 'Add key'}
228
+ </Button>
229
+ )}
230
+ {provider.editable && provider.configured && !removing && (
231
+ <Button
232
+ size="compact"
233
+ variant="ghost"
234
+ disabled={busy}
235
+ onClick={() => onRemoveIntent(provider.provider)}
236
+ >
237
+ Remove
238
+ </Button>
239
+ )}
240
+ {removing && (
241
+ <>
242
+ <Button
243
+ size="compact"
244
+ variant="danger"
245
+ disabled={busy}
246
+ onClick={() =>
247
+ void run(async () => {
248
+ await removeProviderCredential(provider.provider);
249
+ onRemoveIntent(null);
250
+ }, `${provider.label} credential removed.`)
251
+ }
252
+ >
253
+ Confirm remove
254
+ </Button>
255
+ <Button
256
+ size="compact"
257
+ variant="ghost"
258
+ disabled={busy}
259
+ onClick={() => onRemoveIntent(null)}
260
+ >
261
+ Cancel
262
+ </Button>
263
+ </>
264
+ )}
265
+ </div>
266
+ )}
267
+ </div>
268
+ );
269
+ }
270
+
271
+ function ProviderConnections({ busy, run }: { busy: boolean; run: AccountActionRunner }) {
272
+ const account = accountSnapshot();
273
+ const [editing, setEditing] = useState<ProviderCredentialId | null>(null);
274
+ const [removing, setRemoving] = useState<ProviderCredentialId | null>(null);
275
+
276
+ const connectedCount = account.providerCredentials.filter(
277
+ (provider) => provider.configured,
278
+ ).length;
279
+
280
+ return (
281
+ <section className="vgai-account-card vgai-provider-connections">
282
+ <div className="vgai-account-providers-header">
283
+ <div>
284
+ <h3 className="vgai-account-card-title">Provider connections</h3>
285
+ <p className="vgai-account-card-sub">
286
+ Keys live in this computer&apos;s credential manager — never in project files or the
287
+ editor UI.
288
+ </p>
289
+ </div>
290
+ <span className="vgai-status-dot" data-state={connectedCount > 0 ? 'on' : 'off'}>
291
+ {connectedCount} connected
292
+ </span>
293
+ </div>
294
+ <div className="vgai-account-provider-list">
295
+ {account.providerCredentials.map((provider) => (
296
+ <ProviderRow
297
+ key={provider.provider}
298
+ provider={provider}
299
+ busy={busy}
300
+ run={run}
301
+ editing={editing === provider.provider}
302
+ removing={removing === provider.provider}
303
+ onEdit={() => {
304
+ setRemoving(null);
305
+ setEditing(provider.provider);
306
+ }}
307
+ onStopEditing={() => setEditing(null)}
308
+ onRemoveIntent={setRemoving}
309
+ />
310
+ ))}
311
+ </div>
312
+ </section>
313
+ );
314
+ }
315
+
316
+ function CodingInferenceCard({ busy, run }: { busy: boolean; run: AccountActionRunner }) {
317
+ const account = accountSnapshot();
318
+ const settings = account.codingInference;
319
+ const [model, setModel] = useState(settings.model);
320
+ const [quote, setQuote] = useState<Awaited<ReturnType<typeof quoteCodingInference>> | null>(null);
321
+ const [quoteProblem, setQuoteProblem] = useState<string | null>(null);
322
+ useEffect(() => setModel(settings.model), [settings.model]);
323
+ useEffect(() => {
324
+ setQuote(null);
325
+ setQuoteProblem(null);
326
+ }, [model, account.preferredRoute]);
327
+
328
+ const save = (next: { enabled?: boolean; model?: string }) =>
329
+ run(
330
+ () =>
331
+ updateCodingInference({
332
+ ...settings,
333
+ ...next,
334
+ model: (next.model ?? settings.model).trim(),
335
+ }),
336
+ 'Coding inference settings saved.',
337
+ );
338
+
339
+ return (
340
+ <section className="vgai-account-card">
341
+ <h3 className="vgai-account-card-title">Coding inference</h3>
342
+ <p className="vgai-account-card-sub">
343
+ Coding agents run on the login you already did — <code>codex login</code>, Claude&apos;s
344
+ device login, <code>grok login</code> — and Volter Editor injects nothing into them. Turn this on
345
+ only for a harness you have no subscription for: it routes that harness through OpenRouter
346
+ on this account&apos;s managed credits or your own key, and it takes effect only while
347
+ Preferred route above is Managed or BYOK. Existing sessions keep the route they launched
348
+ with.
349
+ </p>
350
+ <label className="vgai-account-switch-row">
351
+ <Checkbox
352
+ checked={settings.enabled}
353
+ disabled={busy}
354
+ onChange={(event) => void save({ enabled: event.target.checked })}
355
+ />
356
+ <span className="vgai-account-switch-copy">
357
+ <strong>Use OpenRouter for coding agents</strong>
358
+ <small>
359
+ Codex, Claude Code, OpenCode and Pi are configured per spawned process. Grok takes no
360
+ provider configuration and always runs on its own device login.
361
+ </small>
362
+ </span>
363
+ </label>
364
+ <label className="vgai-field">
365
+ <span className="vgai-field-label">Managed coding model</span>
366
+ <Select
367
+ value={model}
368
+ disabled={busy}
369
+ onChange={(event) => {
370
+ const next = event.target.value;
371
+ setModel(next);
372
+ if (next !== settings.model) void save({ model: next });
373
+ }}
374
+ >
375
+ {!['z-ai/glm-5.2', 'moonshotai/kimi-k3'].includes(model) && (
376
+ <option value={model}>{model} · custom</option>
377
+ )}
378
+ <option value="z-ai/glm-5.2">GLM-5.2 · managed default</option>
379
+ <option value="moonshotai/kimi-k3">Kimi K3 · higher-cost option</option>
380
+ </Select>
381
+ </label>
382
+ {account.preferredRoute === 'managed' ? (
383
+ <div className="vgai-account-card-footer">
384
+ <Button
385
+ size="compact"
386
+ disabled={busy}
387
+ onClick={() => {
388
+ setQuoteProblem(null);
389
+ void quoteCodingInference({ model, promptTokens: 100_000, outputTokens: 20_000 })
390
+ .then(setQuote)
391
+ .catch((cause) =>
392
+ setQuoteProblem(cause instanceof Error ? cause.message : String(cause)),
393
+ );
394
+ }}
395
+ >
396
+ Preflight typical coding session
397
+ </Button>
398
+ {quote && (
399
+ <span className="vgai-account-help">
400
+ Estimate: {quote.estimatedCredits.toLocaleString()} Volter credits ($
401
+ {quote.providerAmountUsd.toFixed(4)} provider cost) for 100k input + 20k output
402
+ tokens.
403
+ </span>
404
+ )}
405
+ {quoteProblem && (
406
+ <span className="vgai-account-provider-warning" role="alert">
407
+ {quoteProblem}
408
+ </span>
409
+ )}
410
+ </div>
411
+ ) : account.preferredRoute === 'byok' ? (
412
+ <InfoStrip>
413
+ BYOK coding charges OpenRouter directly and consumes zero Volter credits.
414
+ </InfoStrip>
415
+ ) : null}
416
+ <InfoStrip>
417
+ Active account route: {account.preferredRoute}. BYOK requires the OpenRouter connection
418
+ below; managed uses the Volter balance.
419
+ </InfoStrip>
420
+ </section>
421
+ );
422
+ }
423
+
424
+ /** Plan/status pills shown beside the email in the identity header. Suppresses
425
+ * a status that merely repeats the plan (the mock account reports plan "Free"
426
+ * with status "free"). */
427
+ function PlanChips({
428
+ plan,
429
+ }: {
430
+ plan: {
431
+ name: string;
432
+ status: string;
433
+ renewsAt?: string | undefined;
434
+ endsAt?: string | undefined;
435
+ };
436
+ }) {
437
+ const status = plan.status.replace('_', ' ');
438
+ const redundant =
439
+ plan.status === 'active' || status.toLowerCase() === plan.name.trim().toLowerCase();
440
+ return (
441
+ <span className="vgai-account-plan-chips">
442
+ <span className="vgai-chip" data-tone="accent">
443
+ {plan.name} plan
444
+ </span>
445
+ {!redundant && (
446
+ <span className="vgai-chip" data-tone={plan.status === 'cancelling' ? 'warn' : undefined}>
447
+ {status}
448
+ </span>
449
+ )}
450
+ {(plan.renewsAt || plan.endsAt) && (
451
+ <span className="vgai-chip">
452
+ {plan.endsAt ? 'Access ends' : 'Renews'}{' '}
453
+ {new Date(plan.endsAt ?? plan.renewsAt!).toLocaleDateString()}
454
+ </span>
455
+ )}
456
+ </span>
457
+ );
458
+ }
459
+
460
+ /** Global account subject. It can be hosted either as an editor workspace
461
+ * document or directly by the projectless product shell. Account ownership is
462
+ * product-global, so rendering it must never require an open game project. */
463
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: One account form intentionally keeps interdependent plan, balance, route, and spend-policy disclosure in a single inspectable document.
464
+ export function AccountDocument() {
465
+ useSyncExternalStore(subscribeAccount, accountVersion, accountVersion);
466
+ const account = accountSnapshot();
467
+ const [email, setEmail] = useState('builder@example.test');
468
+ const [usage, setUsage] = useState<AccountUsageEntry[]>([]);
469
+ const [catalog, setCatalog] = useState<AccountCatalog | null>(null);
470
+ const [selectedPlan, setSelectedPlan] = useState<AccountPlanId>('creator');
471
+ const [selectedPack, setSelectedPack] = useState('');
472
+ const [busy, setBusy] = useState(false);
473
+ const [notice, setNotice] = useState<string | null>(null);
474
+ const [authorization, setAuthorization] = useState<Awaited<
475
+ ReturnType<typeof beginAccountDeviceAuthorization>
476
+ > | null>(null);
477
+ const [browserAuthorization, setBrowserAuthorization] = useState<Awaited<
478
+ ReturnType<typeof beginAccountBrowserAuthorization>
479
+ > | null>(null);
480
+ const [deviceAuthorizationAvailable, setDeviceAuthorizationAvailable] = useState(false);
481
+
482
+ useEffect(() => {
483
+ if (
484
+ account.authenticated ||
485
+ account.backend !== 'live' ||
486
+ account.accountEnvironment === 'development-twin'
487
+ ) {
488
+ setDeviceAuthorizationAvailable(false);
489
+ return;
490
+ }
491
+ void fetchAccountAuthorizationMethods()
492
+ .then((methods) => setDeviceAuthorizationAvailable(methods.device))
493
+ .catch(() => setDeviceAuthorizationAvailable(false));
494
+ }, [account.authenticated, account.backend]);
495
+
496
+ useEffect(() => {
497
+ if (!browserAuthorization) return;
498
+ const poll = () => {
499
+ void pollAccountBrowserAuthorization(browserAuthorization.authorizationId)
500
+ .then((complete) => {
501
+ if (complete) setBrowserAuthorization(null);
502
+ })
503
+ .catch((cause) => {
504
+ setNotice(cause instanceof Error ? cause.message : String(cause));
505
+ setBrowserAuthorization(null);
506
+ });
507
+ };
508
+ poll();
509
+ const timer = window.setInterval(poll, 1_000);
510
+ return () => window.clearInterval(timer);
511
+ }, [browserAuthorization]);
512
+
513
+ useEffect(() => {
514
+ if (!authorization) return;
515
+ const poll = () => {
516
+ void pollAccountDeviceAuthorization(authorization.deviceCode)
517
+ .then((complete) => {
518
+ if (complete) setAuthorization(null);
519
+ })
520
+ .catch((cause) => {
521
+ setNotice(cause instanceof Error ? cause.message : String(cause));
522
+ setAuthorization(null);
523
+ });
524
+ };
525
+ const timer = window.setInterval(poll, Math.max(1, authorization.intervalSeconds) * 1_000);
526
+ return () => window.clearInterval(timer);
527
+ }, [authorization]);
528
+
529
+ useEffect(() => {
530
+ if (!account.authenticated) {
531
+ setUsage([]);
532
+ return;
533
+ }
534
+ void fetchAccountUsage()
535
+ .then(setUsage)
536
+ .catch(() => setUsage([]));
537
+ }, [
538
+ account.authenticated,
539
+ account.authenticated ? account.user.id : 'signed-out',
540
+ account.authenticated ? account.credits.used : 0,
541
+ ]);
542
+
543
+ useEffect(() => {
544
+ if (!account.authenticated) {
545
+ setCatalog(null);
546
+ return;
547
+ }
548
+ void fetchAccountCatalog()
549
+ .then((next) => {
550
+ setCatalog(next);
551
+ const paid = (['creator', 'max', 'ultra'] as const).find((id) => id !== account.plan.id);
552
+ setSelectedPlan(paid ?? 'creator');
553
+ setSelectedPack(Object.keys(next.creditPacks)[0] ?? '');
554
+ })
555
+ .catch(() => setCatalog(null));
556
+ }, [account.authenticated, account.authenticated ? account.user.id : 'signed-out']);
557
+
558
+ const run = async (action: () => Promise<unknown>, success?: string) => {
559
+ setBusy(true);
560
+ setNotice(null);
561
+ try {
562
+ await action();
563
+ if (success) setNotice(success);
564
+ } catch (cause) {
565
+ setNotice(cause instanceof Error ? cause.message : String(cause));
566
+ } finally {
567
+ setBusy(false);
568
+ }
569
+ };
570
+
571
+ if (!account.authenticated) {
572
+ return (
573
+ <div className="vgai-account" data-testid="account-document">
574
+ <div className="vgai-account-signin-stack">
575
+ {/* §4-R cinematic sign-in: the key-lit mark floats on the CANVAS above
576
+ * the card (hub empty-state anatomy) — the card carries only the form. */}
577
+ <div className="vgai-account-signin-hero">
578
+ <span className="vgai-account-signin-mark" aria-hidden="true">
579
+ <VgaiLogo size={52} animation="static" />
580
+ </span>
581
+ <h1 className="vgai-account-signin-title">Sign in to Volter Editor</h1>
582
+ </div>
583
+ {account.accountEnvironment === 'development-twin' ? (
584
+ <form
585
+ className="vgai-account-card vgai-account-signin-card"
586
+ onSubmit={(event) => {
587
+ event.preventDefault();
588
+ void run(() => signInTwinAccount(email), 'Signed in through the local Clerk Twin.');
589
+ }}
590
+ >
591
+ <label className="vgai-field">
592
+ <span className="vgai-field-label">Development email</span>
593
+ <TextInput
594
+ value={email}
595
+ type="email"
596
+ onChange={(event) => setEmail(event.target.value)}
597
+ />
598
+ </label>
599
+ <Button type="submit" variant="primary" disabled={busy || !email.includes('@')}>
600
+ {busy ? 'Signing in…' : 'Use Clerk Twin account'}
601
+ </Button>
602
+ <InfoStrip>
603
+ Local Clerk and Polar twins — fake identity and checkout, with the real Volter account
604
+ and credit boundaries.
605
+ </InfoStrip>
606
+ </form>
607
+ ) : account.backend === 'live' ? (
608
+ <section className="vgai-account-card vgai-account-signin-card">
609
+ <Button
610
+ variant="primary"
611
+ disabled={busy || authorization !== null || browserAuthorization !== null}
612
+ onClick={() =>
613
+ void run(async () => {
614
+ const next = await beginAccountBrowserAuthorization();
615
+ setBrowserAuthorization(next);
616
+ window.open(next.authorizationUrl, '_blank', 'noopener,noreferrer');
617
+ })
618
+ }
619
+ >
620
+ {browserAuthorization ? 'Waiting for browser sign-in…' : 'Continue in browser'}
621
+ </Button>
622
+ {deviceAuthorizationAvailable && (
623
+ <Button
624
+ variant="ghost"
625
+ disabled={busy || authorization !== null || browserAuthorization !== null}
626
+ onClick={() =>
627
+ void run(async () => {
628
+ const next = await beginAccountDeviceAuthorization();
629
+ setAuthorization(next);
630
+ window.open(next.verificationUri, '_blank', 'noopener,noreferrer');
631
+ })
632
+ }
633
+ >
634
+ Use a device code instead
635
+ </Button>
636
+ )}
637
+ {authorization && (
638
+ <div className="vgai-account-device-code">
639
+ <strong>{authorization.userCode}</strong>
640
+ <span>
641
+ Complete authorization in the browser. This editor polls without receiving your
642
+ password.
643
+ </span>
644
+ </div>
645
+ )}
646
+ <InfoStrip>
647
+ Authorization happens in your browser — this editor never sees your password.
648
+ </InfoStrip>
649
+ </section>
650
+ ) : (
651
+ <form
652
+ className="vgai-account-card vgai-account-signin-card"
653
+ onSubmit={(event) => {
654
+ event.preventDefault();
655
+ void run(
656
+ () => signInMockAccount(email),
657
+ 'Signed in to the zero-charge local account.',
658
+ );
659
+ }}
660
+ >
661
+ <label className="vgai-field">
662
+ <span className="vgai-field-label">Email</span>
663
+ <TextInput
664
+ value={email}
665
+ type="email"
666
+ onChange={(event) => setEmail(event.target.value)}
667
+ />
668
+ </label>
669
+ <Button type="submit" variant="primary" disabled={busy || !email.includes('@')}>
670
+ {busy ? 'Signing in…' : 'Use local mock account'}
671
+ </Button>
672
+ <InfoStrip>Local mock — no payment, network identity, or provider request.</InfoStrip>
673
+ </form>
674
+ )}
675
+ <ul className="vgai-account-signin-points">
676
+ <li>Every paid generation shows its route and estimate first</li>
677
+ <li>Spend controls and hard limits are on by default</li>
678
+ <li>Provider keys stay in your system credential manager</li>
679
+ </ul>
680
+ <CodingInferenceCard busy={busy} run={run} />
681
+ <ProviderConnections busy={busy} run={run} />
682
+ {(notice || accountError()) && (
683
+ <div className="vgai-account-status" role="status">
684
+ {notice ?? accountError()}
685
+ </div>
686
+ )}
687
+ </div>
688
+ </div>
689
+ );
690
+ }
691
+
692
+ const totalCredits = account.credits.included + account.credits.purchased;
693
+ const usedPercent =
694
+ totalCredits === 0 ? 0 : Math.min(100, (account.credits.used / totalCredits) * 100);
695
+ const reservedPercent =
696
+ totalCredits === 0
697
+ ? 0
698
+ : Math.max(0, Math.min(100 - usedPercent, (account.credits.reserved / totalCredits) * 100));
699
+ const overQuota = totalCredits > 0 && account.credits.used > totalCredits;
700
+ const managedAccess = account.backend === 'mock' || account.entitlements.generation;
701
+ const updatePolicy = (patch: Partial<AccountSpendPolicy>) =>
702
+ updateAccountSpendPolicy({ ...account.spendPolicy, ...patch });
703
+
704
+ return (
705
+ <div className="vgai-account" data-testid="account-document">
706
+ <h1 className="vgai-sr-only">Account</h1>
707
+ <div className="vgai-account-stack">
708
+ <header className="vgai-account-identity">
709
+ <span className="vgai-account-avatar" aria-hidden="true">
710
+ {account.user.email.slice(0, 1)}
711
+ </span>
712
+ <div className="vgai-account-identity-main">
713
+ <div className="vgai-account-identity-row">
714
+ <h2 className="vgai-account-identity-email">{account.user.email}</h2>
715
+ <PlanChips plan={account.plan} />
716
+ </div>
717
+ <p className="vgai-account-identity-note">
718
+ {account.accountEnvironment === 'development-twin'
719
+ ? 'Clerk + Polar Twin development account'
720
+ : account.backend === 'mock'
721
+ ? 'Isolated test account'
722
+ : 'Volter account'}
723
+ </p>
724
+ </div>
725
+ <Button
726
+ variant="ghost"
727
+ className="vgai-account-signout"
728
+ disabled={busy}
729
+ onClick={() => void run(signOutAccount)}
730
+ >
731
+ Sign out
732
+ </Button>
733
+ </header>
734
+
735
+ <section className="vgai-account-card">
736
+ <div className="vgai-account-card-head">
737
+ <h3 className="vgai-account-card-title">Credits</h3>
738
+ {account.plan.id !== 'free' && account.plan.status === 'cancelling' && (
739
+ <Button
740
+ size="compact"
741
+ disabled={busy}
742
+ onClick={() => void run(() => updateAccountPlan('resume'))}
743
+ >
744
+ Resume subscription
745
+ </Button>
746
+ )}
747
+ </div>
748
+ {!managedAccess && (
749
+ <InfoStrip>
750
+ This account has not been admitted to Volter managed services. Signing up or paying does
751
+ not grant provider access; contact the Volter team for admission.
752
+ </InfoStrip>
753
+ )}
754
+ {catalog && (
755
+ <div className="vgai-account-card-footer">
756
+ <label className="vgai-field">
757
+ <span className="vgai-field-label">Subscription tier</span>
758
+ <Select
759
+ value={selectedPlan}
760
+ disabled={busy || !managedAccess}
761
+ onChange={(event) => setSelectedPlan(event.target.value as AccountPlanId)}
762
+ >
763
+ {(Object.keys(catalog.plans) as AccountPlanId[]).map((id) => {
764
+ const plan = catalog.plans[id];
765
+ const price = plan.monthlyPriceUsd;
766
+ return (
767
+ <option key={id} value={id} disabled={id === 'free'}>
768
+ {plan.name}
769
+ {price !== undefined ? ` · $${price}/month` : ''} ·{' '}
770
+ {plan.includedCredits.toLocaleString()} credits
771
+ {id === account.plan.id ? ' · current' : ''}
772
+ </option>
773
+ );
774
+ })}
775
+ </Select>
776
+ </label>
777
+ <Button
778
+ variant="primary"
779
+ size="compact"
780
+ disabled={
781
+ busy ||
782
+ !managedAccess ||
783
+ selectedPlan === account.plan.id ||
784
+ selectedPlan === 'free'
785
+ }
786
+ onClick={() =>
787
+ void run(async () => {
788
+ const checkout = await checkoutAccountPlan(selectedPlan);
789
+ if (checkout.checkoutUrl)
790
+ window.open(checkout.checkoutUrl, '_blank', 'noopener,noreferrer');
791
+ else
792
+ setNotice(
793
+ checkout.mock
794
+ ? `Mock ${catalog.plans[selectedPlan].name} upgrade completed. No payment occurred.`
795
+ : `Subscription changed to ${catalog.plans[selectedPlan].name}.`,
796
+ );
797
+ })
798
+ }
799
+ >
800
+ Choose {catalog.plans[selectedPlan].name}
801
+ </Button>
802
+ </div>
803
+ )}
804
+ <div className="vgai-account-credit-figure">
805
+ <span className="vgai-account-credit-number">
806
+ {account.credits.remaining.toLocaleString()}
807
+ </span>
808
+ <span className="vgai-account-credit-unit">credits remaining</span>
809
+ </div>
810
+ <div
811
+ className="vgai-meter"
812
+ role="progressbar"
813
+ aria-label="Credits used"
814
+ aria-valuenow={Math.round(usedPercent)}
815
+ aria-valuemin={0}
816
+ aria-valuemax={100}
817
+ data-state={overQuota ? 'over' : undefined}
818
+ >
819
+ <span className="vgai-meter-fill" style={{ width: `${usedPercent}%` }} />
820
+ {reservedPercent > 0 && (
821
+ <span
822
+ className="vgai-meter-fill"
823
+ data-part="reserved"
824
+ style={{ left: `${usedPercent}%`, width: `${reservedPercent}%` }}
825
+ />
826
+ )}
827
+ </div>
828
+ <p className="vgai-account-cycle-line">
829
+ {account.credits.used.toLocaleString()} of {totalCredits.toLocaleString()} used this
830
+ cycle
831
+ {account.credits.resetsAt && (
832
+ <> · resets {new Date(account.credits.resetsAt).toLocaleDateString()}</>
833
+ )}
834
+ {overQuota && <span className="vgai-account-cycle-over"> · over allowance</span>}
835
+ </p>
836
+ <div className="vgai-account-legend" data-state={overQuota ? 'over' : undefined}>
837
+ <span className="vgai-account-legend-item" data-part="included">
838
+ <span className="vgai-account-legend-dot" aria-hidden="true" />
839
+ {account.credits.included.toLocaleString()} included
840
+ </span>
841
+ <span className="vgai-account-legend-item" data-part="used">
842
+ <span className="vgai-account-legend-dot" aria-hidden="true" />
843
+ {account.credits.used.toLocaleString()} used
844
+ </span>
845
+ <span className="vgai-account-legend-item" data-part="reserved">
846
+ <span className="vgai-account-legend-dot" aria-hidden="true" />
847
+ {account.credits.reserved.toLocaleString()} reserved
848
+ </span>
849
+ <span className="vgai-account-legend-item" data-part="purchased">
850
+ <span className="vgai-account-legend-dot" aria-hidden="true" />
851
+ {account.credits.purchased.toLocaleString()} purchased
852
+ </span>
853
+ </div>
854
+ {account.credits.purchasedExpiresAt && (
855
+ <p className="vgai-account-help vgai-account-expiry">
856
+ Purchased credits expire{' '}
857
+ {new Date(account.credits.purchasedExpiresAt).toLocaleDateString()}.
858
+ </p>
859
+ )}
860
+ <div className="vgai-account-card-footer">
861
+ {catalog && selectedPack && catalog.creditPacks[selectedPack] && (
862
+ <>
863
+ <Select
864
+ aria-label="Credit pack"
865
+ value={selectedPack}
866
+ disabled={busy || !managedAccess}
867
+ onChange={(event) => setSelectedPack(event.target.value)}
868
+ >
869
+ {Object.entries(catalog.creditPacks).map(([id, pack]) => (
870
+ <option key={id} value={id}>
871
+ {pack.name} · {pack.credits.toLocaleString()} credits
872
+ </option>
873
+ ))}
874
+ </Select>
875
+ <Button
876
+ size="compact"
877
+ disabled={busy || !managedAccess}
878
+ onClick={() =>
879
+ void run(async () => {
880
+ const pack = catalog.creditPacks[selectedPack];
881
+ const checkout = await purchaseAccountCredits(selectedPack);
882
+ if (checkout.checkoutUrl)
883
+ window.open(checkout.checkoutUrl, '_blank', 'noopener,noreferrer');
884
+ else
885
+ setNotice(
886
+ `Added ${pack?.credits.toLocaleString() ?? ''} mock credits. No payment occurred.`,
887
+ );
888
+ })
889
+ }
890
+ >
891
+ Buy credits
892
+ </Button>
893
+ </>
894
+ )}
895
+ <Button
896
+ size="compact"
897
+ variant="ghost"
898
+ disabled={busy}
899
+ onClick={() =>
900
+ void run(async () => {
901
+ const portal = await openAccountBillingPortal();
902
+ if (portal.mock) setNotice('Mock billing portal: no payment page was opened.');
903
+ else window.open(portal.url, '_blank', 'noopener,noreferrer');
904
+ })
905
+ }
906
+ >
907
+ Billing and invoices
908
+ </Button>
909
+ {account.plan.id !== 'free' && account.plan.status === 'active' && (
910
+ <Button
911
+ size="compact"
912
+ variant="ghost"
913
+ className="vgai-account-quiet-danger"
914
+ disabled={busy}
915
+ onClick={() => void run(() => updateAccountPlan('cancel'))}
916
+ >
917
+ Cancel at renewal
918
+ </Button>
919
+ )}
920
+ </div>
921
+ </section>
922
+
923
+ <section className="vgai-account-card">
924
+ <h3 className="vgai-account-card-title">Generation routing</h3>
925
+ <p className="vgai-account-card-sub">
926
+ Billing preference for AI usage. Provider libraries apply it automatically; requests
927
+ show the resolved account and cost before submission.
928
+ </p>
929
+ <label className="vgai-field">
930
+ <span className="vgai-field-label">Preferred route</span>
931
+ <Select
932
+ value={account.preferredRoute}
933
+ onChange={(event) =>
934
+ void run(() =>
935
+ updatePreferredGenerationRoute(
936
+ event.target.value as 'auto' | 'mock' | 'managed' | 'byok',
937
+ ),
938
+ )
939
+ }
940
+ >
941
+ {account.accountEnvironment === 'test-mock' && (
942
+ <option value="mock">Mock · free and local</option>
943
+ )}
944
+ <option value="auto">Automatic · saved key or Volter account</option>
945
+ <option value="managed" disabled={!account.routes.managed}>
946
+ Managed · Volter credits{account.routes.managed ? '' : ' (unavailable)'}
947
+ </option>
948
+ <option value="byok" disabled={!account.routes.byok}>
949
+ BYOK · provider dollars{account.routes.byok ? '' : ' (no key found)'}
950
+ </option>
951
+ </Select>
952
+ </label>
953
+ </section>
954
+
955
+ <CodingInferenceCard busy={busy} run={run} />
956
+ <ProviderConnections busy={busy} run={run} />
957
+
958
+ {(account.alerts?.length ?? 0) > 0 && (
959
+ <section className="vgai-account-card">
960
+ <div className="vgai-account-card-head">
961
+ <h3 className="vgai-account-card-title">Account alerts</h3>
962
+ {account.alerts?.some((alert) => !alert.readAt) && (
963
+ <Button
964
+ size="compact"
965
+ variant="ghost"
966
+ disabled={busy}
967
+ onClick={() => void run(markAccountAlertsRead)}
968
+ >
969
+ Mark read
970
+ </Button>
971
+ )}
972
+ </div>
973
+ <div className="vgai-account-usage">
974
+ {account.alerts?.map((alert) => (
975
+ <div className="vgai-account-usage-row" key={alert.id}>
976
+ <span>
977
+ <strong>{alert.readAt ? 'Notice' : 'New'}</strong>
978
+ <small>{alert.message}</small>
979
+ </span>
980
+ <span>{new Date(alert.createdAt).toLocaleDateString()}</span>
981
+ </div>
982
+ ))}
983
+ </div>
984
+ </section>
985
+ )}
986
+
987
+ <section className="vgai-account-card">
988
+ <h3 className="vgai-account-card-title">Spend controls</h3>
989
+ <p className="vgai-account-card-sub">
990
+ Alerts at {account.spendPolicy.alertThresholds.join('%, ')}% of the available allowance.
991
+ </p>
992
+ <label className="vgai-account-switch-row">
993
+ <Checkbox
994
+ checked={account.spendPolicy.overageEnabled}
995
+ onChange={(event) =>
996
+ void run(() =>
997
+ updatePolicy({
998
+ overageEnabled: event.target.checked,
999
+ monthlyLimitCredits:
1000
+ event.target.checked && account.spendPolicy.monthlyLimitCredits === 0
1001
+ ? 1_000
1002
+ : account.spendPolicy.monthlyLimitCredits,
1003
+ }),
1004
+ )
1005
+ }
1006
+ />
1007
+ <span className="vgai-account-switch-copy">
1008
+ <strong>Allow paid overage</strong>
1009
+ <small>Off by default. Managed requests stop before provider submission.</small>
1010
+ </span>
1011
+ </label>
1012
+ <div className="vgai-account-limit-field">
1013
+ <label className="vgai-field">
1014
+ <span className="vgai-field-label">Monthly overage hard limit</span>
1015
+ <span className="vgai-account-input-suffix">
1016
+ <TextInput
1017
+ key={account.spendPolicy.monthlyLimitCredits}
1018
+ type="number"
1019
+ min={0}
1020
+ defaultValue={account.spendPolicy.monthlyLimitCredits}
1021
+ disabled={!account.spendPolicy.overageEnabled}
1022
+ onBlur={(event) =>
1023
+ void run(() =>
1024
+ updatePolicy({ monthlyLimitCredits: Number(event.target.value) }),
1025
+ )
1026
+ }
1027
+ />
1028
+ <span className="vgai-account-suffix" aria-hidden="true">
1029
+ credits
1030
+ </span>
1031
+ </span>
1032
+ </label>
1033
+ </div>
1034
+ <label className="vgai-account-switch-row">
1035
+ <Checkbox
1036
+ checked={account.spendPolicy.autoReload?.enabled === true}
1037
+ onChange={(event) =>
1038
+ void run(() =>
1039
+ updatePolicy({
1040
+ autoReload: {
1041
+ enabled: event.target.checked,
1042
+ whenRemainingBelow: account.spendPolicy.autoReload?.whenRemainingBelow ?? 100,
1043
+ reloadTo: account.spendPolicy.autoReload?.reloadTo ?? 500,
1044
+ monthlyLimitCredits:
1045
+ account.spendPolicy.autoReload?.monthlyLimitCredits ?? 2_000,
1046
+ },
1047
+ }),
1048
+ )
1049
+ }
1050
+ />
1051
+ <span className="vgai-account-switch-copy">
1052
+ <strong>Automatically reload credits</strong>
1053
+ <small>Optional, independently capped, and never enabled by checkout.</small>
1054
+ </span>
1055
+ </label>
1056
+ {account.spendPolicy.autoReload?.enabled && (
1057
+ <div className="vgai-account-policy-fields">
1058
+ <label className="vgai-field">
1059
+ <span className="vgai-field-label">Reload below</span>
1060
+ <span className="vgai-account-input-suffix">
1061
+ <TextInput
1062
+ key={account.spendPolicy.autoReload.whenRemainingBelow}
1063
+ type="number"
1064
+ min={0}
1065
+ defaultValue={account.spendPolicy.autoReload.whenRemainingBelow}
1066
+ onBlur={(event) =>
1067
+ void run(() =>
1068
+ updatePolicy({
1069
+ autoReload: {
1070
+ ...account.spendPolicy.autoReload!,
1071
+ whenRemainingBelow: Number(event.target.value),
1072
+ },
1073
+ }),
1074
+ )
1075
+ }
1076
+ />
1077
+ <span className="vgai-account-suffix" aria-hidden="true">
1078
+ credits
1079
+ </span>
1080
+ </span>
1081
+ </label>
1082
+ <label className="vgai-field">
1083
+ <span className="vgai-field-label">Reload balance to</span>
1084
+ <span className="vgai-account-input-suffix">
1085
+ <TextInput
1086
+ key={account.spendPolicy.autoReload.reloadTo}
1087
+ type="number"
1088
+ min={1}
1089
+ defaultValue={account.spendPolicy.autoReload.reloadTo}
1090
+ onBlur={(event) =>
1091
+ void run(() =>
1092
+ updatePolicy({
1093
+ autoReload: {
1094
+ ...account.spendPolicy.autoReload!,
1095
+ reloadTo: Number(event.target.value),
1096
+ },
1097
+ }),
1098
+ )
1099
+ }
1100
+ />
1101
+ <span className="vgai-account-suffix" aria-hidden="true">
1102
+ credits
1103
+ </span>
1104
+ </span>
1105
+ </label>
1106
+ <label className="vgai-field">
1107
+ <span className="vgai-field-label">Monthly reload cap</span>
1108
+ <span className="vgai-account-input-suffix">
1109
+ <TextInput
1110
+ key={account.spendPolicy.autoReload.monthlyLimitCredits}
1111
+ type="number"
1112
+ min={0}
1113
+ defaultValue={account.spendPolicy.autoReload.monthlyLimitCredits}
1114
+ onBlur={(event) =>
1115
+ void run(() =>
1116
+ updatePolicy({
1117
+ autoReload: {
1118
+ ...account.spendPolicy.autoReload!,
1119
+ monthlyLimitCredits: Number(event.target.value),
1120
+ },
1121
+ }),
1122
+ )
1123
+ }
1124
+ />
1125
+ <span className="vgai-account-suffix" aria-hidden="true">
1126
+ credits
1127
+ </span>
1128
+ </span>
1129
+ </label>
1130
+ </div>
1131
+ )}
1132
+ </section>
1133
+
1134
+ <section className="vgai-account-card">
1135
+ <h3 className="vgai-account-card-title">Current-cycle usage</h3>
1136
+ {usage.length === 0 ? (
1137
+ <p className="vgai-account-help">
1138
+ No managed credit usage has been settled in this account.
1139
+ </p>
1140
+ ) : (
1141
+ <div className="vgai-account-usage">
1142
+ {usage.map((entry) => (
1143
+ <div className="vgai-account-usage-row" key={entry.id}>
1144
+ <span>
1145
+ {entry.kind} · {entry.provider} · {entry.operation ?? 'managed operation'}
1146
+ </span>
1147
+ <strong>{entry.credits.toLocaleString()} credits</strong>
1148
+ </div>
1149
+ ))}
1150
+ </div>
1151
+ )}
1152
+ </section>
1153
+
1154
+ {(notice || accountError()) && (
1155
+ <div className="vgai-account-status" role="status">
1156
+ {notice ?? accountError()}
1157
+ </div>
1158
+ )}
1159
+ </div>
1160
+ </div>
1161
+ );
1162
+ }