@vgai/editor 0.2.0 → 0.4.0-canary.20260715.0

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 (430) hide show
  1. package/README.md +24 -0
  2. package/dist/assets/axe-C7YBtOuM.js +30 -0
  3. package/dist/assets/browserAll-DDtGGE48.js +1 -0
  4. package/dist/assets/create-runtime-DbTlBlKG.js +1 -0
  5. package/dist/assets/{game-BI111o5f.js → game-BE0r7DMI.js} +3 -3
  6. package/dist/assets/game-BdTHR2_X.js +1 -0
  7. package/dist/assets/{game-pt_1lQLv.js → game-C3iQGSqo.js} +1 -1
  8. package/dist/assets/game-CcHfYxjP.js +1 -0
  9. package/dist/assets/game-Cqzj_lpD.js +1 -0
  10. package/dist/assets/game-DSZdeCHu.js +1 -0
  11. package/dist/assets/game-Dch9sAPc.js +1 -0
  12. package/dist/assets/{game-xsOho_wg.js → game-SAFn90pA.js} +3 -3
  13. package/dist/assets/game-loop-DMIgSMV-.js +1 -0
  14. package/dist/assets/{game-source-CvidN7AB.js → game-source-Dk83w26t.js} +1 -1
  15. package/dist/assets/{index-Bp5cecD6.js → index-4idIsHHJ.js} +2 -2
  16. package/dist/assets/{index-DL7RcQ0n.js → index-C-LhOTBN.js} +1 -1
  17. package/dist/assets/{index-Cs8xwv3e.js → index-CDkxceqE.js} +1 -1
  18. package/dist/assets/index-CQEfi217.js +15412 -0
  19. package/dist/assets/index-Dvi-V0NE.css +1 -0
  20. package/dist/assets/ingest-siblings-s0P5I1Ub.js +1 -0
  21. package/dist/assets/mount-manifest-DXMOlF79.js +1 -0
  22. package/dist/assets/react-18-C9eHn8x1.js +1 -0
  23. package/dist/assets/webworkerAll-Fn0e-ouF.js +1 -0
  24. package/dist/assets/world-documents-GM0a9Kv7.js +1 -0
  25. package/dist/index.html +15 -13
  26. package/dist/ingest/racing-game/vgai.game.json +5 -4
  27. package/dist/ingest/simcity/vgai.game.json +5 -4
  28. package/dist/ingest/tanks/vgai.game.json +5 -4
  29. package/dist/project-scripts.bundle.js +3917 -0
  30. package/dist/scenes/untitled.vscn.json +2 -0
  31. package/dist-server/packaged.mjs +7697 -3435
  32. package/package.json +31 -4
  33. package/scripts/probe-reporter.ts +349 -0
  34. package/scripts/run-probe.ts +2260 -0
  35. package/server/asset-catalog-audit.ts +268 -0
  36. package/server/asset-catalog-v2.ts +146 -0
  37. package/server/asset-library-routes.ts +276 -58
  38. package/server/canonical-path.ts +1 -1
  39. package/server/dev.ts +148 -44
  40. package/server/editor-server.ts +888 -61
  41. package/server/editor-sse.ts +115 -4
  42. package/server/harness-chat-service.ts +796 -0
  43. package/server/local-asset-catalog.ts +157 -22
  44. package/server/packaged.ts +51 -21
  45. package/server/process-shutdown.ts +136 -0
  46. package/server/prod.ts +9 -6
  47. package/server/project-hmr-files.ts +41 -0
  48. package/server/project-operations.ts +343 -0
  49. package/server/project-optimize-deps-entries.ts +4 -4
  50. package/server/project-output-writer.ts +156 -0
  51. package/server/project-validation.ts +140 -0
  52. package/server/project-view.ts +8 -50
  53. package/server/server-utils.ts +75 -10
  54. package/server/session-registry.ts +12 -0
  55. package/src/EditorContext.tsx +107 -3
  56. package/src/action-registry.ts +115 -17
  57. package/src/adapter-resolver.ts +213 -478
  58. package/src/asset-events.ts +64 -1
  59. package/src/asset-preview.ts +906 -0
  60. package/src/asset-selection.ts +121 -0
  61. package/src/asset-workflow/asset-capabilities.ts +259 -0
  62. package/src/asset-workflow/asset-editor-errors.ts +46 -0
  63. package/src/asset-workflow/asset-import-pipeline.ts +176 -0
  64. package/src/asset-workflow/asset-library-query.ts +107 -0
  65. package/src/asset-workflow/asset-types.ts +192 -0
  66. package/src/asset-workflow/asset-workflow-quality.ts +65 -0
  67. package/src/asset-workflow/import-contract.ts +113 -0
  68. package/src/asset-workflow/model-conversion.ts +126 -0
  69. package/src/asset-workflow/model-inspection.ts +185 -0
  70. package/src/asset-workflow/project-asset-commands.ts +23 -0
  71. package/src/asset-workflow/project-asset-health.ts +153 -0
  72. package/src/asset-workflow/project-asset-operations.ts +119 -0
  73. package/src/asset-workflow/thumbnail-system.ts +172 -0
  74. package/src/authoring/active-systems.ts +15 -0
  75. package/src/authoring/boundary-authoring-adapter.ts +16 -5
  76. package/src/authoring/composite-authoring-adapter.ts +305 -124
  77. package/src/authoring/design-time-layers.ts +300 -117
  78. package/src/authoring/edit-mode-authoring.ts +278 -293
  79. package/src/authoring/embed-only-adapter.ts +4 -2
  80. package/src/authoring/first-party-hierarchy-menu.ts +7 -2
  81. package/src/authoring/first-party-inspector/AddSectionMenu.tsx +5 -12
  82. package/src/authoring/first-party-inspector/EnvironmentSection.tsx +53 -126
  83. package/src/authoring/first-party-inspector/HeaderSection.tsx +54 -65
  84. package/src/authoring/first-party-inspector/is-first-party-selection.ts +30 -11
  85. package/src/authoring/first-party-inspector/register.tsx +13 -2
  86. package/src/authoring/ingest-authoring-adapter.ts +142 -45
  87. package/src/authoring/mount-failure-report.ts +5 -5
  88. package/src/authoring/provenance.ts +41 -0
  89. package/src/authoring/react-dom-authoring-adapter.ts +92 -92
  90. package/src/authoring/react-inspector-section.tsx +489 -51
  91. package/src/authoring/react-world-authoring-adapter.ts +541 -306
  92. package/src/authoring/story-overlay.ts +1 -1
  93. package/src/authoring/story-types.ts +11 -0
  94. package/src/authoring/vgai-scene-authoring-adapter.ts +54 -25
  95. package/src/authoring/viewport-tool-context.ts +78 -0
  96. package/src/authoring/world-canvas-viewport-state.ts +35 -0
  97. package/src/authoring/world-hidden-viewport.ts +186 -0
  98. package/src/authoring/world-pan-state.ts +33 -17
  99. package/src/authoring/world-session-state.ts +1 -1
  100. package/src/authoring/world2d-authoring-adapter.ts +109 -52
  101. package/src/availability-tick.ts +56 -0
  102. package/src/browser-play-entry.ts +5 -5
  103. package/src/browser-project-scripts.ts +1 -0
  104. package/src/build-identity.ts +15 -0
  105. package/src/command-listener.ts +893 -19
  106. package/src/components/AlignToolbar.tsx +74 -63
  107. package/src/components/AppRoot.tsx +193 -26
  108. package/src/components/ApplicationMenus.tsx +272 -0
  109. package/src/components/AssetBrowser.tsx +1450 -293
  110. package/src/components/AssetEditorShell.tsx +141 -0
  111. package/src/components/AssetImportDetails.tsx +218 -0
  112. package/src/components/AssetLibraryPanel.tsx +14 -0
  113. package/src/components/CameraInfo.tsx +11 -20
  114. package/src/components/CenterDocuments.tsx +585 -0
  115. package/src/components/CommandPalette.tsx +75 -30
  116. package/src/components/ConsolePanel.css +55 -0
  117. package/src/components/ConsolePanel.tsx +57 -112
  118. package/src/components/DebugConsolePanel.tsx +461 -0
  119. package/src/components/DebuggerPanel.tsx +165 -0
  120. package/src/components/DefaultEditorLayout.tsx +167 -231
  121. package/src/components/GameHierarchy.tsx +873 -290
  122. package/src/components/GamePerformanceOverlay.tsx +46 -0
  123. package/src/components/HarnessChatPanel.tsx +2352 -0
  124. package/src/components/HistoryPanel.tsx +226 -0
  125. package/src/components/Inspector.tsx +153 -99
  126. package/src/components/InspectorToolSection.tsx +6 -4
  127. package/src/components/MountFailureBanner.tsx +31 -48
  128. package/src/components/NetworkSection.tsx +25 -14
  129. package/src/components/OnlineAssetBrowser.tsx +794 -343
  130. package/src/components/OverlayReportBanner.tsx +38 -64
  131. package/src/components/PerformancePanel.tsx +206 -0
  132. package/src/components/PlayBar.tsx +93 -263
  133. package/src/components/PrefabSaveDialog.tsx +55 -127
  134. package/src/components/ProjectHeader.css +60 -0
  135. package/src/components/ProjectHeader.tsx +170 -105
  136. package/src/components/ProjectScreen.tsx +246 -184
  137. package/src/components/ReactCanvasControls.tsx +185 -0
  138. package/src/components/ResolutionPicker.tsx +85 -0
  139. package/src/components/RootActivateDialog.tsx +39 -68
  140. package/src/components/SaveDialog.tsx +55 -127
  141. package/src/components/SaveStatus.tsx +11 -29
  142. package/src/components/StartupLoadingScreen.tsx +96 -0
  143. package/src/components/StateWatchPanel.tsx +302 -0
  144. package/src/components/StatsOverlay.tsx +12 -5
  145. package/src/components/StatusBar.tsx +64 -0
  146. package/src/components/ThemeManagerDialog.css +89 -0
  147. package/src/components/ThemeManagerDialog.tsx +371 -0
  148. package/src/components/ToolHost.tsx +26 -15
  149. package/src/components/Toolbar.tsx +117 -214
  150. package/src/components/VgaiVoxelLogo.tsx +89 -0
  151. package/src/components/ViewportOverlay.tsx +97 -172
  152. package/src/components/ViewportPanel.tsx +130 -31
  153. package/src/components/WorkspaceDock.tsx +891 -0
  154. package/src/components/WorldSelectionOverlay.tsx +51 -63
  155. package/src/components/WorldTextEditor.tsx +9 -5
  156. package/src/components/asset-documents.tsx +390 -0
  157. package/src/components/asset-editor-persistence.ts +44 -0
  158. package/src/components/asset-selection-section.tsx +338 -0
  159. package/src/components/asset-viewers/AudioViewer.tsx +73 -21
  160. package/src/components/asset-viewers/GenericJsonViewer.tsx +26 -11
  161. package/src/components/asset-viewers/ImageViewer.tsx +43 -11
  162. package/src/components/asset-viewers/InputMapViewer.tsx +512 -134
  163. package/src/components/asset-viewers/MaterialAssetEditor.tsx +540 -0
  164. package/src/components/asset-viewers/ModelViewer.tsx +805 -121
  165. package/src/components/asset-viewers/OnlineAssetDetail.tsx +177 -60
  166. package/src/components/asset-viewers/SourceAssetViewer.tsx +121 -0
  167. package/src/components/asset-workflow.css +506 -0
  168. package/src/components/build-documents.tsx +696 -0
  169. package/src/components/core-utilities.tsx +75 -0
  170. package/src/components/data-documents.tsx +364 -0
  171. package/src/components/{DataPanel.tsx → data-editor.tsx} +138 -288
  172. package/src/components/debugger-panel-state.ts +57 -0
  173. package/src/components/inspector-widgets/AlignmentGrid.tsx +8 -14
  174. package/src/components/inspector-widgets/BorderEditor.tsx +41 -33
  175. package/src/components/inspector-widgets/ColorPicker.tsx +39 -39
  176. package/src/components/inspector-widgets/FontPicker.tsx +13 -21
  177. package/src/components/inspector-widgets/GradientEditor.tsx +11 -19
  178. package/src/components/inspector-widgets/ScrubbableInput.tsx +6 -3
  179. package/src/components/inspector-widgets/ShadowEditor.tsx +20 -33
  180. package/src/components/inspector-widgets/shared.tsx +90 -49
  181. package/src/components/inspectors/AnimationSection.tsx +39 -40
  182. package/src/components/inspectors/AudioSection.tsx +28 -39
  183. package/src/components/inspectors/BoneAttachmentSection.tsx +19 -24
  184. package/src/components/inspectors/CameraSection.tsx +14 -23
  185. package/src/components/inspectors/ComponentsSection.tsx +39 -43
  186. package/src/components/inspectors/JointsSection.tsx +34 -43
  187. package/src/components/inspectors/LightSection.tsx +90 -35
  188. package/src/components/inspectors/MaterialSection.tsx +137 -174
  189. package/src/components/inspectors/MeshSection.tsx +9 -17
  190. package/src/components/inspectors/NavigationSection.tsx +8 -15
  191. package/src/components/inspectors/ParticleSection.tsx +91 -76
  192. package/src/components/inspectors/PhysicsSection.tsx +42 -46
  193. package/src/components/inspectors/PostProcessingSection.tsx +28 -36
  194. package/src/components/inspectors/RenderingSection.tsx +12 -20
  195. package/src/components/inspectors/ShadowSection.tsx +7 -8
  196. package/src/components/inspectors/SplineSection.tsx +31 -42
  197. package/src/components/inspectors/TransformSection.tsx +14 -22
  198. package/src/components/patterns/Dialog.tsx +95 -0
  199. package/src/components/patterns/Fields.tsx +44 -0
  200. package/src/components/patterns/List.tsx +25 -0
  201. package/src/components/patterns/StateSurface.tsx +40 -0
  202. package/src/components/patterns/Surfaces.tsx +115 -0
  203. package/src/components/patterns/Tabs.tsx +80 -0
  204. package/src/components/patterns/Toolbar.tsx +51 -0
  205. package/src/components/patterns/Tree.tsx +72 -0
  206. package/src/components/primitives/Button.tsx +46 -0
  207. package/src/components/primitives/ColorInput.tsx +11 -47
  208. package/src/components/primitives/EditorIcon.tsx +99 -0
  209. package/src/components/primitives/FormControls.tsx +71 -0
  210. package/src/components/primitives/JsonInput.tsx +15 -2
  211. package/src/components/primitives/Layout.tsx +100 -0
  212. package/src/components/primitives/Menu.tsx +105 -0
  213. package/src/components/primitives/NumberInput.tsx +8 -35
  214. package/src/components/primitives/Panel.tsx +49 -44
  215. package/src/components/primitives/SectionHeader.tsx +10 -28
  216. package/src/components/primitives/Text.tsx +46 -0
  217. package/src/components/primitives/Tooltip.tsx +20 -12
  218. package/src/components/primitives/Vec3Input.tsx +3 -0
  219. package/src/components/primitives/banner-tones.ts +33 -0
  220. package/src/components/primitives/editor-icons.ts +102 -0
  221. package/src/components/primitives/panel-header-styles.ts +30 -0
  222. package/src/components/project-operation-documents.tsx +226 -0
  223. package/src/components/status-contributions.tsx +175 -0
  224. package/src/components/story-documents-register.ts +15 -0
  225. package/src/components/story-documents.tsx +1185 -0
  226. package/src/components/tool-documents.tsx +110 -0
  227. package/src/components/workspace-dock.css +293 -0
  228. package/src/components/workspace-history.ts +33 -0
  229. package/src/components/world-documents.tsx +145 -0
  230. package/src/components/xstate-documents.tsx +209 -0
  231. package/src/composite-screenshot.ts +248 -0
  232. package/src/design-system/index.ts +70 -0
  233. package/src/design-system-stories/Foundation.stories.tsx +183 -0
  234. package/src/design-system-stories/Patterns.stories.tsx +251 -0
  235. package/src/design-system-stories/Workspace.stories.tsx +538 -0
  236. package/src/doctor/report.ts +57 -13
  237. package/src/editor-api.ts +295 -31
  238. package/src/editor-compatibility.ts +200 -0
  239. package/src/editor-console.ts +35 -17
  240. package/src/editor-hotkeys.ts +85 -33
  241. package/src/editor-store.ts +538 -233
  242. package/src/editor-viewport.ts +160 -34
  243. package/src/entity-factory.ts +14 -3
  244. package/src/gizmo-registry.ts +21 -5
  245. package/src/harness-chat-editor-context.ts +37 -0
  246. package/src/harness-chat-indicator.ts +68 -0
  247. package/src/harness-chat-layout.ts +53 -0
  248. package/src/harness-chat-lifecycle.ts +170 -0
  249. package/src/harness-chat-reconnect.ts +85 -0
  250. package/src/harness-chat-types.ts +409 -0
  251. package/src/hierarchy-drop.ts +50 -0
  252. package/src/hierarchy-expansion-state.ts +100 -0
  253. package/src/hierarchy-menu-registry.ts +1 -1
  254. package/src/hierarchy-projection.ts +76 -0
  255. package/src/history/editor-session.ts +21 -0
  256. package/src/history/history-commands.ts +147 -0
  257. package/src/history/history-service.ts +904 -0
  258. package/src/history/json-history-resource.ts +165 -0
  259. package/src/history/persistence-coordinator.ts +35 -0
  260. package/src/history/project-file-history.ts +266 -0
  261. package/src/history/project-root-history-backends.ts +53 -0
  262. package/src/history/resource-registry.ts +209 -0
  263. package/src/history/snapshot-store.ts +108 -0
  264. package/src/history/source-history-backend.ts +374 -0
  265. package/src/history/types.ts +100 -0
  266. package/src/hmr-registration-group.ts +67 -0
  267. package/src/hmr-stable-react-context.ts +23 -0
  268. package/src/hosted-example.ts +12 -2
  269. package/src/hotkeys.ts +89 -18
  270. package/src/ingest/discovery-react.ts +9 -10
  271. package/src/ingest/discovery.ts +4 -4
  272. package/src/ingest/discovery2d.ts +5 -5
  273. package/src/ingest/games/bloom-composer/vgai.game.json +3 -3
  274. package/src/ingest/games/embed-bundle/vgai.game.json +3 -3
  275. package/src/ingest/games/games-fps/vgai.game.json +3 -3
  276. package/src/ingest/games/iframe-reachable/vgai.game.json +3 -3
  277. package/src/ingest/games/interactive-cubes/vgai.game.json +3 -3
  278. package/src/ingest/games/mismatched-three/vgai.game.json +3 -3
  279. package/src/ingest/games/own-three-bundled/vgai.game.json +3 -3
  280. package/src/ingest/games/pointerlock-maze/vgai.game.json +7 -4
  281. package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +3 -3
  282. package/src/ingest/games-2d/bunnymark/vgai.game.json +3 -3
  283. package/src/ingest/games-2d/flappy/vgai.game.json +3 -3
  284. package/src/ingest/games-2d/flappy-pixi/vgai.game.json +3 -3
  285. package/src/ingest/games-2d/puzzling-potions/vgai.game.json +3 -3
  286. package/src/ingest/games-2d/tilemap-camera/vgai.game.json +3 -3
  287. package/src/ingest/games-react/react-rpg/vgai.game.json +3 -3
  288. package/src/ingest/types.ts +1 -1
  289. package/src/ingest-mode.ts +47 -52
  290. package/src/ingest-siblings.ts +23 -33
  291. package/src/initial-scene.ts +1 -2
  292. package/src/main.tsx +16 -8
  293. package/src/manifest-project.ts +10 -12
  294. package/src/model-thumbnail.ts +261 -26
  295. package/src/module-mode.ts +44 -50
  296. package/src/play-log-events.ts +26 -0
  297. package/src/play-mode.ts +495 -401
  298. package/src/play-scene-adoption.ts +23 -0
  299. package/src/playground/main.ts +68 -24
  300. package/src/playwright-shim.ts +473 -0
  301. package/src/project-manager.ts +90 -10
  302. package/src/project-operations.ts +112 -0
  303. package/src/project-story-discovery.ts +25 -0
  304. package/src/project-tool-discovery.ts +43 -0
  305. package/src/scene-serializer.ts +0 -6
  306. package/src/scene-sync.ts +7 -2
  307. package/src/script-loader.ts +3 -3
  308. package/src/skeleton-helper.ts +57 -0
  309. package/src/storage/seed.ts +68 -8
  310. package/src/stories/StoryPreviewMount.tsx +265 -0
  311. package/src/stories/action-stories.fixture.tsx +44 -0
  312. package/src/stories/button-stories.fixture.tsx +50 -0
  313. package/src/stories/compose-project-stories.ts +182 -0
  314. package/src/stories/loader-stories.fixture.tsx +53 -0
  315. package/src/stories/story-discovery.ts +130 -0
  316. package/src/stories/story-registry.ts +146 -0
  317. package/src/theme-css.ts +1049 -0
  318. package/src/theme-library.ts +238 -0
  319. package/src/theme-preference.ts +130 -0
  320. package/src/theme.ts +527 -0
  321. package/src/tool-loader.ts +80 -24
  322. package/src/ui-source/inspect.ts +27 -4
  323. package/src/ui-source/source-write-backend.ts +83 -22
  324. package/src/widgets/index.ts +9 -18
  325. package/src/workspace-aux-commands.ts +11 -0
  326. package/src/workspace-dock-controller.ts +64 -0
  327. package/src/workspace-document-ids.ts +5 -0
  328. package/src/workspace-document-registry.ts +367 -0
  329. package/src/workspace-state-persistence.ts +455 -0
  330. package/src/workspace-status-registry.ts +110 -0
  331. package/src/workspace-utility-commands.ts +49 -0
  332. package/src/workspace-utility-registry.ts +161 -0
  333. package/src/world-document-routing.ts +16 -0
  334. package/src/xstate-inspect/XStateMachineInspector.tsx +544 -0
  335. package/src/xstate-inspect/character-animation-machine.fixture.ts +74 -0
  336. package/src/xstate-inspect/use-live-actor-state.ts +39 -0
  337. package/src/xstate-inspect/xstate-graph.ts +265 -0
  338. package/src/xstate-inspect/xstate-layout.ts +76 -0
  339. package/src/z-index.ts +26 -0
  340. package/template/.claude/skills/editor/SKILL.md +129 -90
  341. package/template/AGENTS.md +1110 -0
  342. package/template/CLAUDE.md +1 -512
  343. package/template/IDIOMS.md +50 -0
  344. package/template/check-idioms.ts +706 -0
  345. package/template/manifest-entry-modules-plugin.ts +53 -0
  346. package/template/package.json +13 -4
  347. package/template/public/scenes/default.scn2d.json +6 -0
  348. package/template/public/scenes/default.vscn.json +7 -1
  349. package/template/public/scenes/untitled.vscn.json +10 -0
  350. package/template/scripts/playtest.ts +40 -0
  351. package/template/server/loopback.ts +75 -0
  352. package/template/server/rooms/arena-room.ts +69 -1
  353. package/template/server/rooms/game-room.ts +68 -1
  354. package/template/src/data/README.md +9 -4
  355. package/template/src/data/data-ref.ts +35 -0
  356. package/template/src/data/tuning.schema.ts +1 -1
  357. package/template/src/main.ts +41 -62
  358. package/template/src/manifest-entry-modules.d.ts +3 -0
  359. package/template/src/operations/README.md +88 -0
  360. package/template/src/runtime/pixi-adapter.ts +24 -0
  361. package/template/src/scripts/components/data-spinner.ts +43 -2
  362. package/template/src/scripts/components/spin-lap-math.ts +27 -0
  363. package/template/src/tools/README.md +5 -3
  364. package/template/src/tools/example.tool.tsx +13 -7
  365. package/template/src/ui/Button.stories.tsx +66 -0
  366. package/template/src/ui/RebindPanel.tsx +2 -6
  367. package/template/src/ui/game.tsx +17 -0
  368. package/template/tests/acceptance/example.spec.ts +64 -0
  369. package/template/tests/logic/example.test.ts +31 -0
  370. package/template/tests/logic/room.test.ts +100 -0
  371. package/template/tests/tsconfig.json +30 -0
  372. package/template/validate-manifest.ts +5 -5
  373. package/template/validate-scenes.ts +50 -18
  374. package/template/vgai.game.json +20 -13
  375. package/template/vite.config.ts +77 -16
  376. package/vite-plugin-ui-oid.ts +261 -17
  377. package/dist/assets/CCDIKSolver-Dz-ysxZF.js +0 -1
  378. package/dist/assets/SelectionBox-BXsv_6aH.js +0 -1
  379. package/dist/assets/browser-project-scripts-DOru4L1x.js +0 -2
  380. package/dist/assets/browserAll-CEkWYlFk.js +0 -1
  381. package/dist/assets/create-runtime-DYIWr3va.js +0 -1
  382. package/dist/assets/data-asset-Bb9sIM7i.js +0 -5
  383. package/dist/assets/game-8SDZPPgH.js +0 -1
  384. package/dist/assets/game-CevorxyV.js +0 -1
  385. package/dist/assets/game-Di7XlywD.js +0 -1
  386. package/dist/assets/game-DjnCaw8R.js +0 -1
  387. package/dist/assets/game-Q9A0aXSV.js +0 -1
  388. package/dist/assets/index-CPVV6pjw.js +0 -1
  389. package/dist/assets/index-CpOnYyeD.js +0 -213
  390. package/dist/assets/index-Dr8H_6Rl.js +0 -1
  391. package/dist/assets/index-_bCaipUk.js +0 -13650
  392. package/dist/assets/ingest-siblings-Dz8eEqrV.js +0 -1
  393. package/dist/assets/mount-manifest-Bhk0ohsl.js +0 -1
  394. package/dist/assets/scene-query-BivAHhJu.js +0 -1
  395. package/dist/assets/webworkerAll-BLrH0F6F.js +0 -1
  396. package/dist/assets/xstate-animation-binding-1NGLmw6y.js +0 -1
  397. package/dist/assets/xstate.esm-BgUD33nl.js +0 -3
  398. package/src/authoring/scene-ui-layer.ts +0 -485
  399. package/src/authoring/ui-authoring-adapter.ts +0 -325
  400. package/src/authoring/ui-hierarchy-menu.ts +0 -40
  401. package/src/authoring/ui-inspector-sections.tsx +0 -156
  402. package/src/authoring/undo-timeline.ts +0 -145
  403. package/src/components/BottomPanel.tsx +0 -326
  404. package/src/components/BuildPanel.tsx +0 -455
  405. package/src/components/FolderTree.tsx +0 -179
  406. package/src/components/RightPanel.tsx +0 -166
  407. package/src/components/WorldContextMenu.tsx +0 -257
  408. package/src/components/asset-viewers/AssetViewer.tsx +0 -38
  409. package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +0 -129
  410. package/src/scene-ui-mode/active-scene-ui.ts +0 -58
  411. package/src/scene-ui-mode/scene-ui-palette-panel.tsx +0 -152
  412. package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +0 -29
  413. package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +0 -74
  414. package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +0 -49
  415. package/src/scene-ui-mode/ui-prefab-io.ts +0 -76
  416. package/src/ui-editor/inspector.tsx +0 -563
  417. package/src/ui-editor/node-ops.ts +0 -266
  418. package/src/ui-editor/overlay.tsx +0 -380
  419. package/src/ui-editor/palette.tsx +0 -114
  420. package/src/ui-editor/react-store.ts +0 -78
  421. package/src/ui-editor/timeline-panel.tsx +0 -443
  422. package/src/ui-editor/ui-edit-store.ts +0 -552
  423. package/src/ui-editor/ui-prefab.ts +0 -95
  424. package/template/project.json +0 -9
  425. package/template/src/scripts/main.ts +0 -94
  426. package/template/src/ui/DefaultHud.tsx +0 -40
  427. package/template/src/ui/game-state.tsx +0 -37
  428. package/template/src/ui/mount-default-hud.tsx +0 -19
  429. package/template/src/ui/mount-ui.ts +0 -15
  430. package/vite-plugin-react-world-provider.ts +0 -81
@@ -0,0 +1,696 @@
1
+ /**
2
+ * Build split (W3, docs/EDITOR-WORKSPACE-SHELL-TODO.md §5/§8/§9 W3 —
3
+ * inventory rows B4 + B15's W3 share): the permanent bottom BUILD tab
4
+ * (`BuildPanel.tsx`, deleted) mixed configuration + output log + progress in
5
+ * one drawer pane. Split per §5/§8:
6
+ *
7
+ * - **Build Profiles** (configuration: target list, project name /
8
+ * resolution / app id, the Build trigger) → a center DOCUMENT (kind
9
+ * `'build-profiles'`). Chosen over the §5 row's focused-dialog
10
+ * alternative because (a) §7.1 names Build Profiles among the real
11
+ * first-party document implementers, (b) the configuration is a
12
+ * persistent authored surface (it writes `vgai.game.json` through the
13
+ * history service) someone tunes and re-runs, not a one-shot confirm —
14
+ * document semantics, and (c) a modal would block the viewport while a
15
+ * build streams. A future focused "quick build" dialog stays additive.
16
+ * - **Build Output** (the log) → a workspace-utility registration
17
+ * (`build-output`) — rendered by Dockview as a movable utility panel that
18
+ * defaults to the bottom edge group, exactly like story utilities. Always available in server mode so
19
+ * it is reachable before the first build (it owns its empty state);
20
+ * starting a build raises `editor:show-build-output` so its current group
21
+ * reveals and focuses it (the old `editor:show-build-tab` reflex, split in two).
22
+ * - **build progress** → a workspace-STATUS registration
23
+ * (`build-progress`, W0 registry). Registering now is correct even
24
+ * though the status bar doesn't exist yet: the registry renders nowhere
25
+ * until W4 consumes it — the contribution is inert, typed, tested.
26
+ *
27
+ * The build SESSION (phase/log/result) is a module-scope store so all three
28
+ * surfaces observe one truth — the old panel lost its log if the tab
29
+ * unmounted mid-build; the session now outlives any one surface.
30
+ *
31
+ * Build ACTIONS remain reachable everywhere they were: the PlayBar hammer,
32
+ * the palette `build.*` actions, and the `show-build` control command all
33
+ * open the Build Profiles document (whose Build button runs the same
34
+ * `startExport` server flow as before).
35
+ */
36
+
37
+ import { faGlobe } from '@fortawesome/free-solid-svg-icons';
38
+ import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';
39
+ import {
40
+ accent,
41
+ Button,
42
+ bg,
43
+ border,
44
+ danger,
45
+ EditorIcon,
46
+ EditorListButton,
47
+ fontMono,
48
+ success,
49
+ TextInput,
50
+ text,
51
+ } from '../design-system';
52
+ import { getDownloadUrl, readProjectTextFile, saveFile, startExport } from '../editor-api';
53
+ import { getProjectFileHistory } from '../history/project-file-history';
54
+ import { createHmrRegistrationGroup } from '../hmr-registration-group';
55
+ import {
56
+ openWorkspaceDocument,
57
+ type WorkspaceDocumentContentProps,
58
+ } from '../workspace-document-registry';
59
+ import { registerWorkspaceStatus } from '../workspace-status-registry';
60
+ import { registerWorkspaceUtility } from '../workspace-utility-registry';
61
+ import { workspaceHistoryService } from './workspace-history';
62
+
63
+ export type BuildTarget = 'web';
64
+ export type BuildPhase = 'idle' | 'building' | 'done';
65
+
66
+ export interface BuildResult {
67
+ ok: boolean;
68
+ code: number | null;
69
+ }
70
+
71
+ interface ProjectConfig {
72
+ name: string;
73
+ resolution: { width: number; height: number };
74
+ appId: string;
75
+ }
76
+
77
+ const MANIFEST_PATH = 'vgai.game.json';
78
+
79
+ const DEFAULT_CONFIG: ProjectConfig = {
80
+ name: 'My Game',
81
+ resolution: { width: 1280, height: 720 },
82
+ appId: 'com.example.mygame',
83
+ };
84
+
85
+ const TARGETS: {
86
+ id: BuildTarget;
87
+ label: string;
88
+ subtitle: string;
89
+ output: string;
90
+ icon: typeof faGlobe;
91
+ }[] = [
92
+ { id: 'web', label: 'Web', subtitle: 'HTML5 for itch.io', output: 'vgai-web.zip', icon: faGlobe },
93
+ ];
94
+
95
+ // --- The build SESSION store (one truth for document/utility/status) -------
96
+
97
+ export interface BuildSession {
98
+ readonly phase: BuildPhase;
99
+ readonly target: BuildTarget;
100
+ readonly log: readonly string[];
101
+ readonly result: BuildResult | null;
102
+ }
103
+
104
+ let _session: BuildSession = { phase: 'idle', target: 'web', log: [], result: null };
105
+ let _version = 0;
106
+ const _listeners = new Set<() => void>();
107
+ let _abort: AbortController | null = null;
108
+
109
+ function setSession(patch: Partial<BuildSession>): void {
110
+ _session = { ..._session, ...patch };
111
+ _version++;
112
+ for (const fn of _listeners) fn();
113
+ }
114
+
115
+ /** Subscribe to build-session changes. Returns unsubscribe. */
116
+ export function subscribeBuildSession(fn: () => void): () => void {
117
+ _listeners.add(fn);
118
+ return () => {
119
+ _listeners.delete(fn);
120
+ };
121
+ }
122
+
123
+ /** Monotonic change counter — the `getSnapshot` for `useSyncExternalStore`. */
124
+ export function buildSessionVersion(): number {
125
+ return _version;
126
+ }
127
+
128
+ /** The current build session (stable reference between changes). */
129
+ export function getBuildSession(): BuildSession {
130
+ return _session;
131
+ }
132
+
133
+ /** Back to idle (the `Build Again` reset). No-op mid-build. */
134
+ export function resetBuildSession(): void {
135
+ if (_session.phase === 'building') return;
136
+ setSession({ phase: 'idle', log: [], result: null });
137
+ }
138
+
139
+ /** Abort a running build (the old panel's Cancel, unchanged semantics). */
140
+ export function cancelBuild(): void {
141
+ _abort?.abort();
142
+ setSession({ phase: 'idle' });
143
+ }
144
+
145
+ /**
146
+ * Run a build — the exact `startExport` SSE-stream consumption the old
147
+ * panel's `startBuild` had, moved to module scope so the session outlives
148
+ * any one surface. Raises `editor:show-build-output` so the bottom area
149
+ * focuses the Build Output utility.
150
+ */
151
+ export async function startBuild(target: BuildTarget): Promise<void> {
152
+ if (_session.phase === 'building') return;
153
+ setSession({ phase: 'building', target, log: [], result: null });
154
+ if (typeof window !== 'undefined') {
155
+ window.dispatchEvent(new CustomEvent('editor:show-build-output'));
156
+ }
157
+
158
+ const controller = new AbortController();
159
+ _abort = controller;
160
+
161
+ try {
162
+ const res = await startExport(target, controller.signal);
163
+
164
+ if (!res.ok) {
165
+ const err = await res.json().catch(() => ({ error: 'Build request failed' }));
166
+ setSession({
167
+ phase: 'done',
168
+ log: [..._session.log, `Error: ${err.error ?? res.statusText}`],
169
+ result: { ok: false, code: null },
170
+ });
171
+ return;
172
+ }
173
+
174
+ const reader = res.body!.getReader();
175
+ const decoder = new TextDecoder();
176
+ let buffer = '';
177
+
178
+ while (true) {
179
+ const { done, value } = await reader.read();
180
+ if (done) break;
181
+ buffer += decoder.decode(value, { stream: true });
182
+
183
+ const parts = buffer.split('\n\n');
184
+ buffer = parts.pop()!;
185
+
186
+ for (const part of parts) {
187
+ if (part.startsWith(':')) continue;
188
+ const lines = part.split('\n');
189
+ let event = '';
190
+ let data = '';
191
+ for (const line of lines) {
192
+ if (line.startsWith('event: ')) event = line.slice(7);
193
+ else if (line.startsWith('data: ')) data = line.slice(6);
194
+ }
195
+ if (event === 'log') {
196
+ let lineText: string;
197
+ try {
198
+ lineText = JSON.parse(data) as string;
199
+ } catch {
200
+ lineText = data;
201
+ }
202
+ setSession({ log: [..._session.log, lineText] });
203
+ } else if (event === 'done') {
204
+ let result: BuildResult;
205
+ try {
206
+ result = JSON.parse(data) as BuildResult;
207
+ } catch {
208
+ result = { ok: false, code: null };
209
+ }
210
+ setSession({ phase: 'done', result });
211
+ }
212
+ }
213
+ }
214
+ } catch (err: unknown) {
215
+ if ((err as Error).name !== 'AbortError') {
216
+ setSession({
217
+ phase: 'done',
218
+ log: [..._session.log, `Error: ${(err as Error).message}`],
219
+ result: { ok: false, code: null },
220
+ });
221
+ }
222
+ }
223
+ }
224
+
225
+ /** Test-only reset. */
226
+ export function __resetBuildSessionForTest(): void {
227
+ _abort?.abort();
228
+ _abort = null;
229
+ _session = { phase: 'idle', target: 'web', log: [], result: null };
230
+ _version++;
231
+ for (const fn of _listeners) fn();
232
+ }
233
+
234
+ // --- Shell contributions (utility + status) ---------------------------------
235
+
236
+ const registrationGroup = createHmrRegistrationGroup(import.meta.hot, 'build-contributions');
237
+
238
+ /**
239
+ * Idempotent one-time registration of the build shell contributions: the
240
+ * on-demand `Build Output` utility and the `build-progress` status
241
+ * contribution. Call in server mode only (builds spawn
242
+ * `npm run <script>` server-side — no browser-mode equivalent, same gate the
243
+ * old tab had). Output appears when a build starts, when a failure requests
244
+ * attention, or when the user explicitly opens it; an empty output tab does
245
+ * not occupy the clean default strip.
246
+ */
247
+ export function ensureBuildContributionsRegistered(): void {
248
+ registrationGroup.ensure((track) => {
249
+ track(
250
+ registerWorkspaceUtility({
251
+ id: 'build-output',
252
+ title: 'Build Output',
253
+ Content: BuildOutputUtility,
254
+ visibleByDefault: false,
255
+ closeable: true,
256
+ }),
257
+ );
258
+ track(
259
+ registerWorkspaceStatus({
260
+ id: 'build-progress',
261
+ align: 'left',
262
+ Content: BuildProgressStatus,
263
+ }),
264
+ );
265
+ });
266
+ }
267
+
268
+ /** Test-only: allow re-registration after a registry reset. */
269
+ export function __resetBuildContributionsForTest(): void {
270
+ registrationGroup.reset();
271
+ }
272
+
273
+ /** Build Output utility content: the session log (+ result line), or its
274
+ * own empty state before any build has run. */
275
+ export function BuildOutputUtility() {
276
+ useSyncExternalStore(subscribeBuildSession, buildSessionVersion);
277
+ const session = _session;
278
+ const logRef = useRef<HTMLDivElement>(null);
279
+
280
+ // Auto-scroll (the old panel's log behavior, unchanged).
281
+ useEffect(() => {
282
+ if (logRef.current) logRef.current.scrollTop = logRef.current.scrollHeight;
283
+ });
284
+
285
+ if (session.phase === 'idle' && session.log.length === 0) {
286
+ return (
287
+ <div
288
+ data-testid="build-output-empty"
289
+ style={{ padding: 12, fontSize: 12, color: text[2], maxWidth: 480 }}
290
+ >
291
+ <p style={{ margin: 0 }}>
292
+ No build output yet — run a build from the <b>Build Profiles</b> document (toolbar hammer,
293
+ or the palette's Build actions).
294
+ </p>
295
+ </div>
296
+ );
297
+ }
298
+ return (
299
+ <div
300
+ data-testid="build-output-utility"
301
+ style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column' }}
302
+ >
303
+ <div ref={logRef} style={logBoxStyle}>
304
+ {session.log.map((line, i) => (
305
+ <div key={i}>{line || ' '}</div>
306
+ ))}
307
+ {session.phase === 'building' && (
308
+ <div
309
+ style={{
310
+ display: 'inline-block',
311
+ width: 6,
312
+ height: 14,
313
+ background: accent,
314
+ animation: 'blink 1s step-end infinite',
315
+ }}
316
+ />
317
+ )}
318
+ {session.phase === 'done' && session.result && (
319
+ <div style={{ color: session.result.ok ? success : danger, marginTop: 4 }}>
320
+ {session.result.ok
321
+ ? 'Build complete.'
322
+ : `Build failed (exit code: ${session.result.code ?? 'unknown'}).`}
323
+ </div>
324
+ )}
325
+ </div>
326
+ </div>
327
+ );
328
+ }
329
+
330
+ /** Compact build-progress status contribution (§5 "build progress | status
331
+ * bar"). Nothing while idle; consumed by nothing until W4's status bar. */
332
+ export function BuildProgressStatus() {
333
+ useSyncExternalStore(subscribeBuildSession, buildSessionVersion);
334
+ const session = _session;
335
+ if (session.phase === 'idle') return null;
336
+ if (session.phase === 'building') {
337
+ return (
338
+ <span data-testid="status-build-progress" style={{ fontSize: 10, color: accent }}>
339
+ Building {session.target}…
340
+ </span>
341
+ );
342
+ }
343
+ return (
344
+ <span
345
+ data-testid="status-build-progress"
346
+ style={{ fontSize: 10, color: session.result?.ok ? success : danger }}
347
+ >
348
+ {session.result?.ok ? 'Build ✓' : 'Build ✗'}
349
+ </span>
350
+ );
351
+ }
352
+
353
+ // --- The Build Profiles document --------------------------------------------
354
+
355
+ /** Stable §7.1 identity of the single Build Profiles document. */
356
+ export const BUILD_PROFILES_DOCUMENT_ID = 'workspace:build-profiles';
357
+
358
+ /** The narrow store surface (T6.3 tab hand-off). */
359
+ export interface BuildDocumentStore {
360
+ setActiveViewportTab(tab: 'scene' | 'game'): void;
361
+ }
362
+
363
+ /** Open (or activate) the center `Build Profiles` document — the target of
364
+ * every build ACTION entry point (PlayBar hammer, palette, `show-build`). */
365
+ export function openBuildProfilesDocument(store: BuildDocumentStore): string {
366
+ return openWorkspaceDocument({
367
+ id: BUILD_PROFILES_DOCUMENT_ID,
368
+ title: 'Build Profiles',
369
+ kind: 'build-profiles',
370
+ Content: BuildProfilesDocumentContent,
371
+ closeable: true,
372
+ onActivate: () => store.setActiveViewportTab('scene'),
373
+ });
374
+ }
375
+
376
+ /** The configuration surface — the old panel's target list + settings form,
377
+ * minus the log (Build Output's job now). */
378
+ export function BuildProfilesDocumentContent(_props: WorkspaceDocumentContentProps) {
379
+ useSyncExternalStore(subscribeBuildSession, buildSessionVersion);
380
+ const session = _session;
381
+ // Session history via the workspace-history bridge (see its doc comment);
382
+ // the per-service manager is cached inside getProjectFileHistory.
383
+ const history = workspaceHistoryService();
384
+ const fileHistory = history ? getProjectFileHistory(history) : null;
385
+ const [config, setConfig] = useState<ProjectConfig>(DEFAULT_CONFIG);
386
+ const [selected, setSelected] = useState<BuildTarget>('web');
387
+ const saveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
388
+
389
+ const loadConfig = useCallback(async () => {
390
+ const manifestText = await readProjectTextFile(MANIFEST_PATH);
391
+ const manifest = manifestText
392
+ ? (JSON.parse(manifestText) as Partial<ProjectConfig>)
393
+ : DEFAULT_CONFIG;
394
+ setConfig({
395
+ ...DEFAULT_CONFIG,
396
+ ...manifest,
397
+ resolution: { ...DEFAULT_CONFIG.resolution, ...manifest.resolution },
398
+ });
399
+ }, []);
400
+
401
+ useEffect(() => {
402
+ void loadConfig().catch(() => {});
403
+ return (
404
+ fileHistory?.subscribeAll((path) => {
405
+ if (path === MANIFEST_PATH) void loadConfig().catch(() => {});
406
+ }) ?? (() => {})
407
+ );
408
+ }, [fileHistory, loadConfig]);
409
+
410
+ useEffect(
411
+ () => () => {
412
+ if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
413
+ },
414
+ [],
415
+ );
416
+
417
+ // Merge only the build-owned fields into the manifest. Roots and every
418
+ // unrelated authored field must survive a Build Profiles edit.
419
+ const saveConfig = useCallback(
420
+ (cfg: ProjectConfig) => {
421
+ if (saveTimerRef.current) clearTimeout(saveTimerRef.current);
422
+ saveTimerRef.current = setTimeout(async () => {
423
+ const currentText = await readProjectTextFile(MANIFEST_PATH);
424
+ if (!currentText)
425
+ throw new Error(`Cannot edit build settings: ${MANIFEST_PATH} is missing`);
426
+ const manifest = JSON.parse(currentText) as Record<string, unknown>;
427
+ const content = `${JSON.stringify(
428
+ { ...manifest, name: cfg.name, resolution: cfg.resolution, appId: cfg.appId },
429
+ null,
430
+ 2,
431
+ )}\n`;
432
+ if (!fileHistory) {
433
+ await saveFile(MANIFEST_PATH, content);
434
+ return;
435
+ }
436
+ await fileHistory
437
+ .write(MANIFEST_PATH, content, {
438
+ label: 'Edit Build Settings',
439
+ kind: 'settings',
440
+ contentType: 'application/json',
441
+ })
442
+ .catch(() => void loadConfig().catch(() => {}));
443
+ }, 500);
444
+ },
445
+ [fileHistory, loadConfig],
446
+ );
447
+
448
+ const updateConfig = useCallback(
449
+ (patch: Partial<ProjectConfig>) => {
450
+ setConfig((prev) => {
451
+ const next = { ...prev, ...patch };
452
+ saveConfig(next);
453
+ return next;
454
+ });
455
+ },
456
+ [saveConfig],
457
+ );
458
+
459
+ const updateResolution = useCallback(
460
+ (key: 'width' | 'height', value: number) => {
461
+ setConfig((prev) => {
462
+ const next = { ...prev, resolution: { ...prev.resolution, [key]: Math.round(value) } };
463
+ saveConfig(next);
464
+ return next;
465
+ });
466
+ },
467
+ [saveConfig],
468
+ );
469
+
470
+ const phase = session.phase;
471
+ const targetInfo = TARGETS.find((t) => t.id === selected)!;
472
+
473
+ return (
474
+ <div
475
+ data-testid="build-profiles-document"
476
+ style={{ display: 'flex', flex: 1, minHeight: 0, background: bg[1], pointerEvents: 'auto' }}
477
+ >
478
+ {/* Left: target list (the "profiles") */}
479
+ <div style={targetListStyle}>
480
+ {TARGETS.map((t) => (
481
+ <EditorListButton
482
+ key={t.id}
483
+ selected={selected === t.id}
484
+ disabled={phase === 'building'}
485
+ onClick={() => {
486
+ if (phase !== 'building') setSelected(t.id);
487
+ }}
488
+ style={{
489
+ padding: '8px 12px',
490
+ opacity: phase === 'building' && selected !== t.id ? 0.4 : 1,
491
+ }}
492
+ >
493
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
494
+ <EditorIcon
495
+ icon={t.icon}
496
+ style={{ fontSize: 13, color: selected === t.id ? accent : text[2] }}
497
+ />
498
+ <div>
499
+ <div
500
+ style={{
501
+ fontSize: 12,
502
+ fontWeight: 600,
503
+ color: selected === t.id ? text[1] : text[2],
504
+ }}
505
+ >
506
+ {t.label}
507
+ </div>
508
+ <div style={{ fontSize: 10, color: text[3], marginTop: 1 }}>{t.subtitle}</div>
509
+ </div>
510
+ </div>
511
+ </EditorListButton>
512
+ ))}
513
+ </div>
514
+
515
+ {/* Right: settings + phase-dependent action row */}
516
+ <div
517
+ style={{
518
+ flex: 1,
519
+ display: 'flex',
520
+ flexDirection: 'column',
521
+ minWidth: 0,
522
+ padding: '10px 16px',
523
+ maxWidth: 560,
524
+ }}
525
+ >
526
+ <div style={sectionHeaderStyle}>Project</div>
527
+
528
+ <div style={fieldRowStyle}>
529
+ <label style={labelStyle}>Name</label>
530
+ <TextInput
531
+ className="vgai-input"
532
+ style={{ flex: 1 }}
533
+ value={config.name}
534
+ onChange={(e) => updateConfig({ name: e.target.value })}
535
+ />
536
+ </div>
537
+
538
+ <div style={fieldRowStyle}>
539
+ <label style={labelStyle}>Resolution</label>
540
+ <div style={{ display: 'flex', alignItems: 'center', gap: 4, flex: 1 }}>
541
+ <TextInput
542
+ type="number"
543
+ className="vgai-input"
544
+ style={{ width: 0, flex: 1 }}
545
+ value={config.resolution.width}
546
+ onChange={(e) => {
547
+ const v = Number.parseInt(e.target.value, 10);
548
+ if (Number.isFinite(v) && v > 0) updateResolution('width', v);
549
+ }}
550
+ />
551
+ <span style={{ fontSize: 10, color: text[3] }}>&times;</span>
552
+ <TextInput
553
+ type="number"
554
+ className="vgai-input"
555
+ style={{ width: 0, flex: 1 }}
556
+ value={config.resolution.height}
557
+ onChange={(e) => {
558
+ const v = Number.parseInt(e.target.value, 10);
559
+ if (Number.isFinite(v) && v > 0) updateResolution('height', v);
560
+ }}
561
+ />
562
+ </div>
563
+ </div>
564
+
565
+ <div style={fieldRowStyle}>
566
+ <label style={labelStyle}>App ID</label>
567
+ <TextInput
568
+ className="vgai-input"
569
+ style={{ flex: 1 }}
570
+ value={config.appId}
571
+ onChange={(e) => updateConfig({ appId: e.target.value })}
572
+ />
573
+ </div>
574
+
575
+ <div style={{ ...fieldRowStyle, marginTop: 4 }}>
576
+ <label style={labelStyle}>Output</label>
577
+ <span style={{ fontSize: 12, color: text[2] }}>{targetInfo.output}</span>
578
+ </div>
579
+
580
+ {/* Action row: Build / Cancel / result — the log itself streams into
581
+ the Build Output utility (the split's whole point). */}
582
+ <div
583
+ style={{
584
+ marginTop: 12,
585
+ display: 'flex',
586
+ alignItems: 'center',
587
+ gap: 8,
588
+ paddingTop: 8,
589
+ borderTop: `1px solid ${border[1]}`,
590
+ }}
591
+ >
592
+ {phase === 'building' ? (
593
+ <>
594
+ <span style={{ fontSize: 12, fontWeight: 600, color: accent }}>
595
+ Building for {TARGETS.find((t) => t.id === session.target)?.label ?? session.target}
596
+ … (log in Build Output below)
597
+ </span>
598
+ <Button size="compact" onClick={cancelBuild}>
599
+ Cancel
600
+ </Button>
601
+ </>
602
+ ) : (
603
+ <>
604
+ {phase === 'done' && session.result && (
605
+ <span
606
+ data-testid="build-profiles-result"
607
+ style={{
608
+ fontSize: 12,
609
+ fontWeight: 600,
610
+ color: session.result.ok ? success : danger,
611
+ }}
612
+ >
613
+ {session.result.ok
614
+ ? '✓ Build complete'
615
+ : `✗ Build failed (exit ${session.result.code ?? 'unknown'})`}
616
+ </span>
617
+ )}
618
+ {phase === 'done' && session.result?.ok && session.target === 'web' && (
619
+ <a
620
+ href={getDownloadUrl(targetInfo.output)}
621
+ download={targetInfo.output}
622
+ className="vgai-btn"
623
+ data-variant="secondary"
624
+ data-size="default"
625
+ style={{
626
+ textDecoration: 'none',
627
+ display: 'inline-flex',
628
+ }}
629
+ >
630
+ Download
631
+ </a>
632
+ )}
633
+ <Button
634
+ variant="primary"
635
+ data-testid="build-profiles-build"
636
+ onClick={() => {
637
+ if (phase === 'done') resetBuildSession();
638
+ void startBuild(selected);
639
+ }}
640
+ style={{ marginLeft: 'auto' }}
641
+ >
642
+ {phase === 'done' ? 'Build Again' : 'Build'}
643
+ </Button>
644
+ </>
645
+ )}
646
+ </div>
647
+ </div>
648
+ </div>
649
+ );
650
+ }
651
+
652
+ /* ---- Styles (carried from the old panel) ---- */
653
+
654
+ const targetListStyle: React.CSSProperties = {
655
+ width: 160,
656
+ flexShrink: 0,
657
+ borderRight: `1px solid ${border[1]}`,
658
+ overflowY: 'auto',
659
+ };
660
+
661
+ const sectionHeaderStyle: React.CSSProperties = {
662
+ fontSize: 10,
663
+ fontWeight: 700,
664
+ textTransform: 'uppercase',
665
+ color: text[3],
666
+ letterSpacing: '0.05em',
667
+ marginBottom: 6,
668
+ };
669
+
670
+ const fieldRowStyle: React.CSSProperties = {
671
+ display: 'flex',
672
+ alignItems: 'center',
673
+ gap: 6,
674
+ marginBottom: 4,
675
+ };
676
+
677
+ const labelStyle: React.CSSProperties = {
678
+ width: 65,
679
+ fontSize: 11,
680
+ color: text[2],
681
+ flexShrink: 0,
682
+ };
683
+
684
+ const logBoxStyle: React.CSSProperties = {
685
+ flex: 1,
686
+ overflowY: 'auto',
687
+ background: bg[0],
688
+ padding: '4px 8px',
689
+ fontFamily: fontMono,
690
+ fontSize: 11,
691
+ lineHeight: '16px',
692
+ color: text[2],
693
+ whiteSpace: 'pre-wrap',
694
+ wordBreak: 'break-all',
695
+ minHeight: 0,
696
+ };