@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
@@ -1,3 +1,4 @@
1
+ import { Button, IconButton, Select, TextInput, themeVars } from '../design-system';
1
2
  /**
2
3
  * Project screen — shown before the editor when no project is specified.
3
4
  *
@@ -7,6 +8,12 @@
7
8
  */
8
9
 
9
10
  import { useCallback, useEffect, useReducer, useRef, useState } from 'react';
11
+ import {
12
+ BUNDLED_EDITOR_COMPATIBILITY,
13
+ BUNDLED_EDITOR_VERSION,
14
+ BUNDLED_ENGINE_VERSION,
15
+ } from '../build-identity';
16
+ import { TONE_ERROR } from '../design-system';
10
17
  import {
11
18
  browseFolder,
12
19
  createProject,
@@ -17,6 +24,11 @@ import {
17
24
  type RecentProject,
18
25
  revealInFinder,
19
26
  } from '../editor-api';
27
+ import {
28
+ assertProjectCompatibility,
29
+ ProjectCompatibilityError,
30
+ type StartupRecovery,
31
+ } from '../editor-compatibility';
20
32
  import { getEditorMode } from '../editor-mode';
21
33
  import { type ActiveProject, type ProjectConfig, setActiveProject } from '../project-manager';
22
34
  import {
@@ -26,6 +38,7 @@ import {
26
38
  type StorageBackend,
27
39
  } from '../storage';
28
40
  import { readProjectManifest, seedIfEmpty } from '../storage/seed';
41
+ import { zIndex } from '../z-index';
29
42
 
30
43
  // ---------------------------------------------------------------------------
31
44
  // Injected stylesheet for interactive states
@@ -33,12 +46,18 @@ import { readProjectManifest, seedIfEmpty } from '../storage/seed';
33
46
 
34
47
  const STYLE_ID = 'vgai-project-screen';
35
48
 
49
+ // D-15: this CSS block used to hand-retype its own palette (three different
50
+ // themeVars.accent.default blues inside one file, its own menu recipe, its own button shapes)
51
+ // because a template string can't share inline-style constants. It now
52
+ // interpolates `theme.ts` tokens directly — same delivery mechanism
53
+ // (className + injected sheet, the only way to get :hover/:focus-visible/
54
+ // :disabled here), one source of truth for the values.
36
55
  const CSS = `
37
56
  .ps-card {
38
57
  position: relative;
39
- background: #222;
40
- border: 1px solid #333;
41
- border-radius: 8px;
58
+ background: ${themeVars.surface.panel};
59
+ border: 1px solid ${themeVars.boundary.default};
60
+ border-radius: ${themeVars.shape.large};
42
61
  padding: 14px 14px 12px;
43
62
  cursor: pointer;
44
63
  text-align: left;
@@ -47,21 +66,21 @@ const CSS = `
47
66
  transition: border-color 0.15s, background 0.15s;
48
67
  width: 100%;
49
68
  }
50
- .ps-card:hover { border-color: #555; background: #2a2a2a; }
51
- .ps-card:focus-visible { outline: 1px solid #579eff; outline-offset: 1px; }
52
- .ps-card:disabled { opacity: 0.5; cursor: default; }
69
+ .ps-card:hover { border-color: ${themeVars.boundary.strong}; background: ${themeVars.surface.chrome}; }
70
+ .ps-card:focus-visible { outline: 1px solid ${themeVars.accent.default}; outline-offset: 1px; }
71
+ .ps-card:disabled, .ps-card[aria-disabled="true"] { opacity: 0.5; cursor: default; }
53
72
 
54
73
  .ps-card-remove {
55
74
  position: absolute;
56
75
  top: 6px;
57
76
  right: 6px;
58
- background: #333;
77
+ background: ${themeVars.surface.raised};
59
78
  border: none;
60
- color: #888;
61
- font-size: 14px;
79
+ color: ${themeVars.content.dim};
80
+ font-size: var(--vgai-font-xl);
62
81
  width: 20px;
63
82
  height: 20px;
64
- border-radius: 4px;
83
+ border-radius: ${themeVars.shape.small};
65
84
  cursor: pointer;
66
85
  opacity: 0;
67
86
  transition: opacity 0.15s;
@@ -70,58 +89,19 @@ const CSS = `
70
89
  justify-content: center;
71
90
  }
72
91
  .ps-card:hover .ps-card-remove { opacity: 1; }
73
- .ps-card-remove:hover { background: #444; color: #ccc; }
74
-
75
- .ps-btn-primary {
76
- background: #3a8eef;
77
- color: #fff;
78
- border: none;
79
- border-radius: 6px;
80
- padding: 10px 20px;
81
- font-size: 14px;
82
- font-weight: 600;
83
- font-family: inherit;
84
- cursor: pointer;
85
- transition: background 0.15s;
86
- }
87
- .ps-btn-primary:hover { background: #4a9eff; }
88
- .ps-btn-primary:focus-visible { outline: 1px solid #579eff; outline-offset: 1px; }
89
- .ps-btn-primary:disabled { opacity: 0.5; cursor: default; }
90
-
91
- .ps-btn-secondary {
92
- background: #333;
93
- color: #c5c8ce;
94
- border: none;
95
- border-radius: 6px;
96
- padding: 10px 20px;
97
- font-size: 14px;
98
- font-family: inherit;
99
- cursor: pointer;
100
- transition: background 0.15s;
101
- white-space: nowrap;
102
- }
103
- .ps-btn-secondary:hover { background: #444; }
104
- .ps-btn-secondary:focus-visible { outline: 1px solid #579eff; outline-offset: 1px; }
105
- .ps-btn-secondary:disabled { opacity: 0.5; cursor: default; }
106
-
107
- .ps-input {
108
- flex: 1;
109
- background: #2a2a2a;
110
- border: 1px solid #333;
111
- border-radius: 6px;
112
- padding: 8px 12px;
113
- font-size: 14px;
114
- color: #e0e0e0;
115
- outline: none;
116
- font-family: inherit;
117
- }
118
- .ps-input:focus { border-color: #579eff; }
92
+ /* Hover fill is one neutral step above the resting bg-3 (#333) — that step
93
+ renders as #444444, a value the bg ramp deliberately has no slot for
94
+ (adding a bg-4 for one hover state would invite ramp sprawl). border-2 is
95
+ the token that carries #444444; the theme already shares values across
96
+ ramps by design (border-1 ≡ bg-3 — see theme.ts), so this is the same
97
+ documented cross-ramp reuse, not drift. (U6 punch-list #4.) */
98
+ .ps-card-remove:hover { background: ${themeVars.boundary.strong}; color: ${themeVars.content.primary}; }
119
99
 
120
100
  .ps-template {
121
101
  flex: 1;
122
- background: #222;
123
- border: 1px solid #333;
124
- border-radius: 8px;
102
+ background: ${themeVars.surface.panel};
103
+ border: 1px solid ${themeVars.boundary.default};
104
+ border-radius: ${themeVars.shape.large};
125
105
  padding: 12px 14px;
126
106
  cursor: pointer;
127
107
  text-align: left;
@@ -129,49 +109,50 @@ const CSS = `
129
109
  color: inherit;
130
110
  transition: border-color 0.15s;
131
111
  }
132
- .ps-template:hover { border-color: #555; }
133
- .ps-template[data-selected="true"] { border-color: #3a8eef; }
112
+ .ps-template:hover { border-color: ${themeVars.boundary.strong}; }
113
+ .ps-template:focus-visible { outline: 1px solid ${themeVars.accent.default}; outline-offset: 1px; }
114
+ .ps-template[data-selected="true"] { border-color: ${themeVars.accent.default}; }
134
115
 
135
116
  .ps-ctx-menu {
136
117
  position: fixed;
137
- background: #2a2a2a;
138
- border: 1px solid #444;
139
- border-radius: 6px;
118
+ background: ${themeVars.surface.chrome};
119
+ border: 1px solid ${themeVars.boundary.strong};
120
+ border-radius: ${themeVars.shape.medium};
140
121
  padding: 4px 0;
141
122
  min-width: 160px;
142
- z-index: 100;
143
- box-shadow: 0 4px 16px rgba(0,0,0,0.4);
123
+ z-index: ${zIndex.dropdown};
124
+ box-shadow: ${themeVars.elevation.medium};
144
125
  }
145
126
  .ps-ctx-item {
146
127
  display: block;
147
128
  width: 100%;
148
129
  background: none;
149
130
  border: none;
150
- color: #c5c8ce;
131
+ color: ${themeVars.content.primary};
151
132
  font: inherit;
152
- font-size: 13px;
133
+ font-size: var(--vgai-font-lg);
153
134
  padding: 6px 14px;
154
135
  text-align: left;
155
136
  cursor: pointer;
156
137
  }
157
- .ps-ctx-item:hover { background: #333; }
138
+ .ps-ctx-item:hover { background: ${themeVars.accent.muted}; }
158
139
  .ps-ctx-sep {
159
140
  height: 1px;
160
- background: #444;
141
+ background: ${themeVars.boundary.strong};
161
142
  margin: 4px 0;
162
143
  }
163
144
 
164
145
  .ps-thumbnail {
165
146
  width: 100%;
166
147
  aspect-ratio: 16/9;
167
- background: #1a1a1a;
168
- border-radius: 4px;
148
+ background: ${themeVars.surface.shell};
149
+ border-radius: ${themeVars.shape.small};
169
150
  margin-bottom: 8px;
170
151
  overflow: hidden;
171
152
  display: flex;
172
153
  align-items: center;
173
154
  justify-content: center;
174
- color: #333;
155
+ color: ${themeVars.surface.raised};
175
156
  font-size: 24px;
176
157
  }
177
158
  .ps-thumbnail img {
@@ -200,12 +181,12 @@ interface ScreenState {
200
181
  view: 'home' | 'new-project';
201
182
  recentProjects: RecentProject[];
202
183
  loaded: boolean;
203
- error: string | null;
184
+ error: ScreenError | null;
204
185
  busy: boolean; // opening or creating
205
186
  // New project form
206
187
  projectName: string;
207
188
  projectLocation: string;
208
- template: 'starter' | 'empty' | 'example';
189
+ template: 'default' | '2d' | 'react' | 'example';
209
190
  exampleId: string | null;
210
191
  availableExamples: ExampleInfo[];
211
192
  }
@@ -213,12 +194,12 @@ interface ScreenState {
213
194
  type ScreenAction =
214
195
  | { type: 'LOADED'; projects: RecentProject[] }
215
196
  | { type: 'SET_VIEW'; view: 'home' | 'new-project' }
216
- | { type: 'SET_ERROR'; error: string | null }
197
+ | { type: 'SET_ERROR'; error: unknown | null }
217
198
  | { type: 'SET_BUSY'; busy: boolean }
218
199
  | { type: 'REMOVE_RECENT'; path: string }
219
200
  | { type: 'SET_NAME'; name: string }
220
201
  | { type: 'SET_LOCATION'; location: string }
221
- | { type: 'SET_TEMPLATE'; template: 'starter' | 'empty' | 'example' }
202
+ | { type: 'SET_TEMPLATE'; template: 'default' | '2d' | 'react' | 'example' }
222
203
  | { type: 'SET_EXAMPLE_ID'; exampleId: string }
223
204
  | { type: 'SET_AVAILABLE_EXAMPLES'; examples: ExampleInfo[] };
224
205
 
@@ -230,11 +211,24 @@ const initialState: ScreenState = {
230
211
  busy: false,
231
212
  projectName: 'My Game',
232
213
  projectLocation: '~/VGAI Projects',
233
- template: 'starter',
214
+ template: 'default',
234
215
  exampleId: null,
235
216
  availableExamples: [],
236
217
  };
237
218
 
219
+ interface ScreenError {
220
+ message: string;
221
+ recovery?: StartupRecovery;
222
+ }
223
+
224
+ function screenError(error: unknown): ScreenError | null {
225
+ if (error === null) return null;
226
+ if (error instanceof ProjectCompatibilityError) {
227
+ return { message: error.message, recovery: error.recovery };
228
+ }
229
+ return { message: error instanceof Error ? error.message : String(error) };
230
+ }
231
+
238
232
  function reducer(state: ScreenState, action: ScreenAction): ScreenState {
239
233
  switch (action.type) {
240
234
  case 'LOADED':
@@ -247,7 +241,7 @@ function reducer(state: ScreenState, action: ScreenAction): ScreenState {
247
241
  case 'SET_VIEW':
248
242
  return { ...state, view: action.view, error: null };
249
243
  case 'SET_ERROR':
250
- return { ...state, error: action.error, busy: false };
244
+ return { ...state, error: screenError(action.error), busy: false };
251
245
  case 'SET_BUSY':
252
246
  return { ...state, busy: action.busy, error: null };
253
247
  case 'REMOVE_RECENT':
@@ -324,7 +318,7 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
324
318
  dispatch({ type: 'SET_ERROR', error: `Failed to open project at ${path}` });
325
319
  }
326
320
  } catch (err) {
327
- dispatch({ type: 'SET_ERROR', error: err instanceof Error ? err.message : String(err) });
321
+ dispatch({ type: 'SET_ERROR', error: err });
328
322
  }
329
323
  },
330
324
  [onOpen],
@@ -358,7 +352,7 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
358
352
  dispatch({ type: 'SET_ERROR', error: result.error ?? 'Failed to create project.' });
359
353
  }
360
354
  } catch (err) {
361
- dispatch({ type: 'SET_ERROR', error: err instanceof Error ? err.message : String(err) });
355
+ dispatch({ type: 'SET_ERROR', error: err });
362
356
  }
363
357
  }, [state.projectName, state.projectLocation, state.template, state.exampleId, onOpen]);
364
358
 
@@ -367,7 +361,7 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
367
361
  const path = await browseFolder('Select a project folder');
368
362
  if (path) openProject(path);
369
363
  } catch (err) {
370
- dispatch({ type: 'SET_ERROR', error: err instanceof Error ? err.message : String(err) });
364
+ dispatch({ type: 'SET_ERROR', error: err });
371
365
  }
372
366
  }, [openProject]);
373
367
 
@@ -376,7 +370,7 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
376
370
  const path = await browseFolder('Choose project location');
377
371
  if (path) dispatch({ type: 'SET_LOCATION', location: path });
378
372
  } catch (err) {
379
- dispatch({ type: 'SET_ERROR', error: err instanceof Error ? err.message : String(err) });
373
+ dispatch({ type: 'SET_ERROR', error: err });
380
374
  }
381
375
  }, []);
382
376
 
@@ -386,6 +380,7 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
386
380
  async (backend: StorageBackend) => {
387
381
  await seedIfEmpty(backend);
388
382
  const config = await readProjectManifest(backend);
383
+ assertProjectCompatibility(config, BUNDLED_EDITOR_COMPATIBILITY);
389
384
  const project: ActiveProject = { rootPath: 'fsa', config };
390
385
  setActiveProject(project);
391
386
  onOpen(project);
@@ -396,17 +391,25 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
396
391
  // Browser-first mode: open a REAL on-disk folder via the File System Access
397
392
  // API (Chromium). The picked folder is remembered so a reload reopens it.
398
393
  const handleOpenFolderFsa = useCallback(async () => {
399
- const pick = getHostCapabilities().pickProjectRoot;
400
- if (!pick) return;
401
- const backend = await pick();
402
- if (backend) await openFsaBackend(backend);
394
+ try {
395
+ const pick = getHostCapabilities().pickProjectRoot;
396
+ if (!pick) return;
397
+ const backend = await pick();
398
+ if (backend) await openFsaBackend(backend);
399
+ } catch (error) {
400
+ dispatch({ type: 'SET_ERROR', error });
401
+ }
403
402
  }, [openFsaBackend]);
404
403
 
405
404
  // Reopen the remembered on-disk folder — re-grants read/write permission
406
405
  // (this click IS the required user gesture) and opens it.
407
406
  const handleReopenFolder = useCallback(async () => {
408
- const backend = await reopenPersistedProjectRoot();
409
- if (backend) await openFsaBackend(backend);
407
+ try {
408
+ const backend = await reopenPersistedProjectRoot();
409
+ if (backend) await openFsaBackend(backend);
410
+ } catch (error) {
411
+ dispatch({ type: 'SET_ERROR', error });
412
+ }
410
413
  }, [openFsaBackend]);
411
414
 
412
415
  const slug = state.projectName
@@ -419,12 +422,14 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
419
422
  <div style={S.inner}>
420
423
  <header style={S.header}>
421
424
  <div style={S.title}>VGAI ENGINE</div>
422
- <div style={S.version}>v0.1.0</div>
425
+ <div style={S.version}>
426
+ Editor v{BUNDLED_EDITOR_VERSION} · Engine v{BUNDLED_ENGINE_VERSION}
427
+ </div>
423
428
  </header>
424
429
 
425
430
  {state.error && (
426
431
  <ErrorBanner
427
- message={state.error}
432
+ error={state.error}
428
433
  onDismiss={() => dispatch({ type: 'SET_ERROR', error: null })}
429
434
  />
430
435
  )}
@@ -436,26 +441,32 @@ export function ProjectScreen({ onOpen }: ProjectScreenProps) {
436
441
  gap: 10,
437
442
  alignItems: 'center',
438
443
  margin: '2px 0 14px',
439
- color: '#8b919b',
440
- fontSize: 13,
444
+ color: themeVars.content.muted,
445
+ fontSize: 'var(--vgai-font-lg)',
441
446
  }}
442
447
  >
443
448
  <span>Projects are stored in your browser (OPFS) — nothing leaves this device.</span>
444
449
  {typeof window !== 'undefined' && 'showDirectoryPicker' in window && (
445
450
  <>
446
451
  {persistedRootName && (
447
- <button
452
+ <Button
448
453
  type="button"
449
- className="ps-btn-primary"
454
+ variant="primary"
455
+ size="comfortable"
450
456
  onClick={handleReopenFolder}
451
457
  data-testid="reopen-folder"
452
458
  >
453
459
  Reopen “{persistedRootName}”
454
- </button>
460
+ </Button>
455
461
  )}
456
- <button type="button" className="ps-btn-secondary" onClick={handleOpenFolderFsa}>
462
+ <Button
463
+ type="button"
464
+ variant="secondary"
465
+ size="comfortable"
466
+ onClick={handleOpenFolderFsa}
467
+ >
457
468
  Open Folder…
458
- </button>
469
+ </Button>
459
470
  </>
460
471
  )}
461
472
  </div>
@@ -543,12 +554,12 @@ function HomeView({
543
554
  </section>
544
555
 
545
556
  <div style={S.actions}>
546
- <button type="button" className="ps-btn-primary" onClick={onNewProject}>
557
+ <Button type="button" variant="primary" size="comfortable" onClick={onNewProject}>
547
558
  + New Project
548
- </button>
549
- <button type="button" className="ps-btn-secondary" onClick={onBrowse}>
559
+ </Button>
560
+ <Button type="button" variant="secondary" size="comfortable" onClick={onBrowse}>
550
561
  Open Project...
551
- </button>
562
+ </Button>
552
563
  </div>
553
564
  </>
554
565
  );
@@ -575,12 +586,21 @@ function ProjectCard({ project, disabled, onOpen, onRemove }: ProjectCardProps)
575
586
 
576
587
  return (
577
588
  <>
578
- <button
579
- type="button"
589
+ <div
590
+ role="button"
591
+ tabIndex={disabled ? -1 : 0}
592
+ aria-disabled={disabled}
580
593
  className="ps-card"
581
- onClick={onOpen}
594
+ onClick={() => {
595
+ if (!disabled) onOpen();
596
+ }}
597
+ onKeyDown={(event) => {
598
+ if (!disabled && (event.key === 'Enter' || event.key === ' ')) {
599
+ event.preventDefault();
600
+ onOpen();
601
+ }
602
+ }}
582
603
  onContextMenu={handleContextMenu}
583
- disabled={disabled}
584
604
  >
585
605
  <div className="ps-thumbnail">
586
606
  {project.thumbnail ? <img src={project.thumbnail} alt={project.name} /> : '\u25a6'}
@@ -590,7 +610,7 @@ function ProjectCard({ project, disabled, onOpen, onRemove }: ProjectCardProps)
590
610
  {shortenPath(project.path)}
591
611
  </div>
592
612
  <div style={S.cardMeta}>{formatRelativeTime(project.lastOpened)}</div>
593
- <button
613
+ <Button
594
614
  type="button"
595
615
  className="ps-card-remove"
596
616
  title="Remove from list"
@@ -600,8 +620,8 @@ function ProjectCard({ project, disabled, onOpen, onRemove }: ProjectCardProps)
600
620
  }}
601
621
  >
602
622
  ×
603
- </button>
604
- </button>
623
+ </Button>
624
+ </div>
605
625
 
606
626
  {ctxMenu && (
607
627
  <ContextMenu
@@ -664,7 +684,7 @@ function ContextMenu({ x, y, onClose, items }: ContextMenuProps) {
664
684
  item.separator ? (
665
685
  <div key={`sep-${i}`} className="ps-ctx-sep" />
666
686
  ) : (
667
- <button
687
+ <Button
668
688
  key={item.label}
669
689
  type="button"
670
690
  className="ps-ctx-item"
@@ -674,7 +694,7 @@ function ContextMenu({ x, y, onClose, items }: ContextMenuProps) {
674
694
  }}
675
695
  >
676
696
  {item.label}
677
- </button>
697
+ </Button>
678
698
  ),
679
699
  )}
680
700
  </div>
@@ -688,14 +708,14 @@ function ContextMenu({ x, y, onClose, items }: ContextMenuProps) {
688
708
  interface NewProjectFormProps {
689
709
  name: string;
690
710
  location: string;
691
- template: 'starter' | 'empty' | 'example';
711
+ template: 'default' | '2d' | 'react' | 'example';
692
712
  exampleId: string | null;
693
713
  availableExamples: ExampleInfo[];
694
714
  slug: string;
695
715
  busy: boolean;
696
716
  onNameChange: (name: string) => void;
697
717
  onLocationChange: (location: string) => void;
698
- onTemplateChange: (template: 'starter' | 'empty' | 'example') => void;
718
+ onTemplateChange: (template: 'default' | '2d' | 'react' | 'example') => void;
699
719
  onExampleIdChange: (exampleId: string) => void;
700
720
  onBrowseLocation: () => void;
701
721
  onCancel: () => void;
@@ -724,47 +744,58 @@ function NewProjectForm({
724
744
 
725
745
  <label style={S.label}>
726
746
  Name
727
- <input className="ps-input" value={name} onChange={(e) => onNameChange(e.target.value)} />
747
+ <TextInput
748
+ style={{ flex: 1 }}
749
+ value={name}
750
+ onChange={(e) => onNameChange(e.target.value)}
751
+ />
728
752
  </label>
729
753
 
730
754
  <label style={S.label}>
731
755
  Location
732
756
  <div style={S.locationRow}>
733
- <input
734
- className="ps-input"
757
+ <TextInput
758
+ style={{ flex: 1 }}
735
759
  value={location}
736
760
  onChange={(e) => onLocationChange(e.target.value)}
737
761
  />
738
- <button type="button" className="ps-btn-secondary" onClick={onBrowseLocation}>
762
+ <Button type="button" variant="secondary" size="comfortable" onClick={onBrowseLocation}>
739
763
  Browse
740
- </button>
764
+ </Button>
741
765
  </div>
742
766
  </label>
743
767
 
744
768
  <div style={S.label}>
745
769
  Template
746
770
  <div style={S.templateRow}>
747
- <button
771
+ <Button
748
772
  type="button"
749
773
  className="ps-template"
750
- data-selected={template === 'starter'}
751
- onClick={() => onTemplateChange('starter')}
774
+ data-selected={template === 'default'}
775
+ onClick={() => onTemplateChange('default')}
752
776
  >
753
- <div style={S.templateName}>Starter</div>
754
- <div style={S.templateDesc}>
755
- 7 example scenes, prefabs, input maps, animation graphs
756
- </div>
757
- </button>
758
- <button
777
+ <div style={S.templateName}>3D + UI</div>
778
+ <div style={S.templateDesc}>Three.js game root plus React UI root</div>
779
+ </Button>
780
+ <Button
781
+ type="button"
782
+ className="ps-template"
783
+ data-selected={template === '2d'}
784
+ onClick={() => onTemplateChange('2d')}
785
+ >
786
+ <div style={S.templateName}>2D + UI</div>
787
+ <div style={S.templateDesc}>PixiJS game root plus React UI root</div>
788
+ </Button>
789
+ <Button
759
790
  type="button"
760
791
  className="ps-template"
761
- data-selected={template === 'empty'}
762
- onClick={() => onTemplateChange('empty')}
792
+ data-selected={template === 'react'}
793
+ onClick={() => onTemplateChange('react')}
763
794
  >
764
- <div style={S.templateName}>Empty</div>
765
- <div style={S.templateDesc}>Blank project with a default scene</div>
766
- </button>
767
- <button
795
+ <div style={S.templateName}>React</div>
796
+ <div style={S.templateDesc}>React-only game root</div>
797
+ </Button>
798
+ <Button
768
799
  type="button"
769
800
  className="ps-template"
770
801
  data-selected={template === 'example'}
@@ -774,12 +805,11 @@ function NewProjectForm({
774
805
  <div style={S.templateDesc}>
775
806
  Start from a real example project (e.g. the multiplayer arena)
776
807
  </div>
777
- </button>
808
+ </Button>
778
809
  </div>
779
810
  {template === 'example' && (
780
- <select
781
- className="ps-input"
782
- style={{ marginTop: 10 }}
811
+ <Select
812
+ style={{ flex: 1, marginTop: 10 }}
783
813
  value={exampleId ?? ''}
784
814
  onChange={(e) => onExampleIdChange(e.target.value)}
785
815
  >
@@ -791,7 +821,7 @@ function NewProjectForm({
791
821
  {ex.name}
792
822
  </option>
793
823
  ))}
794
- </select>
824
+ </Select>
795
825
  )}
796
826
  </div>
797
827
 
@@ -802,19 +832,26 @@ function NewProjectForm({
802
832
  )}
803
833
 
804
834
  <div style={S.formActions}>
805
- <button type="button" className="ps-btn-secondary" onClick={onCancel} disabled={busy}>
835
+ <Button
836
+ type="button"
837
+ variant="secondary"
838
+ size="comfortable"
839
+ onClick={onCancel}
840
+ disabled={busy}
841
+ >
806
842
  Cancel
807
- </button>
808
- <button
843
+ </Button>
844
+ <Button
809
845
  type="button"
810
- className="ps-btn-primary"
846
+ variant="primary"
847
+ size="comfortable"
811
848
  onClick={onCreate}
812
849
  disabled={
813
850
  busy || !name.trim() || !location.trim() || (template === 'example' && !exampleId)
814
851
  }
815
852
  >
816
853
  {busy ? 'Creating...' : 'Create Project'}
817
- </button>
854
+ </Button>
818
855
  </div>
819
856
  </div>
820
857
  );
@@ -824,13 +861,46 @@ function NewProjectForm({
824
861
  // ErrorBanner
825
862
  // ---------------------------------------------------------------------------
826
863
 
827
- function ErrorBanner({ message, onDismiss }: { message: string; onDismiss: () => void }) {
864
+ function ErrorBanner({ error, onDismiss }: { error: ScreenError; onDismiss: () => void }) {
865
+ const command = error.recovery && 'command' in error.recovery ? error.recovery.command : null;
828
866
  return (
829
- <div style={S.error}>
830
- <span>{message}</span>
831
- <button type="button" style={S.errorClose} onClick={onDismiss}>
867
+ <div role="alert" style={{ ...S.error, alignItems: 'flex-start', gap: 12 }}>
868
+ <div style={{ flex: 1 }}>
869
+ {error.recovery && (
870
+ <div style={{ marginBottom: 4, color: themeVars.content.primary, fontWeight: 600 }}>
871
+ {error.recovery.title}
872
+ </div>
873
+ )}
874
+ {error.recovery && <div style={{ marginBottom: 6 }}>{error.recovery.guidance}</div>}
875
+ <div>{error.message}</div>
876
+ {command && (
877
+ <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 8 }}>
878
+ <code
879
+ data-testid="project-screen-recovery-command"
880
+ style={{ fontFamily: themeVars.typography.mono }}
881
+ >
882
+ {command}
883
+ </code>
884
+ <Button
885
+ type="button"
886
+ variant="secondary"
887
+ size="compact"
888
+ onClick={() => void navigator.clipboard?.writeText(command)}
889
+ >
890
+ Copy
891
+ </Button>
892
+ </div>
893
+ )}
894
+ </div>
895
+ <IconButton
896
+ type="button"
897
+ variant="danger"
898
+ size="compact"
899
+ aria-label="Dismiss error"
900
+ onClick={onDismiss}
901
+ >
832
902
  ×
833
- </button>
903
+ </IconButton>
834
904
  </div>
835
905
  );
836
906
  }
@@ -869,12 +939,12 @@ const S = {
869
939
  left: 0,
870
940
  width: '100%',
871
941
  height: '100%',
872
- background: '#1a1a1a',
942
+ background: themeVars.surface.shell,
873
943
  display: 'flex',
874
944
  alignItems: 'center',
875
945
  justifyContent: 'center',
876
- fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
877
- color: '#c5c8ce',
946
+ fontFamily: themeVars.typography.sans,
947
+ color: themeVars.content.primary,
878
948
  pointerEvents: 'auto' as const,
879
949
  },
880
950
  inner: {
@@ -889,47 +959,39 @@ const S = {
889
959
  title: {
890
960
  fontSize: 28,
891
961
  fontWeight: 700,
892
- color: '#e0e0e0',
962
+ color: themeVars.content.primary,
893
963
  letterSpacing: 2,
894
964
  },
895
965
  version: {
896
- fontSize: 13,
897
- color: '#666',
966
+ fontSize: 'var(--vgai-font-lg)',
967
+ color: themeVars.content.dim,
898
968
  marginTop: 4,
899
969
  },
900
970
  error: {
901
- background: '#3a1c1c',
902
- border: '1px solid #6b2c2c',
903
- borderRadius: 6,
971
+ background: TONE_ERROR.bg,
972
+ border: `1px solid ${TONE_ERROR.border}`,
973
+ borderRadius: themeVars.shape.medium,
904
974
  padding: '10px 16px',
905
975
  marginBottom: 16,
906
- fontSize: 13,
907
- color: '#e8a0a0',
976
+ fontSize: 'var(--vgai-font-lg)',
977
+ color: TONE_ERROR.fg,
908
978
  display: 'flex',
909
979
  justifyContent: 'space-between',
910
980
  alignItems: 'center',
911
981
  },
912
- errorClose: {
913
- background: 'none',
914
- border: 'none',
915
- color: '#e8a0a0',
916
- fontSize: 18,
917
- cursor: 'pointer',
918
- padding: '0 4px',
919
- },
920
982
  section: {
921
983
  marginBottom: 24,
922
984
  },
923
985
  sectionTitle: {
924
- fontSize: 13,
925
- color: '#888',
986
+ fontSize: 'var(--vgai-font-lg)',
987
+ color: themeVars.content.muted,
926
988
  textTransform: 'uppercase' as const,
927
989
  letterSpacing: 1,
928
990
  marginBottom: 12,
929
991
  },
930
992
  empty: {
931
- color: '#555',
932
- fontSize: 13,
993
+ color: themeVars.content.dim,
994
+ fontSize: 'var(--vgai-font-lg)',
933
995
  padding: '24px 0',
934
996
  textAlign: 'center' as const,
935
997
  },
@@ -939,17 +1001,17 @@ const S = {
939
1001
  gap: 10,
940
1002
  },
941
1003
  cardName: {
942
- fontSize: 14,
1004
+ fontSize: 'var(--vgai-font-xl)',
943
1005
  fontWeight: 600,
944
- color: '#e0e0e0',
1006
+ color: themeVars.content.primary,
945
1007
  marginBottom: 2,
946
1008
  overflow: 'hidden',
947
1009
  textOverflow: 'ellipsis',
948
1010
  whiteSpace: 'nowrap' as const,
949
1011
  },
950
1012
  cardMeta: {
951
- fontSize: 11,
952
- color: '#666',
1013
+ fontSize: 'var(--vgai-font-base)',
1014
+ color: themeVars.content.dim,
953
1015
  overflow: 'hidden',
954
1016
  textOverflow: 'ellipsis',
955
1017
  whiteSpace: 'nowrap' as const,
@@ -967,8 +1029,8 @@ const S = {
967
1029
  display: 'flex',
968
1030
  flexDirection: 'column' as const,
969
1031
  gap: 6,
970
- fontSize: 13,
971
- color: '#888',
1032
+ fontSize: 'var(--vgai-font-lg)',
1033
+ color: themeVars.content.muted,
972
1034
  },
973
1035
  locationRow: {
974
1036
  display: 'flex',
@@ -980,20 +1042,20 @@ const S = {
980
1042
  marginTop: 4,
981
1043
  },
982
1044
  templateName: {
983
- fontSize: 14,
1045
+ fontSize: 'var(--vgai-font-xl)',
984
1046
  fontWeight: 600,
985
- color: '#e0e0e0',
1047
+ color: themeVars.content.primary,
986
1048
  marginBottom: 4,
987
1049
  },
988
1050
  templateDesc: {
989
- fontSize: 11,
990
- color: '#666',
1051
+ fontSize: 'var(--vgai-font-base)',
1052
+ color: themeVars.content.dim,
991
1053
  lineHeight: 1.4,
992
1054
  },
993
1055
  previewPath: {
994
- fontSize: 12,
995
- color: '#555',
996
- fontFamily: 'monospace',
1056
+ fontSize: 'var(--vgai-font-md)',
1057
+ color: themeVars.content.dim,
1058
+ fontFamily: themeVars.typography.mono,
997
1059
  },
998
1060
  formActions: {
999
1061
  display: 'flex',