@vgai/editor 0.2.0 → 0.4.0-canary.20260715.1

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-CyiTVRi6.js +30 -0
  3. package/dist/assets/browserAll-fGZ_P1P8.js +1 -0
  4. package/dist/assets/create-runtime-BzSHBTYw.js +1 -0
  5. package/dist/assets/game-DQ7YHNS8.js +1 -0
  6. package/dist/assets/game-DX6Obomf.js +1 -0
  7. package/dist/assets/game-DoUJj8vY.js +1 -0
  8. package/dist/assets/{game-xsOho_wg.js → game-HqybI_X7.js} +3 -3
  9. package/dist/assets/game-NUAnyPkS.js +1 -0
  10. package/dist/assets/game-ck_dNIE6.js +1 -0
  11. package/dist/assets/{game-pt_1lQLv.js → game-hIf5LD4E.js} +1 -1
  12. package/dist/assets/game-loop-o2CcBMFY.js +1 -0
  13. package/dist/assets/{game-source-CvidN7AB.js → game-source-C8HM7gbP.js} +1 -1
  14. package/dist/assets/{game-BI111o5f.js → game-uR_lv3Mp.js} +3 -3
  15. package/dist/assets/index-B-HA1uu3.js +15412 -0
  16. package/dist/assets/{index-Bp5cecD6.js → index-CSBAZwMx.js} +2 -2
  17. package/dist/assets/{index-DL7RcQ0n.js → index-CoSRCKPS.js} +1 -1
  18. package/dist/assets/{index-Cs8xwv3e.js → index-D93bBuUl.js} +1 -1
  19. package/dist/assets/index-bu01fqch.css +1 -0
  20. package/dist/assets/ingest-siblings-CCkkE2h7.js +1 -0
  21. package/dist/assets/mount-manifest-B2ECssU2.js +1 -0
  22. package/dist/assets/react-18-Bmj05S8J.js +1 -0
  23. package/dist/assets/webworkerAll-BC6gMOLZ.js +1 -0
  24. package/dist/assets/world-documents-DXPcAEA0.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 +7718 -3436
  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 +71 -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 +1236 -296
  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 +1116 -344
  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 +620 -0
  168. package/src/components/build-documents.tsx +696 -0
  169. package/src/components/core-utilities.tsx +75 -0
  170. package/src/components/data-documents.tsx +364 -0
  171. package/src/components/{DataPanel.tsx → data-editor.tsx} +138 -288
  172. package/src/components/debugger-panel-state.ts +57 -0
  173. package/src/components/inspector-widgets/AlignmentGrid.tsx +8 -14
  174. package/src/components/inspector-widgets/BorderEditor.tsx +41 -33
  175. package/src/components/inspector-widgets/ColorPicker.tsx +39 -39
  176. package/src/components/inspector-widgets/FontPicker.tsx +13 -21
  177. package/src/components/inspector-widgets/GradientEditor.tsx +11 -19
  178. package/src/components/inspector-widgets/ScrubbableInput.tsx +6 -3
  179. package/src/components/inspector-widgets/ShadowEditor.tsx +20 -33
  180. package/src/components/inspector-widgets/shared.tsx +90 -49
  181. package/src/components/inspectors/AnimationSection.tsx +39 -40
  182. package/src/components/inspectors/AudioSection.tsx +28 -39
  183. package/src/components/inspectors/BoneAttachmentSection.tsx +19 -24
  184. package/src/components/inspectors/CameraSection.tsx +14 -23
  185. package/src/components/inspectors/ComponentsSection.tsx +39 -43
  186. package/src/components/inspectors/JointsSection.tsx +34 -43
  187. package/src/components/inspectors/LightSection.tsx +90 -35
  188. package/src/components/inspectors/MaterialSection.tsx +137 -174
  189. package/src/components/inspectors/MeshSection.tsx +9 -17
  190. package/src/components/inspectors/NavigationSection.tsx +8 -15
  191. package/src/components/inspectors/ParticleSection.tsx +91 -76
  192. package/src/components/inspectors/PhysicsSection.tsx +42 -46
  193. package/src/components/inspectors/PostProcessingSection.tsx +28 -36
  194. package/src/components/inspectors/RenderingSection.tsx +12 -20
  195. package/src/components/inspectors/ShadowSection.tsx +7 -8
  196. package/src/components/inspectors/SplineSection.tsx +31 -42
  197. package/src/components/inspectors/TransformSection.tsx +14 -22
  198. package/src/components/patterns/Dialog.tsx +95 -0
  199. package/src/components/patterns/Fields.tsx +44 -0
  200. package/src/components/patterns/List.tsx +25 -0
  201. package/src/components/patterns/StateSurface.tsx +40 -0
  202. package/src/components/patterns/Surfaces.tsx +115 -0
  203. package/src/components/patterns/Tabs.tsx +80 -0
  204. package/src/components/patterns/Toolbar.tsx +51 -0
  205. package/src/components/patterns/Tree.tsx +72 -0
  206. package/src/components/primitives/Button.tsx +46 -0
  207. package/src/components/primitives/ColorInput.tsx +11 -47
  208. package/src/components/primitives/EditorIcon.tsx +99 -0
  209. package/src/components/primitives/FormControls.tsx +71 -0
  210. package/src/components/primitives/JsonInput.tsx +15 -2
  211. package/src/components/primitives/Layout.tsx +100 -0
  212. package/src/components/primitives/Menu.tsx +105 -0
  213. package/src/components/primitives/NumberInput.tsx +8 -35
  214. package/src/components/primitives/Panel.tsx +49 -44
  215. package/src/components/primitives/SectionHeader.tsx +10 -28
  216. package/src/components/primitives/Text.tsx +46 -0
  217. package/src/components/primitives/Tooltip.tsx +20 -12
  218. package/src/components/primitives/Vec3Input.tsx +3 -0
  219. package/src/components/primitives/banner-tones.ts +33 -0
  220. package/src/components/primitives/editor-icons.ts +102 -0
  221. package/src/components/primitives/panel-header-styles.ts +30 -0
  222. package/src/components/project-operation-documents.tsx +226 -0
  223. package/src/components/status-contributions.tsx +175 -0
  224. package/src/components/story-documents-register.ts +15 -0
  225. package/src/components/story-documents.tsx +1185 -0
  226. package/src/components/tool-documents.tsx +110 -0
  227. package/src/components/workspace-dock.css +293 -0
  228. package/src/components/workspace-history.ts +33 -0
  229. package/src/components/world-documents.tsx +145 -0
  230. package/src/components/xstate-documents.tsx +209 -0
  231. package/src/composite-screenshot.ts +248 -0
  232. package/src/design-system/index.ts +70 -0
  233. package/src/design-system-stories/Foundation.stories.tsx +183 -0
  234. package/src/design-system-stories/Patterns.stories.tsx +251 -0
  235. package/src/design-system-stories/Workspace.stories.tsx +538 -0
  236. package/src/doctor/report.ts +57 -13
  237. package/src/editor-api.ts +295 -31
  238. package/src/editor-compatibility.ts +200 -0
  239. package/src/editor-console.ts +35 -17
  240. package/src/editor-hotkeys.ts +85 -33
  241. package/src/editor-store.ts +538 -233
  242. package/src/editor-viewport.ts +160 -34
  243. package/src/entity-factory.ts +14 -3
  244. package/src/gizmo-registry.ts +21 -5
  245. package/src/harness-chat-editor-context.ts +37 -0
  246. package/src/harness-chat-indicator.ts +68 -0
  247. package/src/harness-chat-layout.ts +53 -0
  248. package/src/harness-chat-lifecycle.ts +170 -0
  249. package/src/harness-chat-reconnect.ts +85 -0
  250. package/src/harness-chat-types.ts +409 -0
  251. package/src/hierarchy-drop.ts +50 -0
  252. package/src/hierarchy-expansion-state.ts +100 -0
  253. package/src/hierarchy-menu-registry.ts +1 -1
  254. package/src/hierarchy-projection.ts +76 -0
  255. package/src/history/editor-session.ts +21 -0
  256. package/src/history/history-commands.ts +147 -0
  257. package/src/history/history-service.ts +904 -0
  258. package/src/history/json-history-resource.ts +165 -0
  259. package/src/history/persistence-coordinator.ts +35 -0
  260. package/src/history/project-file-history.ts +266 -0
  261. package/src/history/project-root-history-backends.ts +53 -0
  262. package/src/history/resource-registry.ts +209 -0
  263. package/src/history/snapshot-store.ts +108 -0
  264. package/src/history/source-history-backend.ts +374 -0
  265. package/src/history/types.ts +100 -0
  266. package/src/hmr-registration-group.ts +67 -0
  267. package/src/hmr-stable-react-context.ts +23 -0
  268. package/src/hosted-example.ts +12 -2
  269. package/src/hotkeys.ts +89 -18
  270. package/src/ingest/discovery-react.ts +9 -10
  271. package/src/ingest/discovery.ts +4 -4
  272. package/src/ingest/discovery2d.ts +5 -5
  273. package/src/ingest/games/bloom-composer/vgai.game.json +3 -3
  274. package/src/ingest/games/embed-bundle/vgai.game.json +3 -3
  275. package/src/ingest/games/games-fps/vgai.game.json +3 -3
  276. package/src/ingest/games/iframe-reachable/vgai.game.json +3 -3
  277. package/src/ingest/games/interactive-cubes/vgai.game.json +3 -3
  278. package/src/ingest/games/mismatched-three/vgai.game.json +3 -3
  279. package/src/ingest/games/own-three-bundled/vgai.game.json +3 -3
  280. package/src/ingest/games/pointerlock-maze/vgai.game.json +7 -4
  281. package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +3 -3
  282. package/src/ingest/games-2d/bunnymark/vgai.game.json +3 -3
  283. package/src/ingest/games-2d/flappy/vgai.game.json +3 -3
  284. package/src/ingest/games-2d/flappy-pixi/vgai.game.json +3 -3
  285. package/src/ingest/games-2d/puzzling-potions/vgai.game.json +3 -3
  286. package/src/ingest/games-2d/tilemap-camera/vgai.game.json +3 -3
  287. package/src/ingest/games-react/react-rpg/vgai.game.json +3 -3
  288. package/src/ingest/types.ts +1 -1
  289. package/src/ingest-mode.ts +47 -52
  290. package/src/ingest-siblings.ts +23 -33
  291. package/src/initial-scene.ts +1 -2
  292. package/src/main.tsx +16 -8
  293. package/src/manifest-project.ts +10 -12
  294. package/src/model-thumbnail.ts +261 -26
  295. package/src/module-mode.ts +44 -50
  296. package/src/play-log-events.ts +26 -0
  297. package/src/play-mode.ts +495 -401
  298. package/src/play-scene-adoption.ts +23 -0
  299. package/src/playground/main.ts +68 -24
  300. package/src/playwright-shim.ts +473 -0
  301. package/src/project-manager.ts +90 -10
  302. package/src/project-operations.ts +112 -0
  303. package/src/project-story-discovery.ts +25 -0
  304. package/src/project-tool-discovery.ts +43 -0
  305. package/src/scene-serializer.ts +0 -6
  306. package/src/scene-sync.ts +7 -2
  307. package/src/script-loader.ts +3 -3
  308. package/src/skeleton-helper.ts +57 -0
  309. package/src/storage/seed.ts +68 -8
  310. package/src/stories/StoryPreviewMount.tsx +265 -0
  311. package/src/stories/action-stories.fixture.tsx +44 -0
  312. package/src/stories/button-stories.fixture.tsx +50 -0
  313. package/src/stories/compose-project-stories.ts +182 -0
  314. package/src/stories/loader-stories.fixture.tsx +53 -0
  315. package/src/stories/story-discovery.ts +130 -0
  316. package/src/stories/story-registry.ts +146 -0
  317. package/src/theme-css.ts +1049 -0
  318. package/src/theme-library.ts +238 -0
  319. package/src/theme-preference.ts +130 -0
  320. package/src/theme.ts +527 -0
  321. package/src/tool-loader.ts +80 -24
  322. package/src/ui-source/inspect.ts +27 -4
  323. package/src/ui-source/source-write-backend.ts +83 -22
  324. package/src/widgets/index.ts +9 -18
  325. package/src/workspace-aux-commands.ts +11 -0
  326. package/src/workspace-dock-controller.ts +64 -0
  327. package/src/workspace-document-ids.ts +5 -0
  328. package/src/workspace-document-registry.ts +367 -0
  329. package/src/workspace-state-persistence.ts +455 -0
  330. package/src/workspace-status-registry.ts +110 -0
  331. package/src/workspace-utility-commands.ts +49 -0
  332. package/src/workspace-utility-registry.ts +161 -0
  333. package/src/world-document-routing.ts +16 -0
  334. package/src/xstate-inspect/XStateMachineInspector.tsx +544 -0
  335. package/src/xstate-inspect/character-animation-machine.fixture.ts +74 -0
  336. package/src/xstate-inspect/use-live-actor-state.ts +39 -0
  337. package/src/xstate-inspect/xstate-graph.ts +265 -0
  338. package/src/xstate-inspect/xstate-layout.ts +76 -0
  339. package/src/z-index.ts +26 -0
  340. package/template/.claude/skills/editor/SKILL.md +129 -90
  341. package/template/AGENTS.md +1110 -0
  342. package/template/CLAUDE.md +1 -512
  343. package/template/IDIOMS.md +50 -0
  344. package/template/check-idioms.ts +706 -0
  345. package/template/manifest-entry-modules-plugin.ts +53 -0
  346. package/template/package.json +13 -4
  347. package/template/public/scenes/default.scn2d.json +6 -0
  348. package/template/public/scenes/default.vscn.json +7 -1
  349. package/template/public/scenes/untitled.vscn.json +10 -0
  350. package/template/scripts/playtest.ts +40 -0
  351. package/template/server/loopback.ts +75 -0
  352. package/template/server/rooms/arena-room.ts +69 -1
  353. package/template/server/rooms/game-room.ts +68 -1
  354. package/template/src/data/README.md +9 -4
  355. package/template/src/data/data-ref.ts +35 -0
  356. package/template/src/data/tuning.schema.ts +1 -1
  357. package/template/src/main.ts +41 -62
  358. package/template/src/manifest-entry-modules.d.ts +3 -0
  359. package/template/src/operations/README.md +88 -0
  360. package/template/src/runtime/pixi-adapter.ts +24 -0
  361. package/template/src/scripts/components/data-spinner.ts +43 -2
  362. package/template/src/scripts/components/spin-lap-math.ts +27 -0
  363. package/template/src/tools/README.md +5 -3
  364. package/template/src/tools/example.tool.tsx +13 -7
  365. package/template/src/ui/Button.stories.tsx +66 -0
  366. package/template/src/ui/RebindPanel.tsx +2 -6
  367. package/template/src/ui/game.tsx +17 -0
  368. package/template/tests/acceptance/example.spec.ts +64 -0
  369. package/template/tests/logic/example.test.ts +31 -0
  370. package/template/tests/logic/room.test.ts +100 -0
  371. package/template/tests/tsconfig.json +30 -0
  372. package/template/validate-manifest.ts +5 -5
  373. package/template/validate-scenes.ts +50 -18
  374. package/template/vgai.game.json +20 -13
  375. package/template/vite.config.ts +77 -16
  376. package/vite-plugin-ui-oid.ts +261 -17
  377. package/dist/assets/CCDIKSolver-Dz-ysxZF.js +0 -1
  378. package/dist/assets/SelectionBox-BXsv_6aH.js +0 -1
  379. package/dist/assets/browser-project-scripts-DOru4L1x.js +0 -2
  380. package/dist/assets/browserAll-CEkWYlFk.js +0 -1
  381. package/dist/assets/create-runtime-DYIWr3va.js +0 -1
  382. package/dist/assets/data-asset-Bb9sIM7i.js +0 -5
  383. package/dist/assets/game-8SDZPPgH.js +0 -1
  384. package/dist/assets/game-CevorxyV.js +0 -1
  385. package/dist/assets/game-Di7XlywD.js +0 -1
  386. package/dist/assets/game-DjnCaw8R.js +0 -1
  387. package/dist/assets/game-Q9A0aXSV.js +0 -1
  388. package/dist/assets/index-CPVV6pjw.js +0 -1
  389. package/dist/assets/index-CpOnYyeD.js +0 -213
  390. package/dist/assets/index-Dr8H_6Rl.js +0 -1
  391. package/dist/assets/index-_bCaipUk.js +0 -13650
  392. package/dist/assets/ingest-siblings-Dz8eEqrV.js +0 -1
  393. package/dist/assets/mount-manifest-Bhk0ohsl.js +0 -1
  394. package/dist/assets/scene-query-BivAHhJu.js +0 -1
  395. package/dist/assets/webworkerAll-BLrH0F6F.js +0 -1
  396. package/dist/assets/xstate-animation-binding-1NGLmw6y.js +0 -1
  397. package/dist/assets/xstate.esm-BgUD33nl.js +0 -3
  398. package/src/authoring/scene-ui-layer.ts +0 -485
  399. package/src/authoring/ui-authoring-adapter.ts +0 -325
  400. package/src/authoring/ui-hierarchy-menu.ts +0 -40
  401. package/src/authoring/ui-inspector-sections.tsx +0 -156
  402. package/src/authoring/undo-timeline.ts +0 -145
  403. package/src/components/BottomPanel.tsx +0 -326
  404. package/src/components/BuildPanel.tsx +0 -455
  405. package/src/components/FolderTree.tsx +0 -179
  406. package/src/components/RightPanel.tsx +0 -166
  407. package/src/components/WorldContextMenu.tsx +0 -257
  408. package/src/components/asset-viewers/AssetViewer.tsx +0 -38
  409. package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +0 -129
  410. package/src/scene-ui-mode/active-scene-ui.ts +0 -58
  411. package/src/scene-ui-mode/scene-ui-palette-panel.tsx +0 -152
  412. package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +0 -29
  413. package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +0 -74
  414. package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +0 -49
  415. package/src/scene-ui-mode/ui-prefab-io.ts +0 -76
  416. package/src/ui-editor/inspector.tsx +0 -563
  417. package/src/ui-editor/node-ops.ts +0 -266
  418. package/src/ui-editor/overlay.tsx +0 -380
  419. package/src/ui-editor/palette.tsx +0 -114
  420. package/src/ui-editor/react-store.ts +0 -78
  421. package/src/ui-editor/timeline-panel.tsx +0 -443
  422. package/src/ui-editor/ui-edit-store.ts +0 -552
  423. package/src/ui-editor/ui-prefab.ts +0 -95
  424. package/template/project.json +0 -9
  425. package/template/src/scripts/main.ts +0 -94
  426. package/template/src/ui/DefaultHud.tsx +0 -40
  427. package/template/src/ui/game-state.tsx +0 -37
  428. package/template/src/ui/mount-default-hud.tsx +0 -19
  429. package/template/src/ui/mount-ui.ts +0 -15
  430. package/vite-plugin-react-world-provider.ts +0 -81
@@ -0,0 +1,455 @@
1
+ /**
2
+ * W5 (docs/EDITOR-WORKSPACE-SHELL-TODO.md §6.2/§9 W5 — inventory rows
3
+ * B17/R8) — PERSISTED WORKSPACE STATE: Dockview's serialized panel tree plus
4
+ * the open authored documents + active document. Restored on boot, written
5
+ * through (debounced) on every change. Dockview owns every physical concern:
6
+ * groups, sizes, tab order, collapsed edge groups, floating panels, and the
7
+ * maximized group. Manual hierarchy openness is NOT here — it persists in its
8
+ * own store (`hierarchy-expansion-state.ts`, §6.1), same key discipline.
9
+ *
10
+ * STORAGE DECISION (recorded): `localStorage`, key
11
+ * `vgai:workspace-state:v8`, holding a map keyed PER PROJECT by
12
+ * `getCurrentProject().rootPath` (the same project identity
13
+ * `hierarchy-expansion-state.ts` established — it prevents one game's layout
14
+ * leaking into another, and `rootPath` is the durable cross-session
15
+ * identity). Why localStorage and NOT a project-scoped metadata file
16
+ * (including the game manifest): layout is per-user/per-machine CONVENIENCE,
17
+ * not project
18
+ * truth — writing it into the project would dirty the user's repo from mere
19
+ * UI fiddling (the harness fence "no persistent scene/data edits" names
20
+ * exactly this hazard), would break read-only hosted `?project=` sessions,
21
+ * and would fight the precedent that hierarchy openness (the other §6.2
22
+ * persisted preference) already lives in localStorage.
23
+ *
24
+ * DOCUMENT RESTORE RULES (§9/W5 "persist+restore only documents that can
25
+ * truthfully reopen — verify on restore, drop silently if gone"):
26
+ * - scene/game/world: NEVER persisted here. Scene and Game are opened
27
+ * unconditionally by `CenterDocuments.syncCenterDocuments`; manifest
28
+ * world documents are rediscovered from the active composite. Their
29
+ * Dockview panels still persist as layout geometry, but none need a
30
+ * synthetic restorable-document record.
31
+ * - asset documents: persisted only for real file-backed specs (a serving
32
+ * path). Verified via `projectFileExists()` (storage-first stat, HTTP HEAD
33
+ * fallback — an existence-only probe; unlike the prefab/scene loaders'
34
+ * `readProjectTextFile`, this never downloads the asset body just to check
35
+ * it is still there). Asset-Lab (`asset-lab:entity:*`) and unresolved
36
+ * online (`online:*`) documents are NOT persisted: a live-entity lab
37
+ * session and a remote detail view are not truthfully re-resolvable from a
38
+ * cold boot.
39
+ * - data documents: verified against `loadedDataFiles()` after an awaited
40
+ * `refreshDataFiles()` — a deleted/renamed `.data.json` is dropped.
41
+ * - tool documents: `openToolDocument` self-verifies against the discovered
42
+ * tool store (awaited `refreshProjectTools()` first) — unknown ids open
43
+ * nothing.
44
+ * - story documents: verified against the story registry after an awaited
45
+ * `refreshProjectStories()` — module must load ok AND still export the
46
+ * story name.
47
+ * - static shell documents (`Data Assets`, `Build Profiles`): always
48
+ * truthfully reopenable (Build Profiles only in server mode). Asset
49
+ * Library is a permanent navigator tab, so Dockview restores its physical
50
+ * placement without fabricating a center document.
51
+ * - live-observation tools are never center documents. A legacy persisted
52
+ * `workspace:profiler` entry migrates into the bottom Debugger; live
53
+ * XState machines cannot restore because reload ended their bindings.
54
+ *
55
+ * Write-through is debounced (300ms trailing) and flushed on `pagehide`, so
56
+ * an immediate reload still captures the last change. Subscriptions install
57
+ * only AFTER the async restore finishes — a half-restored open-set must
58
+ * never overwrite the stored one. That wait is BOUNDED (`RESTORE_TIMEOUT_MS`):
59
+ * a hung verification fetch (e.g. a stalled asset-existence probe) must never
60
+ * block write-through + pagehide-flush installation for the whole session —
61
+ * layout/open-tab state is convenience state, not project truth, so
62
+ * availability beats completeness here. If the restore itself keeps running
63
+ * past the timeout, its later document-opens still land normally; only the
64
+ * subscription install is time-boxed, not the restore itself.
65
+ */
66
+
67
+ import type { DockviewApi, SerializedDockview } from 'dockview-react';
68
+ import { assetDocumentSpec, openAssetDocument } from './components/asset-documents';
69
+ import {
70
+ BUILD_PROFILES_DOCUMENT_ID,
71
+ openBuildProfilesDocument,
72
+ } from './components/build-documents';
73
+ import { SCENE_DOCUMENT_ID } from './components/CenterDocuments';
74
+ import {
75
+ DATA_ASSETS_DOCUMENT_ID,
76
+ loadedDataFiles,
77
+ openDataAssetsDocument,
78
+ openDataDocument,
79
+ refreshDataFiles,
80
+ } from './components/data-documents';
81
+ import { openDebuggerPanel } from './components/debugger-panel-state';
82
+ import {
83
+ openStoryDocsDocument,
84
+ openStoryDocument,
85
+ storyDocumentState,
86
+ } from './components/story-documents';
87
+ import { openToolDocument } from './components/tool-documents';
88
+ import { projectFileExists } from './editor-api';
89
+ import { getEditorMode } from './editor-mode';
90
+ import type { AssetKind } from './editor-store';
91
+ import {
92
+ harnessChat,
93
+ hydrateHarnessChatLayout,
94
+ subscribeHarnessChatLayout,
95
+ } from './harness-chat-layout';
96
+ import { getCurrentProject } from './project-manager';
97
+ import { getProjectStoryModules, refreshProjectStories } from './stories/story-registry';
98
+ import { refreshProjectTools } from './tool-loader';
99
+ import {
100
+ activateWorkspaceDocument,
101
+ activeWorkspaceDocumentId,
102
+ openWorkspaceDocuments,
103
+ subscribeWorkspaceDocuments,
104
+ } from './workspace-document-registry';
105
+
106
+ // v6 flattens the short-lived nested Project > Assets > source navigation.
107
+ // Local Assets and the online Library are peer Dockview tabs in the visible
108
+ // group below Hierarchy; Data and Stories keep their contextual/document
109
+ // entry points instead of masquerading as permanent navigation locations.
110
+ // v7 resets v6's short-lived four-column layout: Agent moved from the left to
111
+ // the right and the role-specific default widths replaced equalized columns.
112
+ // v8 introduces one isolated center document per manifest world plus the
113
+ // world-surface/resource tab-family seam. A v7 layout cannot name those
114
+ // panels and can restore center documents into a navigator group, so reset
115
+ // that physical layout once instead of guessing at user intent.
116
+ const STORAGE_KEY = 'vgai:workspace-state:v8';
117
+ const WRITE_DEBOUNCE_MS = 300;
118
+ /** Bound on the document-restore verification before write-through installs
119
+ * anyway (module header's "wait is BOUNDED" rule). */
120
+ const RESTORE_TIMEOUT_MS = 10_000;
121
+ const LEGACY_PROFILER_DOCUMENT_ID = 'workspace:profiler';
122
+
123
+ /** One persisted, restorable document (see the module header's rules). */
124
+ export type PersistedDocument =
125
+ | { readonly t: 'asset'; readonly id: string; readonly path: string; readonly assetKind: string }
126
+ | { readonly t: 'data'; readonly id: string; readonly path: string }
127
+ | { readonly t: 'tool'; readonly id: string; readonly toolId: string }
128
+ | {
129
+ readonly t: 'story';
130
+ readonly id: string;
131
+ readonly modulePath: string;
132
+ readonly storyName: string;
133
+ readonly view?: 'preview' | 'docs';
134
+ }
135
+ | { readonly t: 'static'; readonly id: string };
136
+
137
+ export interface PersistedWorkspaceState {
138
+ /** Dockview owns Agent geometry; this flag records whether the optional panel exists. */
139
+ readonly chat?: { collapsed: boolean };
140
+ readonly layout?: SerializedDockview;
141
+ readonly documents?: { open: PersistedDocument[]; activeId: string | null };
142
+ }
143
+
144
+ /** The narrow store surface every document open function needs (they all
145
+ * declare the same T6.3 hand-off contract). */
146
+ export interface WorkspaceStateStore {
147
+ setActiveViewportTab(tab: 'scene' | 'game'): void;
148
+ }
149
+
150
+ function browserStorage(): Storage | null {
151
+ try {
152
+ return typeof window === 'undefined' ? null : window.localStorage;
153
+ } catch {
154
+ return null;
155
+ }
156
+ }
157
+
158
+ function projectIdentity(): string {
159
+ return getCurrentProject()?.rootPath ?? '__unscoped__';
160
+ }
161
+
162
+ /** Read one project's persisted state (exported for tests). */
163
+ export function readPersistedWorkspaceState(projectId: string): PersistedWorkspaceState | null {
164
+ const storage = browserStorage();
165
+ if (!storage) return null;
166
+ try {
167
+ const all = JSON.parse(storage.getItem(STORAGE_KEY) ?? '{}') as Record<
168
+ string,
169
+ PersistedWorkspaceState
170
+ >;
171
+ return all[projectId] ?? null;
172
+ } catch {
173
+ // A corrupt preference must never prevent the editor from opening —
174
+ // same failure physics as hierarchy-expansion-state.ts.
175
+ return null;
176
+ }
177
+ }
178
+
179
+ function writePersistedWorkspaceState(projectId: string, state: PersistedWorkspaceState): void {
180
+ const storage = browserStorage();
181
+ if (!storage) return;
182
+ try {
183
+ const all = JSON.parse(storage.getItem(STORAGE_KEY) ?? '{}') as Record<
184
+ string,
185
+ PersistedWorkspaceState
186
+ >;
187
+ all[projectId] = state;
188
+ storage.setItem(STORAGE_KEY, JSON.stringify(all));
189
+ } catch {
190
+ // Layout is convenience state; storage denial/quota is non-fatal.
191
+ }
192
+ }
193
+
194
+ /**
195
+ * Serialize the restorable half of one open document, or `null` for the
196
+ * kinds that must not persist (scene/game/xstate/asset-lab/online — module
197
+ * header). Exported for tests.
198
+ */
199
+ export function persistableDocument(doc: {
200
+ descriptor: { id: string; kind: string };
201
+ }): PersistedDocument | null {
202
+ const { id, kind } = doc.descriptor;
203
+ switch (kind) {
204
+ case 'asset': {
205
+ const spec = assetDocumentSpec(id);
206
+ if (!spec || !spec.assetPath || spec.online || spec.entityId !== undefined) return null;
207
+ return { t: 'asset', id, path: spec.assetPath, assetKind: spec.kind };
208
+ }
209
+ case 'data':
210
+ return id === DATA_ASSETS_DOCUMENT_ID
211
+ ? { t: 'static', id }
212
+ : { t: 'data', id, path: id.slice('data:'.length) };
213
+ case 'tool':
214
+ return { t: 'tool', id, toolId: id.slice('tool:'.length) };
215
+ case 'story': {
216
+ const state = storyDocumentState(id);
217
+ if (!state) return null;
218
+ return {
219
+ t: 'story',
220
+ id,
221
+ modulePath: state.modulePath,
222
+ storyName: state.storyName,
223
+ ...(state.mode === 'docs' ? { view: 'docs' as const } : {}),
224
+ };
225
+ }
226
+ case 'build-profiles':
227
+ return { t: 'static', id };
228
+ default:
229
+ // scene/game (managed by syncCenterDocuments), prefab/content-browser
230
+ // (no implementers persist yet).
231
+ return null;
232
+ }
233
+ }
234
+
235
+ /** Snapshot the CURRENT live workspace state (exported for tests). */
236
+ export function snapshotWorkspaceState(api: Pick<DockviewApi, 'toJSON'>): PersistedWorkspaceState {
237
+ const chat = harnessChat();
238
+ const open: PersistedDocument[] = [];
239
+ for (const doc of openWorkspaceDocuments()) {
240
+ const persisted = persistableDocument(doc);
241
+ if (persisted) open.push(persisted);
242
+ }
243
+ return {
244
+ chat: { collapsed: chat.collapsed },
245
+ layout: api.toJSON(),
246
+ documents: { open, activeId: activeWorkspaceDocumentId() },
247
+ };
248
+ }
249
+
250
+ async function restoreAssetDocument(
251
+ store: WorkspaceStateStore,
252
+ doc: Extract<PersistedDocument, { t: 'asset' }>,
253
+ ): Promise<void> {
254
+ // Existence-only probe (`projectFileExists`) — a deleted asset resolves
255
+ // false and is dropped silently. Deliberately NOT `readProjectTextFile`:
256
+ // that reads the whole asset body just to throw it away here.
257
+ if (!(await projectFileExists(doc.path))) return;
258
+ openAssetDocument(store, doc.path, doc.assetKind as AssetKind);
259
+ }
260
+
261
+ function restoreStoryDocument(
262
+ store: WorkspaceStateStore,
263
+ doc: Extract<PersistedDocument, { t: 'story' }>,
264
+ ): void {
265
+ const module_ = getProjectStoryModules().find((m) => m.modulePath === doc.modulePath);
266
+ if (!module_ || !module_.ok) return;
267
+ if (!module_.stories.some((s) => s.name === doc.storyName)) return;
268
+ if (doc.view === 'docs') openStoryDocsDocument(store, doc.modulePath, doc.storyName);
269
+ else openStoryDocument(store, doc.modulePath, doc.storyName);
270
+ }
271
+
272
+ function restoreStaticDocument(store: WorkspaceStateStore, id: string): void {
273
+ if (id === LEGACY_PROFILER_DOCUMENT_ID) openDebuggerPanel('profiler');
274
+ else if (id === DATA_ASSETS_DOCUMENT_ID) openDataAssetsDocument(store);
275
+ else if (id === BUILD_PROFILES_DOCUMENT_ID && getEditorMode() === 'server')
276
+ openBuildProfilesDocument(store);
277
+ }
278
+
279
+ async function restoreOneDocument(
280
+ store: WorkspaceStateStore,
281
+ doc: PersistedDocument,
282
+ ): Promise<void> {
283
+ switch (doc.t) {
284
+ case 'asset':
285
+ return restoreAssetDocument(store, doc);
286
+ case 'data':
287
+ if (loadedDataFiles().some((f) => f.path === doc.path)) openDataDocument(store, doc.path);
288
+ return;
289
+ case 'tool':
290
+ openToolDocument(store, doc.toolId); // self-verifying (false = no-op)
291
+ return;
292
+ case 'story':
293
+ restoreStoryDocument(store, doc);
294
+ return;
295
+ case 'static':
296
+ restoreStaticDocument(store, doc.id);
297
+ return;
298
+ }
299
+ }
300
+
301
+ /**
302
+ * Restore the persisted open documents + active document (exported for
303
+ * tests). Verification sources (data files / tools / stories) are awaited
304
+ * ONCE up front — only for the kinds actually present — so every open below
305
+ * checks a settled store, in the original tab order.
306
+ */
307
+ export async function restoreWorkspaceDocuments(
308
+ store: WorkspaceStateStore,
309
+ persisted: PersistedWorkspaceState,
310
+ ): Promise<void> {
311
+ const docs = persisted.documents;
312
+ if (!docs || docs.open.length === 0) {
313
+ // Still normalize the active document below when an activeId exists.
314
+ if (docs?.activeId) activateWorkspaceDocument(docs.activeId);
315
+ return;
316
+ }
317
+ if (docs.open.some((d) => d.t === 'data')) await refreshDataFiles().catch(() => {});
318
+ if (docs.open.some((d) => d.t === 'tool')) await refreshProjectTools().catch(() => {});
319
+ if (docs.open.some((d) => d.t === 'story')) {
320
+ const project = getCurrentProject();
321
+ if (project) await refreshProjectStories(project).catch(() => {});
322
+ }
323
+ for (const doc of docs.open) {
324
+ try {
325
+ await restoreOneDocument(store, doc);
326
+ } catch {
327
+ // Drop silently — a document that cannot truthfully reopen is gone.
328
+ }
329
+ }
330
+ // Restore the active tab. Opening activates as it goes, so end by
331
+ // activating the stored active id; if it did not survive restore (dropped,
332
+ // or it was the Game document), fall back to the scene document — there is
333
+ // always an active center document.
334
+ if (!docs.activeId || !activateWorkspaceDocument(docs.activeId)) {
335
+ activateWorkspaceDocument(SCENE_DOCUMENT_ID);
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Install workspace-state persistence for this editor session:
341
+ * 1. restore open documents (async, verified — module header rules);
342
+ * 2. apply Dockview's serialized layout after those panel ids exist;
343
+ * 3. subscribe write-through (debounced 300ms, flushed on pagehide).
344
+ * Returns a teardown (unsubscribes; flushes a pending write).
345
+ */
346
+ export function installWorkspaceStatePersistence(
347
+ store: WorkspaceStateStore,
348
+ api: DockviewApi,
349
+ reconcile: () => void,
350
+ ): () => void {
351
+ const projectId = projectIdentity();
352
+ const persisted = readPersistedWorkspaceState(projectId);
353
+ if (persisted?.chat) hydrateHarnessChatLayout(persisted.chat);
354
+
355
+ let disposed = false;
356
+ let postLayoutTimer: ReturnType<typeof setTimeout> | null = null;
357
+ let layoutApplied = false;
358
+ const applyPersistedLayout = () => {
359
+ if (layoutApplied) return;
360
+ layoutApplied = true;
361
+ if (persisted?.layout) {
362
+ try {
363
+ api.fromJSON(persisted.layout);
364
+ } catch {
365
+ // A stale/corrupt convenience layout must never block the editor.
366
+ api.clear();
367
+ }
368
+ }
369
+ // Restored layouts may omit capabilities added by a newer editor.
370
+ // Reconciliation adds only those missing panels and preserves every
371
+ // surviving group's user-selected position and size.
372
+ reconcile();
373
+ // Dockview completes part of `fromJSON` on its next render turn. A
374
+ // synchronous reconcile alone can therefore run before restored panel
375
+ // parameters are visible and leave a removed manifest-world tab behind.
376
+ // Reconcile once more after that turn so persisted geometry is retained
377
+ // while panels with no live document registration are dropped.
378
+ postLayoutTimer = setTimeout(() => {
379
+ postLayoutTimer = null;
380
+ if (disposed) return;
381
+ reconcile();
382
+ // `fromJSON` also restores Dockview's physical active panel. If that
383
+ // panel belonged to a world that no longer exists, removing it makes
384
+ // Dockview choose an arbitrary neighbor. The document registry is the
385
+ // semantic source of truth, so reassert its verified active document
386
+ // after stale-panel pruning.
387
+ const activeId = activeWorkspaceDocumentId();
388
+ if (activeId) api.getPanel(activeId)?.api.setActive();
389
+ }, 0);
390
+ };
391
+
392
+ let timer: ReturnType<typeof setTimeout> | null = null;
393
+ const unsubscribers: Array<() => void> = [];
394
+
395
+ const flush = () => {
396
+ if (timer !== null) {
397
+ clearTimeout(timer);
398
+ timer = null;
399
+ }
400
+ writePersistedWorkspaceState(projectId, snapshotWorkspaceState(api));
401
+ };
402
+ const schedule = () => {
403
+ if (timer !== null) clearTimeout(timer);
404
+ timer = setTimeout(flush, WRITE_DEBOUNCE_MS);
405
+ };
406
+ const onPageHide = () => flush();
407
+
408
+ // Subscriptions install only after restore settles (module header: a
409
+ // half-restored open-set must never overwrite the stored one). A teardown
410
+ // racing the restore is honored via `disposed`. Bounded by
411
+ // `RESTORE_TIMEOUT_MS`: a hung verification fetch (a stalled asset-existence
412
+ // probe, a network partition on the story/tool/data refresh calls, etc.)
413
+ // must never leave the WHOLE session with no write-through and no
414
+ // pagehide-flush — layout/open-tab state is convenience state, availability
415
+ // beats completeness. Whichever settles first wins; if the restore is
416
+ // still running when the timeout fires, its later document-opens still
417
+ // land normally (the subscriptions below are just live earlier).
418
+ let restoreTimer: ReturnType<typeof setTimeout> | null = null;
419
+ const restoreSettled = (
420
+ persisted ? restoreWorkspaceDocuments(store, persisted) : Promise.resolve()
421
+ )
422
+ .catch(() => {})
423
+ .finally(() => {
424
+ if (disposed) return;
425
+ applyPersistedLayout();
426
+ reconcile();
427
+ });
428
+ const restoreTimedOut = new Promise<void>((resolve) => {
429
+ restoreTimer = setTimeout(() => {
430
+ applyPersistedLayout();
431
+ resolve();
432
+ }, RESTORE_TIMEOUT_MS);
433
+ });
434
+ const restorePromise = Promise.race([restoreSettled, restoreTimedOut]).then(() => {
435
+ if (restoreTimer !== null) clearTimeout(restoreTimer);
436
+ if (disposed) return;
437
+ const dockviewDisposable = api.onDidLayoutChange(schedule);
438
+ unsubscribers.push(
439
+ () => dockviewDisposable.dispose(),
440
+ subscribeHarnessChatLayout(schedule),
441
+ subscribeWorkspaceDocuments(schedule),
442
+ );
443
+ window.addEventListener('pagehide', onPageHide);
444
+ });
445
+ void restorePromise;
446
+
447
+ return () => {
448
+ disposed = true;
449
+ if (postLayoutTimer !== null) clearTimeout(postLayoutTimer);
450
+ if (restoreTimer !== null) clearTimeout(restoreTimer);
451
+ for (const unsub of unsubscribers) unsub();
452
+ window.removeEventListener('pagehide', onPageHide);
453
+ if (timer !== null) flush();
454
+ };
455
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Workspace STATUS-CONTRIBUTION registry (W0,
3
+ * docs/EDITOR-WORKSPACE-SHELL-TODO.md §7 — the shell-contract slice of §9
4
+ * W0). "Status is status, not a command" (§1.5): save/dirty/failure state,
5
+ * problem counts, connection/play state, build progress, and concise
6
+ * selection information belong in a narrow status bar — not mixed into the
7
+ * global command header and not spread across viewport overlays.
8
+ *
9
+ * Consumed since W4 by `components/StatusBar.tsx` (rendered under the
10
+ * bottom dock/drawer strip); the core contributions live in
11
+ * `components/status-contributions.tsx` (+ `build-documents.tsx`'s
12
+ * `build-progress`). Migration map: docs/workspace-shell/W0-INVENTORY.md.
13
+ *
14
+ * Idioms mirror `inspector-section-registry.ts`: module-scope state,
15
+ * `useSyncExternalStore`-shaped subscribe + monotonic version,
16
+ * `__resetForTest`, pure-logic headless tests (`Content` is never rendered
17
+ * here). Ids key persisted/status-bar slots, so duplicates throw (same rule
18
+ * as `workspace-utility-registry.ts`).
19
+ */
20
+
21
+ import type { ComponentType } from 'react';
22
+
23
+ /** The §7 status contribution contract: one compact status-bar item. */
24
+ export interface WorkspaceStatusContribution {
25
+ /** Stable id — keys the status-bar slot (`data-testid`). */
26
+ readonly id: string;
27
+ /** Which end of the bar (§4.1: session state left, selection info right).
28
+ * Default `'left'`. */
29
+ readonly align?: 'left' | 'right';
30
+ /** Slot order within its side (ascending; ties keep registration order).
31
+ * Default `0`. */
32
+ readonly order?: number;
33
+ /** React content host — compact, passive, non-command (§1.5). Never
34
+ * rendered by the registry itself. */
35
+ readonly Content: ComponentType;
36
+ }
37
+
38
+ // --- Module-scope state (mirrors inspector-section-registry.ts) -----------
39
+
40
+ interface StoredContribution {
41
+ readonly contribution: WorkspaceStatusContribution;
42
+ readonly seq: number;
43
+ }
44
+
45
+ let _contributions: StoredContribution[] = [];
46
+ let _seq = 0;
47
+ let _version = 0;
48
+ const _listeners = new Set<() => void>();
49
+
50
+ function notifyChanged(): void {
51
+ _version++;
52
+ for (const fn of _listeners) fn();
53
+ }
54
+
55
+ /** Subscribe to (un)registrations. Returns an unsubscribe function. */
56
+ export function subscribeWorkspaceStatus(fn: () => void): () => void {
57
+ _listeners.add(fn);
58
+ return () => {
59
+ _listeners.delete(fn);
60
+ };
61
+ }
62
+
63
+ /** Monotonic change counter — the `getSnapshot` for `useSyncExternalStore`. */
64
+ export function workspaceStatusRegistryVersion(): number {
65
+ return _version;
66
+ }
67
+
68
+ /**
69
+ * Register a status contribution. Returns an unregister function.
70
+ * Throws on a duplicate id (ids key status-bar slots — same uniqueness rule
71
+ * as `registerWorkspaceUtility`).
72
+ */
73
+ export function registerWorkspaceStatus(contribution: WorkspaceStatusContribution): () => void {
74
+ if (_contributions.some((c) => c.contribution.id === contribution.id)) {
75
+ throw new Error(
76
+ `registerWorkspaceStatus: id "${contribution.id}" is already registered — ` +
77
+ 'status ids key status-bar slots, so they must be unique; unregister the ' +
78
+ 'previous contribution first (docs/EDITOR-WORKSPACE-SHELL-TODO.md §7).',
79
+ );
80
+ }
81
+ const stored: StoredContribution = { contribution, seq: _seq++ };
82
+ _contributions = [..._contributions, stored];
83
+ notifyChanged();
84
+ return () => {
85
+ if (!_contributions.includes(stored)) return;
86
+ _contributions = _contributions.filter((c) => c !== stored);
87
+ notifyChanged();
88
+ };
89
+ }
90
+
91
+ /** Registered contributions for one side of the bar, in (order,
92
+ * registration-sequence) order; with no argument, both sides (left first). */
93
+ export function workspaceStatusContributions(
94
+ align?: 'left' | 'right',
95
+ ): WorkspaceStatusContribution[] {
96
+ const side = (want: 'left' | 'right') =>
97
+ [..._contributions]
98
+ .filter((c) => (c.contribution.align ?? 'left') === want)
99
+ .sort((a, b) => (a.contribution.order ?? 0) - (b.contribution.order ?? 0) || a.seq - b.seq)
100
+ .map((c) => c.contribution);
101
+ if (align) return side(align);
102
+ return [...side('left'), ...side('right')];
103
+ }
104
+
105
+ /** Test-only reset (mirrors `__resetInspectorSectionRegistryForTest`). */
106
+ export function __resetWorkspaceStatusRegistryForTest(): void {
107
+ _contributions = [];
108
+ _seq = 0;
109
+ notifyChanged();
110
+ }
@@ -0,0 +1,49 @@
1
+ /** Semantic commands and failure auto-open policy for workspace utilities. */
2
+
3
+ import { getBuildSession, subscribeBuildSession } from './components/build-documents';
4
+ import { showWorkspaceUtility, toggleWorkspaceUtility } from './workspace-dock-controller';
5
+
6
+ export const CONSOLE_UTILITY_ID = 'console';
7
+ export const DEBUGGER_UTILITY_ID = 'debugger';
8
+ export const BUILD_OUTPUT_UTILITY_ID = 'build-output';
9
+
10
+ export interface UtilityAutoOpenStore {
11
+ subscribe(fn: () => void): () => void;
12
+ readonly saveState: 'saved' | 'unsaved' | 'failed';
13
+ }
14
+
15
+ /** Only actionable failures open a utility automatically. */
16
+ export function installUtilityAutoOpen(store: UtilityAutoOpenStore): () => void {
17
+ let previousSave = store.saveState;
18
+ const stopStore = store.subscribe(() => {
19
+ const next = store.saveState;
20
+ if (next === 'failed' && previousSave !== 'failed') showWorkspaceUtility(CONSOLE_UTILITY_ID);
21
+ previousSave = next;
22
+ });
23
+
24
+ let previousBuildPhase = getBuildSession().phase;
25
+ const stopBuild = subscribeBuildSession(() => {
26
+ const session = getBuildSession();
27
+ if (session.phase === 'done' && previousBuildPhase !== 'done' && session.result?.ok === false) {
28
+ showWorkspaceUtility(BUILD_OUTPUT_UTILITY_ID);
29
+ }
30
+ previousBuildPhase = session.phase;
31
+ });
32
+
33
+ return () => {
34
+ stopStore();
35
+ stopBuild();
36
+ };
37
+ }
38
+
39
+ /** Legacy shell events remain stable while their physical implementation is Dockview. */
40
+ export function installUtilityEvents(): () => void {
41
+ const onToggleConsole = () => toggleWorkspaceUtility(CONSOLE_UTILITY_ID);
42
+ const onShowBuildOutput = () => showWorkspaceUtility(BUILD_OUTPUT_UTILITY_ID);
43
+ window.addEventListener('editor:toggle-console', onToggleConsole);
44
+ window.addEventListener('editor:show-build-output', onShowBuildOutput);
45
+ return () => {
46
+ window.removeEventListener('editor:toggle-console', onToggleConsole);
47
+ window.removeEventListener('editor:show-build-output', onShowBuildOutput);
48
+ };
49
+ }