@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
@@ -22,7 +22,7 @@
22
22
  * `world-session-state.ts`'s own stance — nothing here needs a
23
23
  * project-switch reset within a single browser tab.
24
24
  */
25
- import type { UIStory } from '@vgai/scene-ui/registry';
25
+ import type { UIStory } from './story-types';
26
26
 
27
27
  const _overlays = new Map<string, Record<string, UIStory>>();
28
28
 
@@ -0,0 +1,11 @@
1
+ import type { ComponentType } from 'react';
2
+
3
+ /** Portable design-time state used by React-root authoring. */
4
+ export interface UIStory {
5
+ label: string;
6
+ data: Record<string, unknown>;
7
+ props?: Record<string, unknown>;
8
+ }
9
+
10
+ /** Optional React component catalog used by the root authoring adapter. */
11
+ export type UIComponentRegistry = Record<string, ComponentType<Record<string, unknown>>>;
@@ -15,6 +15,7 @@ import type {
15
15
  AssetDropProvider,
16
16
  AuthoringAdapter,
17
17
  AuthoringCapabilities,
18
+ AuthoringProvenance,
18
19
  ComponentsProvider,
19
20
  EditorNode,
20
21
  FileMapProvider,
@@ -56,7 +57,8 @@ function fieldKindToPropertyType(kind: FieldKind): PropertyDescriptor['type'] {
56
57
  }
57
58
 
58
59
  // ---------------------------------------------------------------------------
59
- // D8 — root groups (radio roots over the project's `*.vscn.json` files)
60
+ // D8 compatibility activation index over project `*.vscn.json` files.
61
+ // Only the active document is projected into the hierarchy (D19/D20).
60
62
  // ---------------------------------------------------------------------------
61
63
 
62
64
  const SCENE_ROOT_PREFIX = 'sceneRoot:';
@@ -77,7 +79,9 @@ function normalizeScenePath(path: string): string {
77
79
  return path.startsWith('/') ? path.slice(1) : path;
78
80
  }
79
81
 
80
- async function fetchSceneDoc(path: string): Promise<SceneFile> {
82
+ /** Exported for `edit-mode-authoring.ts`'s `switchFocusedWorld` (A4) — the
83
+ * one scene-fetch path both root activation and world focus-switch share. */
84
+ export async function fetchSceneDoc(path: string): Promise<SceneFile> {
81
85
  // Mirrors `initial-scene.ts`'s browser-vs-server branch: browser builds
82
86
  // read scene files through the active `StorageBackend` (OPFS/File System
83
87
  // Access), not `fetch()`; the server dev build serves them over HTTP.
@@ -101,6 +105,7 @@ function toNode(e: SceneEntity, parentId: string | null): EditorNode {
101
105
  return {
102
106
  id: e.id!,
103
107
  label: e.name,
108
+ role: 'entity',
104
109
  kind: kindOf(e),
105
110
  parentId,
106
111
  childIds: (e.children ?? []).map((c) => c.id!),
@@ -322,23 +327,35 @@ export class VgaiSceneAuthoringAdapter implements AuthoringAdapter {
322
327
  persist: true,
323
328
  };
324
329
 
330
+ readonly provenance: AuthoringProvenance = {
331
+ source: 'document',
332
+ label: 'scene',
333
+ detail: 'Authored scene document — edits save to the .vscn scene file.',
334
+ };
335
+
325
336
  subscribe(listener: () => void): () => void {
326
337
  return this.store.subscribe(listener);
327
338
  }
328
339
 
329
340
  readonly hierarchy: HierarchyProvider = {
330
- // D8 — member (scene-file root) rows are APPENDED after the store's own
331
- // entity roots (never interleaved between them): several existing e2e
332
- // specs range-select or index-scan entity rows by relative position
333
- // (`29-game-hierarchy.spec.ts`'s multi-select, drag-reorder) — appending
334
- // keeps every entity's relative order/adjacency byte-identical to today,
335
- // the only delta is N additional childless rows at the tail.
336
- roots: () => [...this.store.entities.map((e) => toNode(e, null)), ...this.memberNodes()],
341
+ // D19/D20 — hierarchy is the active world content, not a project-wide
342
+ // checklist. The active scene is a real document row; other scene files
343
+ // remain assets until opened. A transient/untitled store (unit fixtures,
344
+ // browser import before first save) honestly has no document and retains
345
+ // the flat entity-root shape.
346
+ roots: () => {
347
+ const path = this.activeScenePath();
348
+ return path ? [this.memberNode(path)!] : this.store.entities.map((e) => toNode(e, null));
349
+ },
337
350
  node: (id) => {
338
351
  const memberPath = pathForSceneRootId(id);
339
352
  if (memberPath !== null) return this.memberNode(memberPath);
340
353
  const hit = findInTree(this.store.entities, id);
341
- return hit ? toNode(hit.entity, hit.parentId) : null;
354
+ if (!hit) return null;
355
+ return toNode(
356
+ hit.entity,
357
+ hit.parentId ?? (this.activeScenePath() ? sceneRootId(this.activeScenePath()!) : null),
358
+ );
342
359
  },
343
360
  object3D: (id) => this.store.objectMap.get(id) ?? null,
344
361
  idForObject3D: (o) => (getUserData(o, 'entityId') as string | undefined) ?? null,
@@ -351,36 +368,48 @@ export class VgaiSceneAuthoringAdapter implements AuthoringAdapter {
351
368
  return getCachedProjectScenes();
352
369
  }
353
370
 
354
- private memberNodes(): EditorNode[] {
355
- return this.scenesList()
356
- .map((path) => this.memberNode(path))
357
- .filter((n): n is EditorNode => n !== null);
371
+ private activeScenePath(): string | null {
372
+ return this.store.savePath ? normalizeScenePath(this.store.savePath) : null;
373
+ }
374
+
375
+ private sceneLabel(path: string): string {
376
+ const file = path.split('/').pop() ?? path;
377
+ const stem = file.replace(/\.vscn\.json$/i, '').replace(/\.json$/i, '');
378
+ const words = stem
379
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
380
+ .replace(/[-_]+/g, ' ')
381
+ .trim();
382
+ return words ? words.replace(/\b\w/g, (letter) => letter.toUpperCase()) : file;
358
383
  }
359
384
 
360
385
  /** `null` for a path that isn't (or is no longer) a known project scene —
361
386
  * a stale/dangling `sceneRoot:` id resolves to nothing rather than a
362
387
  * fabricated node. */
363
388
  private memberNode(path: string): EditorNode | null {
364
- if (!this.scenesList().includes(path)) return null;
389
+ const activePath = this.activeScenePath();
390
+ if (!this.scenesList().includes(path) && activePath !== path) return null;
365
391
  const isActive =
366
392
  this.store.savePath !== null && normalizeScenePath(this.store.savePath) === path;
367
393
  return {
368
394
  id: sceneRootId(path),
369
- label: path,
370
- kind: 'object',
395
+ label: this.sceneLabel(path),
396
+ secondaryLabel: path,
397
+ role: 'document',
398
+ kind: 'scene',
371
399
  parentId: null,
372
- childIds: [],
373
- // D8 — member of a RootGroup, not the active one (see `authoring.ts`'s
374
- // `EditorNode.flags.inactiveRoot` doc). The active member gets NO flag
375
- // so both radio states are plain childless rows (GameHierarchy renders
376
- // the ● / ○ radio from `rootGroups` data, not from this flag alone).
400
+ childIds: isActive ? this.store.entities.map((entity) => entity.id!) : [],
401
+ // D8 — member of the compatibility activation index, not its active
402
+ // document (see `authoring.ts`'s `inactiveRoot` contract). Inactive
403
+ // members are resolvable for file opening but are not hierarchy roots.
377
404
  flags: isActive ? {} : { inactiveRoot: true },
378
405
  };
379
406
  }
380
407
 
381
408
  /**
382
- * D8 — one mutually-exclusive "Scenes" group over every project
383
- * `*.vscn.json`. `activate()` flushes a pending autosave first (belt: the
409
+ * Activation index over project `*.vscn.json` assets. Only its active member
410
+ * is projected as the hierarchy's document row; inactive members stay in
411
+ * this file-opening index instead of becoming a scene checklist. `activate()`
412
+ * flushes a pending autosave first (belt: the
384
413
  * 300ms debounce could otherwise lose an edit made just before switching),
385
414
  * then loads the target scene into the live store exactly like opening it
386
415
  * from the asset browser (`DefaultEditorLayout.tsx`'s `openSceneFromAsset`
@@ -676,7 +705,7 @@ export class VgaiSceneAuthoringAdapter implements AuthoringAdapter {
676
705
  this.store.moveEntity([id], parentId, index);
677
706
  },
678
707
  // A2 parity growth — the former toolbar-only `addMenuItems` list (12
679
- // kinds), now driving the shell's generic Create▸ palette for ANY
708
+ // kinds), now driving the shell's generic Create submenu for ANY
680
709
  // parent (root via the toolbar `+`, or a specific row via the context
681
710
  // menu) — a capability the old toolbar-only Add menu never had.
682
711
  creatableKinds: () =>
@@ -0,0 +1,78 @@
1
+ import type { AuthoringAdapter } from '@engine/adapter';
2
+ import type { EditorStore } from '../editor-store';
3
+ import { CompositeAuthoringAdapter } from './composite-authoring-adapter';
4
+ import { resolveFocusedViewportWorldId } from './world-hidden-viewport';
5
+ import { isWorldHidden } from './world-session-state';
6
+
7
+ /**
8
+ * The one native root whose viewport tools may claim the current selection.
9
+ *
10
+ * Viewport content can contain several roots at once, but editing chrome needs
11
+ * one unambiguous coordinate system and capability owner. Empty,
12
+ * organizational, and cross-root selections therefore resolve to `null`.
13
+ */
14
+ export interface ViewportToolContext {
15
+ readonly worldId: string | null;
16
+ readonly kind: string;
17
+ readonly adapter: AuthoringAdapter;
18
+ }
19
+
20
+ function compositeContext(
21
+ composite: CompositeAuthoringAdapter,
22
+ selectedIds: readonly string[],
23
+ ): ViewportToolContext | null {
24
+ const children = composite.childAdapters();
25
+ let owner: (typeof children)[number] | null = null;
26
+
27
+ for (const id of selectedIds) {
28
+ const child =
29
+ children.find((candidate) => composite.groupNodeId(candidate.worldId) === id) ??
30
+ children.find((candidate) => composite.ownerOf(id) === candidate.worldId);
31
+ // Kind/projection folders and stale ids do not claim native-world tools.
32
+ if (!child) return null;
33
+ // A heterogeneous selection has no single native editing context.
34
+ if (owner && owner.worldId !== child.worldId) return null;
35
+ owner = child;
36
+ }
37
+
38
+ return owner ? { worldId: owner.worldId, kind: owner.kind, adapter: owner.adapter } : null;
39
+ }
40
+
41
+ /**
42
+ * Resolve viewport-tool ownership from selection, never from which roots
43
+ * merely exist or happen to be visible.
44
+ */
45
+ export function resolveViewportToolContext(
46
+ adapter: AuthoringAdapter,
47
+ selectedIds: Iterable<string>,
48
+ ): ViewportToolContext | null {
49
+ const ids = [...selectedIds];
50
+ if (ids.length === 0) return null;
51
+ if (adapter instanceof CompositeAuthoringAdapter) return compositeContext(adapter, ids);
52
+
53
+ // Legacy/single-adapter sessions have no manifest kind metadata. Keep the
54
+ // fallback structural and conservative: a rect-capable selection is DOM;
55
+ // a selection whose every node resolves to Object3D is Three.js. Unknown
56
+ // adapters advertise no kind-specific chrome rather than guessing.
57
+ if (ids.some((id) => adapter.hierarchy.node(id) === null)) return null;
58
+ if (adapter.rects) return { worldId: null, kind: 'react', adapter };
59
+ if (ids.every((id) => adapter.hierarchy.object3D(id) !== null)) {
60
+ return { worldId: null, kind: 'threejs', adapter };
61
+ }
62
+ return null;
63
+ }
64
+
65
+ /**
66
+ * Whether the selected owner is actually painted in this viewport. DOM
67
+ * layers follow their world eye; among several Three.js roots only the one
68
+ * focused into the editor's single live Three.js scene can own its cockpit.
69
+ */
70
+ export function isViewportToolContextVisible(
71
+ store: EditorStore,
72
+ context: ViewportToolContext | null,
73
+ ): boolean {
74
+ if (!context) return false;
75
+ if (context.worldId !== null && isWorldHidden(context.worldId)) return false;
76
+ if (context.kind !== 'threejs' || context.worldId === null) return true;
77
+ return resolveFocusedViewportWorldId(store) === context.worldId;
78
+ }
@@ -0,0 +1,35 @@
1
+ /** Session-only responsive canvas size for DOM-backed authoring roots. */
2
+ export interface WorldCanvasViewport {
3
+ readonly width: number | null;
4
+ readonly height: number | null;
5
+ }
6
+
7
+ const FILL_VIEWPORT: WorldCanvasViewport = { width: null, height: null };
8
+ let viewport: WorldCanvasViewport = FILL_VIEWPORT;
9
+ const listeners = new Set<() => void>();
10
+
11
+ export function getWorldCanvasViewport(): WorldCanvasViewport {
12
+ return viewport;
13
+ }
14
+
15
+ export function setWorldCanvasViewport(width: number | null, height: number | null): void {
16
+ const nextWidth = width === null ? null : Math.max(1, Math.round(width));
17
+ const nextHeight = height === null ? null : Math.max(1, Math.round(height));
18
+ if (viewport.width === nextWidth && viewport.height === nextHeight) return;
19
+ viewport = { width: nextWidth, height: nextHeight };
20
+ for (const listener of listeners) listener();
21
+ }
22
+
23
+ export function resetWorldCanvasViewport(): void {
24
+ setWorldCanvasViewport(null, null);
25
+ }
26
+
27
+ export function subscribeWorldCanvasViewport(listener: () => void): () => void {
28
+ listeners.add(listener);
29
+ return () => listeners.delete(listener);
30
+ }
31
+
32
+ export function _resetWorldCanvasViewportForTest(): void {
33
+ viewport = FILL_VIEWPORT;
34
+ listeners.clear();
35
+ }
@@ -0,0 +1,186 @@
1
+ /**
2
+ * World-hidden eye (D9, `world-session-state.ts`) applied to the EDIT-mode
3
+ * threejs viewport.
4
+ *
5
+ * Bug this fixes: the eye on a hierarchy world-group row (`world:<id>
6
+ * (<kind>)`, shown for 2+ world manifests) toggles `isWorldHidden(worldId)`
7
+ * and culls the hierarchy subtree, but for a react/pixi world that's ALSO the
8
+ * only thing that mattered — `design-time-layers.ts`'s `applySessionStyle`
9
+ * sets `display:none` on that world's DOM layer. A threejs world has no DOM
10
+ * layer; nothing consumed `isWorldHidden` for its actual `THREE.Object3D`s
11
+ * before this module existed, so the eye was a no-op in the viewport.
12
+ *
13
+ * Session-local only (never touches entity descriptors/undo/the `.vscn`
14
+ * file, matching the DOM roots' own `display:none` semantics): every call
15
+ * RECOMPUTES `obj.visible` from two already-authoritative, already-live
16
+ * sources —
17
+ * - the entity's OWN visible flag, read back off `userData['entity']`
18
+ * (the descriptor `scene-sync.ts` stamps onto every object it builds —
19
+ * see ARCHITECTURE.md's "the world node IS the entity"), which is the
20
+ * SAME flag the generic inspector's eye writes via
21
+ * `VgaiSceneAuthoringAdapter.inspector.set(id, 'visible', …)`;
22
+ * - `!isWorldHidden(focusedWorldId)`.
23
+ * ANDing per call (never "remembering" a previous visibility) is what makes
24
+ * this rebuild-proof: a full or single-entity `scene-sync.ts` rebuild
25
+ * re-stamps `obj.visible = entity.visible !== false` from the descriptor,
26
+ * silently dropping any one-shot override — but the very next call here puts
27
+ * the world-hidden override back, and it never clobbers a per-entity
28
+ * `visible:false` because that flag is read fresh from the descriptor each
29
+ * time rather than assumed. Callers must re-invoke this after every rebuild
30
+ * AND after the toggle itself; `editor-viewport.ts`'s `syncFromStore()` is
31
+ * exactly that seam — it already re-runs on every `store.subscribe` notify,
32
+ * which covers both a scene-sync rebuild (which notifies) and
33
+ * `GameHierarchy.tsx`'s toggle handler (`toggleWorldHidden` +
34
+ * `store.notifyIngestEdit()`).
35
+ *
36
+ * Multi-threejs-world limitation (by design, not an oversight): a composite's
37
+ * `store.objectMap`/live THREE.Scene only ever holds ONE threejs world's real
38
+ * objects — the "focused" one resolved by
39
+ * `edit-mode-authoring.ts`'s `resolveFocusedThreejsWorldId` at composite
40
+ * install time (whichever world's scene `loadInitialScene` loaded into the
41
+ * store). Every OTHER threejs-kind world in the manifest gets a read-only
42
+ * `BoundaryAuthoringAdapter` with NO live geometry in this viewport at all.
43
+ * A4's `switchFocusedWorld` (landed — `edit-mode-authoring.ts`) changes WHICH
44
+ * world is the focused one (document + `?scene=` + rebuild together), but at
45
+ * any instant there is still exactly ONE: `resolveFocusedViewportWorldId`
46
+ * below only ever needs to name ONE worldId, not walk per-object ownership —
47
+ * true CONCURRENT multi-threejs-world rendering remains out of scope.
48
+ */
49
+
50
+ import { getUserData } from '@engine/scene/user-data';
51
+ import * as THREE from 'three';
52
+ import type { EditorStore } from '../editor-store';
53
+ import { getActiveAuthoring } from './active-adapter';
54
+ import { CompositeAuthoringAdapter } from './composite-authoring-adapter';
55
+ import { isWorldHidden } from './world-session-state';
56
+
57
+ /**
58
+ * The worldId of the ONE threejs world actually rendered in this viewport
59
+ * (`store.objectMap`'s contents), or `null` when the active adapter isn't a
60
+ * composite (single-world project — no world-group row/eye exists for it at
61
+ * all, so there is nothing to resolve) or declares no threejs world.
62
+ *
63
+ * Distinguishes the focused, LIVE `VgaiSceneAuthoringAdapter` from every
64
+ * other (possibly also `kind: 'threejs'`) child's read-only
65
+ * `BoundaryAuthoringAdapter` by `capabilities.transform` — the Boundary
66
+ * adapter's capabilities are all `false` (see `boundary-authoring-adapter.ts`),
67
+ * while `VgaiSceneAuthoringAdapter` is the only child that ever reports
68
+ * `transform: true`. This reuses an existing, already-load-bearing signal
69
+ * instead of adding a new `instanceof` check or a parallel "which world is
70
+ * focused" bookkeeping slot.
71
+ */
72
+ export function resolveFocusedViewportWorldId(store: EditorStore): string | null {
73
+ const active = getActiveAuthoring(store);
74
+ if (!(active instanceof CompositeAuthoringAdapter)) return null;
75
+ const focused = active
76
+ .childAdapters()
77
+ .find((c) => c.kind === 'threejs' && c.adapter.capabilities.transform);
78
+ return focused?.worldId ?? null;
79
+ }
80
+
81
+ /**
82
+ * Apply the eye toggle to every object in `objectMap` (see module doc
83
+ * comment for the full rebuild-proofing rationale). `focusedWorldId` is
84
+ * accepted as a parameter (rather than re-resolved internally) so a caller
85
+ * that already computed it once per call (e.g. for a cheap re-apply
86
+ * signature) doesn't pay for `resolveFocusedViewportWorldId`'s composite walk
87
+ * twice.
88
+ */
89
+ export function applyWorldHiddenVisibility(
90
+ objectMap: ReadonlyMap<string, THREE.Object3D>,
91
+ focusedWorldId: string | null,
92
+ ): void {
93
+ const hidden = focusedWorldId !== null && isWorldHidden(focusedWorldId);
94
+ for (const obj of objectMap.values()) {
95
+ // Structural read of the ONE descriptor field this module consumes —
96
+ // deliberately not the SceneEntity type (the authoring-inversion ratchet:
97
+ // only the first-party adapter allowlist may import it).
98
+ const entity = getUserData(obj, 'entity') as { visible?: boolean } | undefined;
99
+ // Defensive fallback for the (should-never-happen) case of an objectMap
100
+ // entry with no stamped descriptor: leave its current visibility alone
101
+ // rather than guessing, UNLESS the world itself is hidden (the eye still
102
+ // wins — nothing in a hidden world should render).
103
+ const ownVisible = entity ? entity.visible !== false : obj.visible;
104
+ obj.visible = hidden ? false : ownVisible;
105
+ }
106
+ }
107
+
108
+ /**
109
+ * Matches `scene-sync.ts`'s `applyEnvironment` DEFAULT_BG — what an editor
110
+ * viewport with NO authored environment shows. A single shared instance so
111
+ * the per-notify re-apply below never allocates.
112
+ */
113
+ const HIDDEN_WORLD_BACKGROUND = new THREE.Color('#aaaaaa');
114
+
115
+ /**
116
+ * The scene-level half of the eye: `applyWorldHiddenVisibility` above only
117
+ * covers entity `Object3D`s (the `objectMap`), but a threejs world also
118
+ * renders things that live directly on the `THREE.Scene` — the authored
119
+ * `background` color/skybox, `fog`, IBL `environment`, and the
120
+ * `envObject`-tagged ambient light `applyEnvironment` adds. None of those are
121
+ * entities, so before this function existed, hiding the world left the
122
+ * viewport painted in the world's background color (looking exactly like
123
+ * "the eye didn't work").
124
+ *
125
+ * Same recompute-don't-remember contract as above: this only ever writes the
126
+ * SUPPRESSED state, idempotently, and callers invoke it on every notify while
127
+ * the world is hidden (an inspector environment edit mid-hide re-paints the
128
+ * authored values via `_applyEnvironment` — the very next notify re-suppresses
129
+ * them here). The restore path is `EditorStore.reapplyEnvironment()`, whose
130
+ * truth is `_sceneMeta.environment` — never a snapshot taken here.
131
+ *
132
+ * Post-processing (bloom/vignette) is the composer's half: `ViewportPanel`'s
133
+ * `rebuildComposer` passes `undefined` instead of `store.environment` while
134
+ * the world is hidden; `editor-viewport.ts` bumps `composerVersion` (via
135
+ * `reapplyEnvironment`) on both hide/show edges so that rebuild actually runs.
136
+ */
137
+ export function suppressWorldEnvironment(scene: THREE.Scene): void {
138
+ scene.background = HIDDEN_WORLD_BACKGROUND;
139
+ scene.fog = null;
140
+ scene.environment = null;
141
+ scene.traverse((obj) => {
142
+ if (getUserData(obj, 'envObject')) obj.visible = false;
143
+ });
144
+ }
145
+
146
+ /**
147
+ * Is a threejs surface currently SHOWING in the viewport? This is the single
148
+ * derived truth the 3D cockpit chrome gates on (spec 28,
149
+ * `docs/unified-world-editor/28-conditional-viewport-chrome.md`): the grid,
150
+ * orientation gizmo, transform toolstrip, camera readout, shading modes, and
151
+ * the transform gizmo itself are claims about what the user can do RIGHT NOW
152
+ * — showing them over a viewport with no visible 3D content is a false claim
153
+ * (same spirit as the adapter tier rule: degrade loudly, never advertise
154
+ * what isn't there). The DOM half of the chrome (`WorldSelectionOverlay`)
155
+ * already self-gates this way; this selector brings the pre-contract 3D
156
+ * chrome under the same rule.
157
+ *
158
+ * Truth table (deliberately conservative — every non-composite mode keeps
159
+ * today's behavior exactly):
160
+ * - active adapter is not a composite (single-world project, classic scene
161
+ * editing, ingest): `true` — no world-group eyes exist there, nothing to
162
+ * derive from, status quo.
163
+ * - composite with NO threejs-kind child at all (e.g. react-only
164
+ * multi-world manifest): `false` — no 3D content can EVER show.
165
+ * - composite whose only threejs children are unfocused read-only
166
+ * Boundaries (`resolveFocusedViewportWorldId` → null): `true` — such a
167
+ * world has no live geometry here (even with A4's `switchFocusedWorld`
168
+ * landed, exactly one threejs world is live at a time and this state is
169
+ * reachable transiently around a rebuild), but it is manifest-declared 3D
170
+ * content; keep the cockpit rather than flickering chrome on
171
+ * adapter-install timing.
172
+ * - composite with a focused live threejs world: `!isWorldHidden(worldId)`
173
+ * — the eye is the "what we're showing" signal.
174
+ *
175
+ * Recompute-per-call like everything else in this module: callers read it on
176
+ * every store notify (`syncFromStore` / `ViewportPanel` render), never cache
177
+ * it across notifies.
178
+ */
179
+ export function isThreejsSurfaceVisible(store: EditorStore): boolean {
180
+ const active = getActiveAuthoring(store);
181
+ if (!(active instanceof CompositeAuthoringAdapter)) return true;
182
+ const children = active.childAdapters();
183
+ if (!children.some((c) => c.kind === 'threejs')) return false;
184
+ const focusedId = resolveFocusedViewportWorldId(store);
185
+ return focusedId === null ? true : !isWorldHidden(focusedId);
186
+ }
@@ -1,8 +1,8 @@
1
1
  /**
2
- * World pan state (spec27 §8 "space-pan" row, D4 rebuild —
2
+ * Shared DOM-world canvas transform (spec27 §8 "space-pan" row, D4 rebuild —
3
3
  * docs/unified-world-editor/27-visual-react-editing.md) — a module-level
4
4
  * subscribable singleton (mirrors `eyedropper-session.ts`'s "bare singleton +
5
- * own listener set" shape) holding the CURRENT SHARED pan translate `(x, y)`
5
+ * own listener set" shape) holding the CURRENT SHARED pan + zoom
6
6
  * applied, in LOCKSTEP, to both every design-time world-layer host
7
7
  * (`design-time-layers.ts`'s `createLayerElement`-produced react/pixi layers)
8
8
  * and `WorldSelectionOverlay`'s own outer wrapper.
@@ -18,17 +18,19 @@
18
18
  * IDENTICAL `(x, y)` delta — a rigid subtree translation — so the
19
19
  * subtraction is invariant under pan: `rect(id)` returns the SAME
20
20
  * host-relative numbers at any pan offset, unmodified, with no pan-awareness
21
- * needed in the adapter, `inspect.ts`'s geometry math, or any of the ~14
22
- * overlay chrome consumers. `PickProvider.pick` needs no changes either — it
21
+ * needed in `inspect.ts`'s geometry math or the overlay chrome consumers.
22
+ * Zoom scales both measured rectangles; `toHostRelative` divides their
23
+ * offsets and dimensions by the shared zoom before exposing authored-space
24
+ * geometry, while `WorldSelectionOverlay.toHostLocal` performs the inverse
25
+ * for pointer gestures. `PickProvider.pick` needs no changes — it
23
26
  * hit-tests real rendered rects against the real client cursor position,
24
27
  * both of which already reflect the applied transform natively.
25
28
  *
26
29
  * Applying the exact SAME transform to `WorldSelectionOverlay`'s own outer
27
30
  * wrapper (a SIBLING of every world-layer host inside the SAME
28
31
  * `ViewportPanel` containerRef parent — see that file's own mount-site doc
29
- * comment) shifts the overlay's local coordinate origin by the identical
30
- * `(x, y)` — so drawing a chrome element at `rect(id)`'s (pan-invariant)
31
- * host-relative `(x, y)` inside the panned overlay wrapper lands it at the
32
+ * comment) shifts and scales the overlay by the identical transform — so
33
+ * drawing a chrome element at `rect(id)`'s authored coordinates lands it at the
32
34
  * SAME screen position as the panned live element it describes. This is the
33
35
  * entire "lockstep" contract: every consumer on both sides of the pan reads
34
36
  * ONE shared value (`getWorldPan()`) and applies it through the ONE shared
@@ -39,9 +41,10 @@
39
41
  export interface WorldPan {
40
42
  readonly x: number;
41
43
  readonly y: number;
44
+ readonly zoom: number;
42
45
  }
43
46
 
44
- const ZERO_PAN: WorldPan = { x: 0, y: 0 };
47
+ const ZERO_PAN: WorldPan = { x: 0, y: 0, zoom: 1 };
45
48
 
46
49
  let _pan: WorldPan = ZERO_PAN;
47
50
  const listeners = new Set<() => void>();
@@ -50,16 +53,27 @@ function notify(): void {
50
53
  for (const l of listeners) l();
51
54
  }
52
55
 
53
- /** The current shared pan translate, in CSS px. */
56
+ /** The current shared canvas transform; x/y are CSS px and zoom is unitless. */
54
57
  export function getWorldPan(): WorldPan {
55
58
  return _pan;
56
59
  }
57
60
 
61
+ /** Set the complete shared canvas transform in one subscriber notification. */
62
+ export function setWorldView(x: number, y: number, zoom: number): void {
63
+ const nextZoom = Math.min(4, Math.max(0.1, zoom));
64
+ if (_pan.x === x && _pan.y === y && _pan.zoom === nextZoom) return;
65
+ _pan = { x, y, zoom: nextZoom };
66
+ notify();
67
+ }
68
+
58
69
  /** Set the shared pan translate to an absolute `(x, y)`. */
59
70
  export function setWorldPan(x: number, y: number): void {
60
- if (_pan.x === x && _pan.y === y) return;
61
- _pan = { x, y };
62
- notify();
71
+ setWorldView(x, y, _pan.zoom);
72
+ }
73
+
74
+ /** Set the shared canvas zoom while preserving its current pan. */
75
+ export function setWorldZoom(zoom: number): void {
76
+ setWorldView(_pan.x, _pan.y, zoom);
63
77
  }
64
78
 
65
79
  /** Set the shared pan translate by a RELATIVE `(dx, dy)` — a convenience
@@ -71,19 +85,19 @@ export function panWorldBy(dx: number, dy: number): void {
71
85
  setWorldPan(_pan.x + dx, _pan.y + dy);
72
86
  }
73
87
 
74
- /** Reset pan to `(0, 0)` — called on a world-layer remount (`design-time-
88
+ /** Reset pan to `(0, 0)` and zoom to 100% — called on a world-layer remount (`design-time-
75
89
  * layers.ts`'s `mountDesignTimeLayers`, invoked on initial edit-mode install
76
90
  * AND every `'editor:edit-mode-rebuild'`), so switching/reloading the active
77
91
  * world set never leaves a stale pan offset applied over a DIFFERENT layer
78
92
  * stack. A no-op (no `notify()`) when already at rest, so a fresh session
79
93
  * never fires a spurious subscriber tick. */
80
94
  export function resetWorldPan(): void {
81
- if (_pan.x === 0 && _pan.y === 0) return;
95
+ if (_pan.x === 0 && _pan.y === 0 && _pan.zoom === 1) return;
82
96
  _pan = ZERO_PAN;
83
97
  notify();
84
98
  }
85
99
 
86
- /** Subscribe to pan changes — both `design-time-layers.ts` (imperative DOM
100
+ /** Subscribe to canvas-transform changes — both `design-time-layers.ts` (imperative DOM
87
101
  * style writes) and `WorldSelectionOverlay` (`useSyncExternalStore`) bind
88
102
  * this so every lockstep mount point re-applies the SAME new value the
89
103
  * instant it changes. */
@@ -102,8 +116,10 @@ export function subscribeWorldPan(listener: () => void): () => void {
102
116
  * to its pre-this-feature output.
103
117
  */
104
118
  export function panTransformValue(pan: WorldPan): string | undefined {
105
- if (pan.x === 0 && pan.y === 0) return undefined;
106
- return `translate(${pan.x}px, ${pan.y}px)`;
119
+ if (pan.x === 0 && pan.y === 0 && pan.zoom === 1) return undefined;
120
+ if (pan.zoom === 1) return `translate(${pan.x}px, ${pan.y}px)`;
121
+ if (pan.x === 0 && pan.y === 0) return `scale(${pan.zoom})`;
122
+ return `translate(${pan.x}px, ${pan.y}px) scale(${pan.zoom})`;
107
123
  }
108
124
 
109
125
  /** Test-only reset (mirrors `eyedropper-session.ts`'s/`world-session-
@@ -21,7 +21,7 @@
21
21
  * B1 only stores/exposes the toggle; nothing reads it yet.
22
22
  *
23
23
  * Keyed by the RAW manifest world id (`CompositeChild.worldId` /
24
- * `ResolvedWorldEntry.id`) — the SAME id `design-time-layers.ts` gets from
24
+ * `ResolvedAdapterRoot.id`) — the SAME id `design-time-layers.ts` gets from
25
25
  * `composite.childAdapters()`, NOT the composite's synthetic `world:<id>`
26
26
  * group-node id. `GameHierarchy.tsx` (whose rows ARE keyed by the group-node
27
27
  * id) resolves the raw worldId via `compositeGroupBadge`'s `worldId` field