@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,67 +1,72 @@
1
1
  import type React from 'react';
2
+ import { space, themeVars } from '../../theme';
3
+ import { panelHeaderStyle } from './panel-header-styles';
2
4
 
3
5
  interface PanelOwnProps {
4
6
  name: React.ReactNode;
5
7
  actions?: React.ReactNode;
6
8
  onHeaderClick?: () => void;
9
+ /** The dock host already names permanent panels in its tab strip. Hide the
10
+ * legacy embedded header there to avoid two consecutive titles for one
11
+ * surface while retaining Panel's content/layout behavior. */
12
+ hideHeader?: boolean;
7
13
  }
8
14
 
9
15
  type PanelProps = PanelOwnProps & Omit<React.HTMLAttributes<HTMLDivElement>, keyof PanelOwnProps>;
10
16
 
11
- export function Panel({ name, actions, onHeaderClick, style, children, ...rest }: PanelProps) {
17
+ export function Panel({
18
+ name,
19
+ actions,
20
+ onHeaderClick,
21
+ hideHeader = false,
22
+ style,
23
+ children,
24
+ ...rest
25
+ }: PanelProps) {
12
26
  return (
13
27
  <div
14
28
  {...rest}
15
29
  style={{
16
- background: '#242424',
30
+ background: themeVars.surface.panel,
17
31
  display: 'flex',
18
32
  flexDirection: 'column',
19
33
  pointerEvents: 'auto',
20
34
  ...style,
21
35
  }}
22
36
  >
23
- {/* Header */}
24
- <div
25
- role={onHeaderClick ? 'button' : undefined}
26
- tabIndex={onHeaderClick ? 0 : undefined}
27
- onClick={onHeaderClick}
28
- onKeyDown={
29
- onHeaderClick
30
- ? (e) => {
31
- if (e.key === 'Enter') onHeaderClick();
32
- }
33
- : undefined
34
- }
35
- style={{
36
- padding: '4px 8px',
37
- background: '#2c2c2c',
38
- borderBottom: '1px solid #333',
39
- fontWeight: 600,
40
- fontSize: 11,
41
- textTransform: 'uppercase',
42
- color: '#8c8c8c',
43
- display: 'flex',
44
- alignItems: 'center',
45
- flexShrink: 0,
46
- cursor: onHeaderClick ? 'pointer' : undefined,
47
- userSelect: 'none',
48
- pointerEvents: 'auto',
49
- }}
50
- >
51
- <span style={{ flexShrink: 0 }}>{name}</span>
52
- {actions && (
53
- <div
54
- style={{
55
- marginLeft: 'auto',
56
- display: 'flex',
57
- alignItems: 'center',
58
- gap: 6,
59
- }}
60
- >
61
- {actions}
62
- </div>
63
- )}
64
- </div>
37
+ {!hideHeader && (
38
+ <div
39
+ role={onHeaderClick ? 'button' : undefined}
40
+ tabIndex={onHeaderClick ? 0 : undefined}
41
+ onClick={onHeaderClick}
42
+ onKeyDown={
43
+ onHeaderClick
44
+ ? (e) => {
45
+ if (e.key === 'Enter') onHeaderClick();
46
+ }
47
+ : undefined
48
+ }
49
+ style={{
50
+ ...panelHeaderStyle,
51
+ cursor: onHeaderClick ? 'pointer' : undefined,
52
+ pointerEvents: 'auto',
53
+ }}
54
+ >
55
+ <span style={{ flexShrink: 0 }}>{name}</span>
56
+ {actions && (
57
+ <div
58
+ style={{
59
+ marginLeft: 'auto',
60
+ display: 'flex',
61
+ alignItems: 'center',
62
+ gap: space[3],
63
+ }}
64
+ >
65
+ {actions}
66
+ </div>
67
+ )}
68
+ </div>
69
+ )}
65
70
  {children}
66
71
  </div>
67
72
  );
@@ -1,6 +1,5 @@
1
- import { faChevronDown, faChevronRight } from '@fortawesome/free-solid-svg-icons';
2
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
3
1
  import { useState } from 'react';
2
+ import { DisclosureIcon } from './EditorIcon';
4
3
 
5
4
  interface SectionHeaderProps {
6
5
  label: string;
@@ -12,34 +11,17 @@ export function SectionHeader({ label, defaultOpen = true, children }: SectionHe
12
11
  const [open, setOpen] = useState(defaultOpen);
13
12
 
14
13
  return (
15
- <div style={{ borderBottom: '1px solid #333' }}>
16
- <div
17
- role="button"
18
- tabIndex={0}
14
+ <section className="vgai-section">
15
+ <button
16
+ type="button"
17
+ className="vgai-section-header"
18
+ aria-expanded={open}
19
19
  onClick={() => setOpen(!open)}
20
- onKeyDown={(e) => {
21
- if (e.key === 'Enter' || e.key === ' ') {
22
- e.preventDefault();
23
- setOpen(!open);
24
- }
25
- }}
26
- style={{
27
- padding: '6px 8px',
28
- cursor: 'pointer',
29
- fontWeight: 600,
30
- fontSize: 11,
31
- textTransform: 'uppercase',
32
- color: '#8c8c8c',
33
- display: 'flex',
34
- alignItems: 'center',
35
- gap: 4,
36
- userSelect: 'none',
37
- }}
38
20
  >
39
- <FontAwesomeIcon icon={open ? faChevronDown : faChevronRight} style={{ fontSize: 8 }} />
21
+ <DisclosureIcon direction={open ? 'down' : 'right'} />
40
22
  {label}
41
- </div>
42
- {open && <div style={{ padding: '0 8px 8px' }}>{children}</div>}
43
- </div>
23
+ </button>
24
+ {open && <div className="vgai-section-body">{children}</div>}
25
+ </section>
44
26
  );
45
27
  }
@@ -0,0 +1,46 @@
1
+ import { type ElementType, forwardRef, type HTMLAttributes } from 'react';
2
+
3
+ export type TextVariant = 'caption' | 'body' | 'label' | 'title' | 'heading' | 'display' | 'code';
4
+ export type TextTone = 'primary' | 'muted' | 'dim' | 'accent' | 'danger' | 'warning' | 'success';
5
+
6
+ export interface TextProps extends HTMLAttributes<HTMLElement> {
7
+ as?: ElementType;
8
+ variant?: TextVariant;
9
+ tone?: TextTone;
10
+ truncate?: boolean;
11
+ selectable?: boolean;
12
+ }
13
+
14
+ function classes(base: string, className?: string): string {
15
+ return className ? `${base} ${className}` : base;
16
+ }
17
+
18
+ /** Semantic editor typography. Product surfaces choose a role, never a px size. */
19
+ export const Text = forwardRef<HTMLElement, TextProps>(function Text(
20
+ {
21
+ as: Component = 'span',
22
+ variant = 'body',
23
+ tone = 'primary',
24
+ truncate = false,
25
+ selectable = false,
26
+ className,
27
+ ...props
28
+ },
29
+ ref,
30
+ ) {
31
+ return (
32
+ <Component
33
+ {...props}
34
+ ref={ref}
35
+ data-variant={variant}
36
+ data-tone={tone}
37
+ data-truncate={truncate || undefined}
38
+ data-selectable={selectable || undefined}
39
+ className={classes('vgai-text', className)}
40
+ />
41
+ );
42
+ });
43
+
44
+ export function Keycap({ className, ...props }: HTMLAttributes<HTMLElement>) {
45
+ return <kbd {...props} className={classes('vgai-keycap', className)} />;
46
+ }
@@ -1,4 +1,5 @@
1
1
  import { useRef, useState } from 'react';
2
+ import { fontSize, themeVars, zIndex } from '../../theme';
2
3
 
3
4
  interface TooltipProps {
4
5
  text: string;
@@ -46,30 +47,37 @@ export function Tooltip({
46
47
  style={{
47
48
  position: 'absolute',
48
49
  ...positionStyles[position],
49
- background: '#1e1e1e',
50
- border: '1px solid #555',
51
- borderRadius: 4,
50
+ background: themeVars.surface.inset,
51
+ // Reconciled to border-2/shadow-sm per EDITOR-STYLE-AUDIT.md §3
52
+ // (was its own one-off `#555`, drifted from the app-wide
53
+ // floating-surface border convention) — U1 reviewer nit:
54
+ // border-2 (#444) to match the menus/popovers chrome contract
55
+ // (`#444` is what every dropdown/context-menu/popover in this
56
+ // rail borders itself with), not border-1 (#333, a plain-panel
57
+ // border, distinct semantic).
58
+ border: `1px solid ${themeVars.boundary.strong}`,
59
+ borderRadius: themeVars.shape.medium,
52
60
  padding: '4px 8px',
53
- zIndex: 10000,
61
+ zIndex: zIndex.modal,
54
62
  pointerEvents: 'none',
55
63
  whiteSpace: 'nowrap',
56
64
  display: 'flex',
57
65
  alignItems: 'center',
58
66
  gap: 8,
59
- boxShadow: '0 2px 8px rgba(0,0,0,0.6)',
67
+ boxShadow: themeVars.elevation.small,
60
68
  }}
61
69
  >
62
- <span style={{ fontSize: 11, color: '#c5c8ce' }}>{text}</span>
70
+ <span style={{ fontSize: fontSize.base, color: themeVars.content.primary }}>{text}</span>
63
71
  {hotkey && (
64
72
  <kbd
65
73
  style={{
66
- background: '#333',
67
- border: '1px solid #555',
68
- borderRadius: 3,
74
+ background: themeVars.surface.raised,
75
+ border: `1px solid ${themeVars.boundary.default}`,
76
+ borderRadius: themeVars.shape.small,
69
77
  padding: '1px 5px',
70
- fontFamily: 'monospace',
71
- fontSize: 10,
72
- color: '#ccc',
78
+ fontFamily: themeVars.typography.mono,
79
+ fontSize: fontSize.sm,
80
+ color: themeVars.content.primary,
73
81
  }}
74
82
  >
75
83
  {hotkey}
@@ -28,6 +28,9 @@ export function Vec3Input({
28
28
  }
29
29
  };
30
30
 
31
+ // Axis tint literals are deliberate (not tokenized): they mirror the
32
+ // viewport gizmo's R/G/B axis colors, a cross-tool convention (Unity/
33
+ // Blender) distinct from the semantic danger/success/accent tones.
31
34
  return (
32
35
  <div style={{ display: 'flex', gap: 4 }}>
33
36
  <NumberInput
@@ -0,0 +1,33 @@
1
+ import { themeVars } from '../../theme';
2
+
3
+ /**
4
+ * Shared semantic-tone triples for dismissible chrome banners
5
+ * (EDITOR-STYLE-AUDIT.md V-16): `MountFailureBanner` (error) and
6
+ * `OverlayReportBanner` (warning) each hand-rolled an identical-shaped
7
+ * `{bg, border, fg}` triple as its own inline magic values — built from the
8
+ * same lightness formula, but two independent literals with no shared
9
+ * constant. Hoisted here so a future retouch of one can't silently drift
10
+ * from the other (the same class of bug V-4 already demonstrated happened
11
+ * to `PlayBar`'s button styles).
12
+ *
13
+ * These are tinted-panel triples, not `theme.ts`'s flat `danger`/`warn`
14
+ * scalars (`theme.ts` has no "dark tinted banner background" slot) — kept
15
+ * as their own small module rather than added to the shared token file.
16
+ */
17
+
18
+ export interface BannerTone {
19
+ bg: string;
20
+ border: string;
21
+ fg: string;
22
+ }
23
+
24
+ export const TONE_ERROR: BannerTone = {
25
+ bg: themeVars.semantic.dangerFaint,
26
+ border: themeVars.semantic.danger,
27
+ fg: themeVars.semantic.danger,
28
+ };
29
+ export const TONE_WARNING: BannerTone = {
30
+ bg: themeVars.semantic.warningMuted,
31
+ border: themeVars.semantic.warning,
32
+ fg: themeVars.semantic.warning,
33
+ };
@@ -0,0 +1,102 @@
1
+ import {
2
+ faArrowPointer,
3
+ faArrowsRotate,
4
+ faBookOpen,
5
+ faBorderAll,
6
+ faCamera,
7
+ faChartSimple,
8
+ faCheck,
9
+ faCircleExclamation,
10
+ faCircleInfo,
11
+ faCode,
12
+ faCrosshairs,
13
+ faCube,
14
+ faDiagramProject,
15
+ faEye,
16
+ faEyeSlash,
17
+ faFile,
18
+ faFileCode,
19
+ faFolder,
20
+ faFolderOpen,
21
+ faForwardStep,
22
+ faHammer,
23
+ faImage,
24
+ faLightbulb,
25
+ faLock,
26
+ faLockOpen,
27
+ faMagnet,
28
+ faMagnifyingGlass,
29
+ faMusic,
30
+ faPause,
31
+ faPlay,
32
+ faPlus,
33
+ faPuzzlePiece,
34
+ faRotate,
35
+ faRotateLeft,
36
+ faRotateRight,
37
+ faStop,
38
+ faTable,
39
+ faTrash,
40
+ faTriangleExclamation,
41
+ faUpDownLeftRight,
42
+ faXmark,
43
+ } from '@fortawesome/free-solid-svg-icons';
44
+
45
+ /**
46
+ * Semantic icon registry for editor chrome. Components may still import a
47
+ * genuinely domain-specific Font Awesome definition, but recurring actions,
48
+ * content kinds, status, and viewport tools use these stable meanings.
49
+ */
50
+ export const editorIcons = {
51
+ action: {
52
+ add: faPlus,
53
+ close: faXmark,
54
+ delete: faTrash,
55
+ search: faMagnifyingGlass,
56
+ refresh: faArrowsRotate,
57
+ undo: faRotateLeft,
58
+ redo: faRotateRight,
59
+ build: faHammer,
60
+ select: faArrowPointer,
61
+ },
62
+ transport: {
63
+ play: faPlay,
64
+ pause: faPause,
65
+ stop: faStop,
66
+ step: faForwardStep,
67
+ },
68
+ content: {
69
+ project: faFolderOpen,
70
+ folder: faFolder,
71
+ file: faFile,
72
+ source: faFileCode,
73
+ scene: faBookOpen,
74
+ entity: faCube,
75
+ component: faPuzzlePiece,
76
+ camera: faCamera,
77
+ light: faLightbulb,
78
+ image: faImage,
79
+ audio: faMusic,
80
+ data: faTable,
81
+ code: faCode,
82
+ stateMachine: faDiagramProject,
83
+ },
84
+ status: {
85
+ success: faCheck,
86
+ info: faCircleInfo,
87
+ warning: faTriangleExclamation,
88
+ error: faCircleExclamation,
89
+ visible: faEye,
90
+ hidden: faEyeSlash,
91
+ locked: faLock,
92
+ unlocked: faLockOpen,
93
+ },
94
+ tool: {
95
+ move: faUpDownLeftRight,
96
+ rotate: faRotate,
97
+ snap: faMagnet,
98
+ frame: faCrosshairs,
99
+ grid: faBorderAll,
100
+ profiler: faChartSimple,
101
+ },
102
+ } as const;
@@ -0,0 +1,30 @@
1
+ import type React from 'react';
2
+ import { chromeSize, fontSize, fontWeight, space, themeVars } from '../../theme';
3
+
4
+ /** Shared typography and metrics for panel titles and disclosure sections. */
5
+ export const panelHeaderLabelStyle: React.CSSProperties = {
6
+ minHeight: chromeSize.panelHeader,
7
+ padding: `0 ${space[4]}px`,
8
+ color: themeVars.content.muted,
9
+ fontSize: fontSize.base,
10
+ fontWeight: fontWeight.semibold,
11
+ textTransform: 'uppercase',
12
+ display: 'flex',
13
+ alignItems: 'center',
14
+ gap: space[2],
15
+ userSelect: 'none',
16
+ };
17
+
18
+ /** Disclosure rows are one density step below full panel headers. */
19
+ export const sectionHeaderLabelStyle: React.CSSProperties = {
20
+ ...panelHeaderLabelStyle,
21
+ minHeight: chromeSize.localToolbar,
22
+ };
23
+
24
+ /** Complete header rail treatment for a standalone panel. */
25
+ export const panelHeaderStyle: React.CSSProperties = {
26
+ ...panelHeaderLabelStyle,
27
+ background: themeVars.surface.chrome,
28
+ borderBottom: `1px solid ${themeVars.boundary.default}`,
29
+ flexShrink: 0,
30
+ };
@@ -0,0 +1,226 @@
1
+ import { useMemo, useState, useSyncExternalStore } from 'react';
2
+ import type { EditorAction } from '../action-registry';
3
+ import { Button, bg, border, danger, fontSize, radius, text } from '../design-system';
4
+ import {
5
+ getProjectOperations,
6
+ projectOperationsVersion,
7
+ runProjectOperation,
8
+ subscribeProjectOperations,
9
+ } from '../project-operations';
10
+ import {
11
+ openWorkspaceDocument,
12
+ type WorkspaceDocumentContentProps,
13
+ } from '../workspace-document-registry';
14
+
15
+ const CATALOG_DOCUMENT_ID = 'project-operations';
16
+ const OPERATION_DOCUMENT_PREFIX = 'project-operation:';
17
+
18
+ export interface ProjectOperationDocumentStore {
19
+ setActiveViewportTab(tab: 'scene' | 'game'): void;
20
+ }
21
+
22
+ function operationDocumentId(name: string): string {
23
+ return `${OPERATION_DOCUMENT_PREFIX}${name}`;
24
+ }
25
+
26
+ export function openProjectOperationsDocument(store: ProjectOperationDocumentStore): void {
27
+ openWorkspaceDocument({
28
+ id: CATALOG_DOCUMENT_ID,
29
+ title: 'Project Commands',
30
+ kind: 'operation',
31
+ Content: ProjectOperationDocumentContent,
32
+ closeable: true,
33
+ onActivate: () => store.setActiveViewportTab('scene'),
34
+ });
35
+ }
36
+
37
+ export function openProjectOperationDocument(
38
+ store: ProjectOperationDocumentStore | null,
39
+ name: string,
40
+ ): boolean {
41
+ const operation = getProjectOperations().operations.find((entry) => entry.name === name);
42
+ if (!operation) return false;
43
+ openWorkspaceDocument({
44
+ id: operationDocumentId(name),
45
+ title: operation.summary || operation.name,
46
+ kind: 'operation',
47
+ provenance: { sourcePath: operation.sourcePath, origin: operation.name },
48
+ Content: ProjectOperationDocumentContent,
49
+ closeable: true,
50
+ ...(store ? { onActivate: () => store.setActiveViewportTab('scene') } : {}),
51
+ });
52
+ return true;
53
+ }
54
+
55
+ export function buildProjectOperationActions(store: ProjectOperationDocumentStore): EditorAction[] {
56
+ return [
57
+ {
58
+ id: 'project-operations:open',
59
+ label: 'Open Project Commands',
60
+ category: 'action',
61
+ execute: () => openProjectOperationsDocument(store),
62
+ },
63
+ ...getProjectOperations().operations.map((operation) => ({
64
+ id: `project-operation:${operation.name}`,
65
+ label: `Run: ${operation.summary || operation.name}`,
66
+ category: 'action' as const,
67
+ execute: () => void openProjectOperationDocument(store, operation.name),
68
+ })),
69
+ ];
70
+ }
71
+
72
+ function JsonBlock({ value }: { value: unknown }) {
73
+ return <pre style={jsonStyle}>{JSON.stringify(value, null, 2)}</pre>;
74
+ }
75
+
76
+ export function ProjectOperationDocumentContent({ documentId }: WorkspaceDocumentContentProps) {
77
+ useSyncExternalStore(subscribeProjectOperations, projectOperationsVersion);
78
+ const snapshot = getProjectOperations();
79
+ const operationName = documentId.startsWith(OPERATION_DOCUMENT_PREFIX)
80
+ ? documentId.slice(OPERATION_DOCUMENT_PREFIX.length)
81
+ : null;
82
+ const operation = operationName
83
+ ? snapshot.operations.find((entry) => entry.name === operationName)
84
+ : null;
85
+ const [inputText, setInputText] = useState('{}');
86
+ const [result, setResult] = useState<unknown>(null);
87
+ const [running, setRunning] = useState(false);
88
+ const [inputError, setInputError] = useState<string | null>(null);
89
+ const parsedSchema = useMemo(() => operation?.inputSchema ?? {}, [operation]);
90
+
91
+ if (!operationName) {
92
+ return (
93
+ <div style={rootStyle} data-testid="project-operations-catalog">
94
+ <h2 style={headingStyle}>Project Commands</h2>
95
+ <p style={mutedStyle}>
96
+ Auto-discovered from nested <code>src/operations/*.operation.ts</code> files. Commands are
97
+ ordinary project functions with a validated tooling boundary; custom interfaces live in{' '}
98
+ <code>src/tools/</code>.
99
+ </p>
100
+ {snapshot.operations.map((entry) => (
101
+ <Button
102
+ key={entry.name}
103
+ type="button"
104
+ className="vgai-operation-card"
105
+ onClick={() => openProjectOperationDocument(null, entry.name)}
106
+ data-testid={`project-command-card:${entry.name}`}
107
+ >
108
+ <div style={{ color: text[1] }}>{entry.summary}</div>
109
+ <code style={mutedStyle}>{entry.name}</code>
110
+ <div style={mutedStyle}>
111
+ {entry.host} · {entry.permission.risk} · {entry.sourcePath}
112
+ </div>
113
+ </Button>
114
+ ))}
115
+ {snapshot.operations.length === 0 && snapshot.loadErrors.length === 0 && (
116
+ <div style={mutedStyle}>No project commands found.</div>
117
+ )}
118
+ {snapshot.loadErrors.map((error) => (
119
+ <div key={`${error.sourcePath}:${error.message}`} style={errorStyle}>
120
+ {error.sourcePath}: {error.message}
121
+ </div>
122
+ ))}
123
+ </div>
124
+ );
125
+ }
126
+
127
+ if (!operation) {
128
+ return <div style={rootStyle}>This operation is no longer available.</div>;
129
+ }
130
+
131
+ const runnable = operation.host === 'node';
132
+ const execute = async () => {
133
+ let input: unknown;
134
+ try {
135
+ input = JSON.parse(inputText);
136
+ setInputError(null);
137
+ } catch (error) {
138
+ setInputError(error instanceof Error ? error.message : String(error));
139
+ return;
140
+ }
141
+ const confirm =
142
+ operation.permission.risk === 'read' ||
143
+ window.confirm(`${operation.permission.summary}\n\nRun ${operation.name}?`);
144
+ if (!confirm) return;
145
+ setRunning(true);
146
+ try {
147
+ setResult(await runProjectOperation(operation.name, input, { confirm: true }));
148
+ } catch (error) {
149
+ setResult({ ok: false, error: { code: 'REQUEST_FAILED', message: String(error) } });
150
+ } finally {
151
+ setRunning(false);
152
+ }
153
+ };
154
+
155
+ return (
156
+ <div style={rootStyle} data-testid={`project-operation:${operation.name}`}>
157
+ <h2 style={headingStyle}>{operation.summary}</h2>
158
+ <code style={{ color: text[2] }}>{operation.name}</code>
159
+ <p style={{ color: text[2] }}>{operation.description}</p>
160
+ <div style={mutedStyle}>
161
+ {operation.host} · {operation.permission.risk} · {operation.sourcePath}
162
+ </div>
163
+ {!runnable && (
164
+ <div style={errorStyle}>
165
+ This command runner supports Node-hosted operations. Import the underlying function
166
+ directly in browser or runtime code.
167
+ </div>
168
+ )}
169
+ <label style={labelStyle} htmlFor={`operation-input:${operation.name}`}>
170
+ Input JSON
171
+ </label>
172
+ <textarea
173
+ id={`operation-input:${operation.name}`}
174
+ value={inputText}
175
+ onChange={(event) => setInputText(event.target.value)}
176
+ rows={10}
177
+ style={textareaStyle}
178
+ />
179
+ {inputError && <div style={errorStyle}>{inputError}</div>}
180
+ <Button type="button" disabled={!runnable || running} onClick={() => void execute()}>
181
+ {running ? 'Running…' : 'Run command'}
182
+ </Button>
183
+ <details>
184
+ <summary style={labelStyle}>Input schema</summary>
185
+ <JsonBlock value={parsedSchema} />
186
+ </details>
187
+ {result !== null && (
188
+ <section>
189
+ <div style={labelStyle}>Result</div>
190
+ <JsonBlock value={result} />
191
+ </section>
192
+ )}
193
+ </div>
194
+ );
195
+ }
196
+
197
+ const rootStyle: React.CSSProperties = {
198
+ padding: 16,
199
+ overflow: 'auto',
200
+ pointerEvents: 'auto',
201
+ color: text[1],
202
+ fontSize: fontSize.base,
203
+ };
204
+ const headingStyle: React.CSSProperties = { margin: '0 0 8px', fontSize: 18 };
205
+ const mutedStyle: React.CSSProperties = { color: text[2], fontSize: fontSize.sm };
206
+ const labelStyle: React.CSSProperties = { display: 'block', margin: '14px 0 6px', color: text[1] };
207
+ const errorStyle: React.CSSProperties = { marginTop: 10, color: danger, whiteSpace: 'pre-wrap' };
208
+ const textareaStyle: React.CSSProperties = {
209
+ width: '100%',
210
+ boxSizing: 'border-box',
211
+ padding: 10,
212
+ resize: 'vertical',
213
+ background: bg.inset,
214
+ color: text[1],
215
+ border: `1px solid ${border[2]}`,
216
+ borderRadius: radius.sm,
217
+ fontFamily: 'monospace',
218
+ };
219
+ const jsonStyle: React.CSSProperties = {
220
+ overflow: 'auto',
221
+ padding: 10,
222
+ background: bg.inset,
223
+ border: `1px solid ${border[1]}`,
224
+ borderRadius: radius.sm,
225
+ color: text[2],
226
+ };