@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
@@ -6,31 +6,21 @@ import {
6
6
  faEye,
7
7
  faEyeSlash,
8
8
  } from '@fortawesome/free-solid-svg-icons';
9
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
10
9
  import { useEffect, useRef, useState } from 'react';
10
+ import {
11
+ Button,
12
+ EditorIcon,
13
+ FloatingToolbar,
14
+ IconButton,
15
+ Menu,
16
+ MenuItem,
17
+ MenuSeparator,
18
+ SplitButtonGroup,
19
+ Text,
20
+ Tooltip,
21
+ } from '../design-system';
11
22
  import { useEditorStore } from '../EditorContext';
12
23
  import type { EditorStore, ShadingMode } from '../editor-store';
13
- import { Tooltip } from './primitives/Tooltip';
14
-
15
- const iconBtn: React.CSSProperties = {
16
- background: '#333',
17
- color: '#c5c8ce',
18
- border: '1px solid #444',
19
- borderRadius: 3,
20
- padding: '4px 7px',
21
- cursor: 'pointer',
22
- fontSize: 12,
23
- lineHeight: '18px',
24
- display: 'flex',
25
- alignItems: 'center',
26
- };
27
-
28
- const activeIconBtn: React.CSSProperties = {
29
- ...iconBtn,
30
- background: '#579EFF',
31
- border: '1px solid #6AADFF',
32
- color: '#fff',
33
- };
34
24
 
35
25
  const shadingLabels: Record<ShadingMode, string> = {
36
26
  solid: 'Solid',
@@ -40,66 +30,54 @@ const shadingLabels: Record<ShadingMode, string> = {
40
30
 
41
31
  const shadingOrder: ShadingMode[] = ['solid', 'wireframe', 'unlit'];
42
32
 
43
- export function ViewportOverlay() {
33
+ /** Viewport-level rendering controls. Three-specific controls hide when no 3D world is active. */
34
+ export function ViewportOverlay({ statsOnly = false }: { statsOnly?: boolean }) {
44
35
  const store = useEditorStore();
45
36
  return (
46
- <div
47
- style={{
48
- position: 'absolute',
49
- top: 8,
50
- right: 8,
51
- display: 'flex',
52
- alignItems: 'center',
53
- gap: 4,
54
- padding: '4px 6px',
55
- background: 'rgba(36, 36, 36, 0.9)',
56
- border: '1px solid #444',
57
- borderRadius: 6,
58
- pointerEvents: 'auto',
59
- backdropFilter: 'blur(8px)',
60
- }}
37
+ <FloatingToolbar
38
+ label="Viewport display"
39
+ className="vgai-viewport-toolbar vgai-viewport-toolbar-right"
61
40
  >
62
- {/* Grid toggle */}
63
- <Tooltip text={`Grid: ${store.showGrid ? 'On' : 'Off'}`}>
64
- <button
65
- type="button"
66
- style={store.showGrid ? activeIconBtn : iconBtn}
67
- onClick={() => store.toggleGrid()}
68
- >
69
- <FontAwesomeIcon icon={faBorderAll} style={{ fontSize: 12 }} />
70
- </button>
71
- </Tooltip>
72
-
73
- {/* Helpers toggle with dropdown */}
74
- <HelpersButton store={store} />
75
-
76
- {/* Shading mode cycle */}
77
- <Tooltip text={`Shading: ${shadingLabels[store.shadingMode]}`}>
78
- <button
79
- type="button"
80
- style={store.shadingMode !== 'solid' ? activeIconBtn : iconBtn}
81
- onClick={() => {
82
- const idx = shadingOrder.indexOf(store.shadingMode);
83
- store.setShadingMode(shadingOrder[(idx + 1) % shadingOrder.length]!);
84
- }}
85
- >
86
- <span style={{ fontSize: 10, fontWeight: 600, letterSpacing: -0.5 }}>
87
- {shadingLabels[store.shadingMode]}
88
- </span>
89
- </button>
90
- </Tooltip>
91
-
92
- {/* Stats toggle */}
93
- <Tooltip text={`Stats: ${store.showStats ? 'On' : 'Off'}`}>
94
- <button
95
- type="button"
96
- style={store.showStats ? activeIconBtn : iconBtn}
41
+ {!statsOnly && (
42
+ <>
43
+ <Tooltip text={`Grid: ${store.showGrid ? 'On' : 'Off'}`}>
44
+ <IconButton
45
+ aria-label="Toggle grid"
46
+ aria-pressed={store.showGrid}
47
+ size="comfortable"
48
+ onClick={() => store.toggleGrid()}
49
+ >
50
+ <EditorIcon icon={faBorderAll} size="md" />
51
+ </IconButton>
52
+ </Tooltip>
53
+ <HelpersButton store={store} />
54
+ <Tooltip text={`Shading: ${shadingLabels[store.shadingMode]}`}>
55
+ <Button
56
+ variant="ghost"
57
+ aria-label={`Shading: ${shadingLabels[store.shadingMode]}`}
58
+ aria-pressed={store.shadingMode !== 'solid'}
59
+ size="comfortable"
60
+ onClick={() => {
61
+ const index = shadingOrder.indexOf(store.shadingMode);
62
+ store.setShadingMode(shadingOrder[(index + 1) % shadingOrder.length]!);
63
+ }}
64
+ >
65
+ <Text variant="label">{shadingLabels[store.shadingMode]}</Text>
66
+ </Button>
67
+ </Tooltip>
68
+ </>
69
+ )}
70
+ <Tooltip text={`Viewport stats: ${store.showStats ? 'On' : 'Off'}`}>
71
+ <IconButton
72
+ aria-label="Toggle viewport stats"
73
+ aria-pressed={store.showStats}
74
+ size="comfortable"
97
75
  onClick={() => store.toggleStats()}
98
76
  >
99
- <FontAwesomeIcon icon={faChartSimple} style={{ fontSize: 12 }} />
100
- </button>
77
+ <EditorIcon icon={faChartSimple} size="md" />
78
+ </IconButton>
101
79
  </Tooltip>
102
- </div>
80
+ </FloatingToolbar>
103
81
  );
104
82
  }
105
83
 
@@ -109,8 +87,8 @@ function HelpersButton({ store }: { store: EditorStore }) {
109
87
 
110
88
  useEffect(() => {
111
89
  if (!open) return;
112
- const handler = (e: MouseEvent) => {
113
- if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
90
+ const handler = (event: MouseEvent) => {
91
+ if (ref.current && !ref.current.contains(event.target as Node)) setOpen(false);
114
92
  };
115
93
  document.addEventListener('mousedown', handler);
116
94
  return () => document.removeEventListener('mousedown', handler);
@@ -123,111 +101,58 @@ function HelpersButton({ store }: { store: EditorStore }) {
123
101
  { key: 'audio' as const, label: 'Audio' },
124
102
  { key: 'splines' as const, label: 'Splines' },
125
103
  { key: 'navmesh' as const, label: 'NavMesh' },
104
+ { key: 'skeletons' as const, label: 'Skeletons' },
126
105
  ];
127
106
 
128
107
  return (
129
- <div ref={ref} style={{ position: 'relative', display: 'flex', alignItems: 'center' }}>
130
- <Tooltip text={`Helpers: ${store.showHelpers ? 'On' : 'Off'}`}>
131
- <button
132
- type="button"
133
- style={store.showHelpers ? activeIconBtn : iconBtn}
134
- onClick={() => store.toggleHelpers()}
108
+ <div ref={ref} className="vgai-viewport-popover-anchor">
109
+ <SplitButtonGroup>
110
+ <Tooltip text={`Helpers: ${store.showHelpers ? 'On' : 'Off'}`}>
111
+ <IconButton
112
+ aria-label="Toggle helpers"
113
+ aria-pressed={store.showHelpers}
114
+ size="comfortable"
115
+ onClick={() => store.toggleHelpers()}
116
+ >
117
+ <EditorIcon icon={store.showHelpers ? faEye : faEyeSlash} size="md" />
118
+ </IconButton>
119
+ </Tooltip>
120
+ <IconButton
121
+ aria-label="Choose helpers"
122
+ aria-expanded={open}
123
+ size="comfortable"
124
+ onClick={() => setOpen(!open)}
135
125
  >
136
- <FontAwesomeIcon icon={store.showHelpers ? faEye : faEyeSlash} style={{ fontSize: 12 }} />
137
- </button>
138
- </Tooltip>
139
- <button
140
- type="button"
141
- onClick={() => setOpen(!open)}
142
- style={{
143
- ...iconBtn,
144
- padding: '4px 3px',
145
- fontSize: 8,
146
- borderLeft: 'none',
147
- borderTopLeftRadius: 0,
148
- borderBottomLeftRadius: 0,
149
- marginLeft: -1,
150
- }}
151
- >
152
- <FontAwesomeIcon icon={faCaretDown} />
153
- </button>
126
+ <EditorIcon icon={faCaretDown} size="xs" />
127
+ </IconButton>
128
+ </SplitButtonGroup>
154
129
  {open && (
155
- <div
156
- style={{
157
- position: 'absolute',
158
- top: '100%',
159
- right: 0,
160
- marginTop: 4,
161
- background: '#2c2c2c',
162
- border: '1px solid #444',
163
- borderRadius: 4,
164
- padding: '4px 0',
165
- zIndex: 1000,
166
- minWidth: 130,
167
- boxShadow: '0 4px 12px rgba(0,0,0,0.5)',
168
- }}
169
- >
170
- <div
171
- role="menuitem"
172
- tabIndex={0}
173
- onClick={() => store.toggleHelpers()}
174
- onKeyDown={(e) => {
175
- if (e.key === 'Enter') store.toggleHelpers();
176
- }}
177
- style={{
178
- padding: '5px 12px',
179
- cursor: 'pointer',
180
- fontSize: 12,
181
- color: '#c5c8ce',
182
- display: 'flex',
183
- alignItems: 'center',
184
- gap: 6,
185
- }}
186
- onMouseEnter={(e) => {
187
- (e.currentTarget as HTMLDivElement).style.background = '#3a3a3a';
188
- }}
189
- onMouseLeave={(e) => {
190
- (e.currentTarget as HTMLDivElement).style.background = 'transparent';
191
- }}
130
+ <Menu className="vgai-helpers-menu" onDismiss={() => setOpen(false)}>
131
+ <MenuItem
132
+ role="menuitemcheckbox"
133
+ aria-checked={store.showHelpers}
134
+ onSelect={() => store.toggleHelpers()}
192
135
  >
193
- <span style={{ opacity: store.showHelpers ? 1 : 0 }}>
194
- <FontAwesomeIcon icon={faCheck} style={{ fontSize: 10 }} />
136
+ <span className="vgai-menu-check">
137
+ {store.showHelpers && <EditorIcon icon={faCheck} size="xs" />}
195
138
  </span>
196
139
  All
197
- </div>
198
- <div style={{ height: 1, background: '#444', margin: '4px 0' }} />
199
- {helperTypes.map((h) => (
200
- <div
201
- role="menuitem"
202
- tabIndex={0}
203
- key={h.key}
204
- onClick={() => store.toggleHelperType(h.key)}
205
- onKeyDown={(e) => {
206
- if (e.key === 'Enter') store.toggleHelperType(h.key);
207
- }}
208
- style={{
209
- padding: '5px 12px',
210
- cursor: 'pointer',
211
- fontSize: 12,
212
- color: '#c5c8ce',
213
- display: 'flex',
214
- alignItems: 'center',
215
- gap: 6,
216
- }}
217
- onMouseEnter={(e) => {
218
- (e.currentTarget as HTMLDivElement).style.background = '#3a3a3a';
219
- }}
220
- onMouseLeave={(e) => {
221
- (e.currentTarget as HTMLDivElement).style.background = 'transparent';
222
- }}
140
+ </MenuItem>
141
+ <MenuSeparator />
142
+ {helperTypes.map((helper) => (
143
+ <MenuItem
144
+ key={helper.key}
145
+ role="menuitemcheckbox"
146
+ aria-checked={store.helperVisibility[helper.key]}
147
+ onSelect={() => store.toggleHelperType(helper.key)}
223
148
  >
224
- <span style={{ opacity: store.helperVisibility[h.key] ? 1 : 0 }}>
225
- <FontAwesomeIcon icon={faCheck} style={{ fontSize: 10 }} />
149
+ <span className="vgai-menu-check">
150
+ {store.helperVisibility[helper.key] && <EditorIcon icon={faCheck} size="xs" />}
226
151
  </span>
227
- {h.label}
228
- </div>
152
+ {helper.label}
153
+ </MenuItem>
229
154
  ))}
230
- </div>
155
+ </Menu>
231
156
  )}
232
157
  </div>
233
158
  );
@@ -3,30 +3,43 @@ import { applyRendererSettings, applyScenePostProcessing } from '@engine/setup/s
3
3
  import { EffectComposer } from 'postprocessing';
4
4
  import { useEffect, useRef, useSyncExternalStore } from 'react';
5
5
  import * as THREE from 'three';
6
+ import { getActiveAuthoring } from '../authoring/active-adapter';
6
7
  import type { CompositeAuthoringAdapter } from '../authoring/composite-authoring-adapter';
7
- import { mountDesignTimeLayers } from '../authoring/design-time-layers';
8
8
  import {
9
9
  EDIT_MODE_REBUILD_EVENT,
10
10
  exitEditModeAuthoring,
11
11
  installEditModeAuthoringForProject,
12
12
  } from '../authoring/edit-mode-authoring';
13
+ import {
14
+ isViewportToolContextVisible,
15
+ resolveViewportToolContext,
16
+ } from '../authoring/viewport-tool-context';
17
+ import {
18
+ isThreejsSurfaceVisible,
19
+ resolveFocusedViewportWorldId,
20
+ } from '../authoring/world-hidden-viewport';
21
+ import { isWorldHidden } from '../authoring/world-session-state';
13
22
  import { collectState } from '../command-listener';
14
- import { useEditorStats, useEditorStore } from '../EditorContext';
23
+ import { themeVars } from '../design-system';
24
+ import {
25
+ useEditorStats,
26
+ useEditorStore,
27
+ useHistoryCommands,
28
+ useHistoryService,
29
+ } from '../EditorContext';
15
30
  import { reportEditorState } from '../editor-api';
16
31
  import { connectServerLogs, editorConsole } from '../editor-console';
17
32
  import { registerEditorHotkeys } from '../editor-hotkeys';
18
33
  import { getEditorMode } from '../editor-mode';
19
34
  import type { EditorStore } from '../editor-store';
20
35
  import { EditorViewport } from '../editor-viewport';
36
+ import { getProjectFileHistory } from '../history/project-file-history';
21
37
  import { autoLaunchIngestFromURL, bindIngestMode } from '../ingest-mode';
22
38
  import { loadInitialScene } from '../initial-scene';
23
39
  import { setupNavMeshHandlers } from '../navmesh-handler';
24
40
  import { bindPlayMode } from '../play-mode';
25
41
  import { resolvePreviewBoneAttachments } from '../scene-sync';
26
42
  import { CameraInfo } from './CameraInfo';
27
- import { CommandPalette } from './CommandPalette';
28
- import { NetworkSection } from './NetworkSection';
29
- import { PlayBar } from './PlayBar';
30
43
  import { StatsOverlay } from './StatsOverlay';
31
44
  import { ToolStrip } from './Toolbar';
32
45
  import { ViewportOverlay } from './ViewportOverlay';
@@ -51,9 +64,22 @@ function buildEntityTagMap(store: EditorStore): Map<string, string[]> {
51
64
  export function ViewportPanel() {
52
65
  const store = useEditorStore();
53
66
  const stats = useEditorStats();
67
+ const history = useHistoryCommands();
68
+ const historyService = useHistoryService();
54
69
  const containerRef = useRef<HTMLDivElement>(null);
55
70
  const canvasRef = useRef<HTMLCanvasElement>(null);
56
71
 
72
+ // Referenced materials are part of the rendered scene even though their
73
+ // bytes live in a separate project resource. Observe the authoritative
74
+ // history stream so writes, undo, and redo all refresh the WYSIWYG preview.
75
+ useEffect(
76
+ () =>
77
+ getProjectFileHistory(historyService).subscribeAll((path) => {
78
+ if (path.split(/[?#]/, 1)[0]!.endsWith('.mat.json')) store.refreshMaterialAssets();
79
+ }),
80
+ [historyService, store],
81
+ );
82
+
57
83
  useEffect(() => {
58
84
  const canvas = canvasRef.current!;
59
85
  const container = containerRef.current!;
@@ -82,7 +108,9 @@ export function ViewportPanel() {
82
108
  store.bindScene(scene, renderer);
83
109
 
84
110
  // --- Viewport ---
85
- const viewport = new EditorViewport(canvas, scene, store);
111
+ // Bind navigation to the whole authoring surface. A DOM selection layer
112
+ // above the WebGL canvas must not disable right-drag orbit/fly controls.
113
+ const viewport = new EditorViewport(canvas, scene, store, container);
86
114
  // Bind the REAL viewport camera + orbit target so store.cameraPose (the
87
115
  // `editor.viewport.camera.get` facet, see command-listener.ts's
88
116
  // collectState) and the thumbnail-capture path read live data instead of
@@ -106,12 +134,19 @@ export function ViewportPanel() {
106
134
  store.environment?.rendering as RenderScope | undefined,
107
135
  window.devicePixelRatio,
108
136
  );
137
+ // World-hidden eye: while the focused threejs world is hidden, its
138
+ // authored post chain (bloom/vignette/…) must not render either — pass
139
+ // no environment so the composer builds bare. `editor-viewport.ts`
140
+ // bumps `composerVersion` (via `store.reapplyEnvironment()`) on both
141
+ // hide/show edges, which is what re-runs this rebuild.
142
+ const hiddenWorldId = resolveFocusedViewportWorldId(store);
143
+ const worldHidden = hiddenWorldId !== null && isWorldHidden(hiddenWorldId);
109
144
  applyScenePostProcessing(
110
145
  composer,
111
146
  renderer,
112
147
  store.scene ?? scene,
113
148
  viewport.camera,
114
- store.environment,
149
+ worldHidden ? undefined : store.environment,
115
150
  store.objectMap,
116
151
  buildEntityTagMap(store),
117
152
  );
@@ -163,7 +198,7 @@ export function ViewportPanel() {
163
198
  });
164
199
 
165
200
  // --- Hotkeys ---
166
- const unregisterHotkeys = registerEditorHotkeys(store, viewport, canvas);
201
+ const unregisterHotkeys = registerEditorHotkeys(store, viewport, canvas, history);
167
202
 
168
203
  // --- NavMesh ---
169
204
  const disposeNavMesh = setupNavMeshHandlers(store, viewport, scene);
@@ -246,13 +281,13 @@ export function ViewportPanel() {
246
281
  // override behind).
247
282
  let disposed = false;
248
283
  let editModeComposite: CompositeAuthoringAdapter | undefined;
249
- let disposeDesignTimeLayers: (() => void) | undefined;
284
+ let disposeWorldDocuments: (() => void) | undefined;
250
285
 
251
286
  /**
252
287
  * (A1) fetch `vgai.game.json` and install the edit-mode composite over
253
- * every declared world, then (B1) composite every OTHER declared world's
254
- * design-time layer into THIS SAME container (the threejs canvas above
255
- * keeps its existing slot, untouched). Callable more than once: the
288
+ * every declared world, then register a distinct center document for
289
+ * every non-Three authoring surface. This component keeps the native
290
+ * Three renderer; only Game composes roots. Callable more than once: the
256
291
  * initial mount (below) AND (C3) the `'editor:edit-mode-rebuild'`
257
292
  * listener — e.g. after the "Add UI Canvas" hierarchy-menu action seeds
258
293
  * the scene's first `ui` root, the composite needs rebuilding so its new
@@ -260,8 +295,8 @@ export function ViewportPanel() {
260
295
  * first, so a rebuild never leaks the old composite/layers.
261
296
  */
262
297
  async function installAll(): Promise<void> {
263
- disposeDesignTimeLayers?.();
264
- disposeDesignTimeLayers = undefined;
298
+ disposeWorldDocuments?.();
299
+ disposeWorldDocuments = undefined;
265
300
  if (editModeComposite) {
266
301
  exitEditModeAuthoring(editModeComposite);
267
302
  editModeComposite = undefined;
@@ -273,7 +308,15 @@ export function ViewportPanel() {
273
308
  editModeComposite = undefined;
274
309
  return;
275
310
  }
276
- disposeDesignTimeLayers = mountDesignTimeLayers(container, composite, store);
311
+ const { installWorldDocuments } = await import('./world-documents');
312
+ if (disposed || editModeComposite !== composite) {
313
+ if (editModeComposite === composite) {
314
+ exitEditModeAuthoring(composite);
315
+ editModeComposite = undefined;
316
+ }
317
+ return;
318
+ }
319
+ disposeWorldDocuments = installWorldDocuments(store, composite);
277
320
  }
278
321
 
279
322
  function onEditModeRebuild(): void {
@@ -313,7 +356,7 @@ export function ViewportPanel() {
313
356
  unregisterHotkeys();
314
357
  disposeNavMesh();
315
358
  window.removeEventListener(EDIT_MODE_REBUILD_EVENT, onEditModeRebuild);
316
- disposeDesignTimeLayers?.();
359
+ disposeWorldDocuments?.();
317
360
  exitEditModeAuthoring(editModeComposite);
318
361
  window.removeEventListener('error', onError);
319
362
  window.removeEventListener('unhandledrejection', onUnhandledRejection);
@@ -323,15 +366,36 @@ export function ViewportPanel() {
323
366
  // render targets on unmount (incl. React StrictMode's mount/unmount/remount).
324
367
  composer.dispose();
325
368
  renderer.dispose();
369
+ // `dispose()` clears Three's GPU resources but intentionally keeps the
370
+ // browser WebGL context alive. Fast Refresh can remount this panel many
371
+ // times in one tab; explicitly release the old context so a later mount
372
+ // cannot fail with "Error creating WebGL context" after the browser's
373
+ // per-process context budget is exhausted.
374
+ renderer.forceContextLoss();
326
375
  };
327
376
  }, []); // eslint-disable-line react-hooks/exhaustive-deps -- runs once on mount
328
377
 
329
378
  // Subscribe to store for overlay visibility
330
379
  useSyncExternalStore(store.subscribe, store.getSnapshot);
331
380
 
381
+ // Spec 28 — figma mode's backdrop is the STANDARD transparency
382
+ // checkerboard, not a flat color: with no threejs surface showing, the
383
+ // area behind the DOM roots isn't "a gray scene", it's NOTHING, and the
384
+ // checkerboard is the universal signal for that. Achieved by fading the
385
+ // WebGL canvas to opacity 0 (keeps layout AND pointer flow identical —
386
+ // the layered-pick path still runs) so the container's own checkerboard
387
+ // shows through behind the DOM world layers appended above it.
388
+ const threejsSurface = isThreejsSurfaceVisible(store);
389
+ const toolContext = resolveViewportToolContext(
390
+ getActiveAuthoring(store),
391
+ store.selectedEntityIds,
392
+ );
393
+ const toolContextVisible = isViewportToolContextVisible(store, toolContext);
394
+ const showThreejsTools = threejsSurface && toolContextVisible && toolContext?.kind === 'threejs';
332
395
  return (
333
396
  <div
334
397
  ref={containerRef}
398
+ data-testid="viewport-panel"
335
399
  style={{
336
400
  position: 'absolute',
337
401
  top: 0,
@@ -340,6 +404,23 @@ export function ViewportPanel() {
340
404
  height: '100%',
341
405
  overflow: 'hidden',
342
406
  pointerEvents: 'auto',
407
+ ...(threejsSurface
408
+ ? {}
409
+ : {
410
+ // Dark-UI transparency checkerboard (VS Code image-preview
411
+ // style — the light Figma checker glares in a dark editor and
412
+ // washes out light-on-transparent HUD content). V-20 — the
413
+ // PATTERN itself is a deliberate, documented design choice
414
+ // (left alone); `themeVars.surface.raised` is an exact-value token match for the
415
+ // base tone, so swapped in. The gradient's `#444649` has no
416
+ // exact token in the current set, so it stays a literal per
417
+ // the audit's own "only swap exact matches" guidance.
418
+ backgroundColor: themeVars.surface.raised,
419
+ backgroundImage:
420
+ 'linear-gradient(45deg, #444649 25%, transparent 25%, transparent 75%, #444649 75%), linear-gradient(45deg, #444649 25%, transparent 25%, transparent 75%, #444649 75%)',
421
+ backgroundPosition: '0 0, 8px 8px',
422
+ backgroundSize: '16px 16px',
423
+ }),
343
424
  }}
344
425
  >
345
426
  <canvas
@@ -347,23 +428,41 @@ export function ViewportPanel() {
347
428
  data-testid="editor-canvas"
348
429
  aria-hidden="true"
349
430
  tabIndex={-1}
350
- style={{ width: '100%', height: '100%', display: 'block', pointerEvents: 'auto' }}
431
+ style={{
432
+ width: '100%',
433
+ height: '100%',
434
+ display: 'block',
435
+ pointerEvents: 'auto',
436
+ opacity: threejsSurface ? 1 : 0,
437
+ }}
351
438
  />
352
- {/* Spec 27 §3 A3 — mounted as a CHILD of THIS SAME containerRef div, the exact
353
- DOM parent `mountDesignTimeLayers(container, …)` above appends each react/pixi
354
- world's own `position:absolute; inset:0` layer into, so `RectProvider.rect(id)`'s
355
- host-relative geometry lines up with this overlay's own coordinate space with no
356
- correction (see WorldSelectionOverlay.tsx's own doc comment). Self-gates to a
357
- no-op when the active composite has no rect-capable (DOM) child, so a 3D-only
358
- project renders nothing extra here. */}
439
+ {/* Three-scene selection overlay. React/Pixi documents mount their own
440
+ co-located overlay beside the isolated world host. */}
359
441
  <WorldSelectionOverlay />
360
- <PlayBar />
361
- <ToolStrip />
362
- <ViewportOverlay />
363
- {store.showStats && <StatsOverlay />}
364
- {store.playState !== 'stopped' && <NetworkSection />}
365
- <CameraInfo />
366
- {store.commandPaletteOpen && <CommandPalette onClose={() => store.closeCommandPalette()} />}
442
+ {/* W4 (inventory row V4): the PlayBar copy that floated here moved to
443
+ the global command header (`ProjectHeader.tsx`) — transport is now
444
+ document-independent. */}
445
+ {/* Spec 28 — the 3D cockpit (transform toolstrip, grid/helpers/shading
446
+ controls, camera Pos/Target readout) gates on a threejs surface
447
+ actually SHOWING, exactly as WorldSelectionOverlay above self-gates
448
+ on a DOM child existing. With every 3D world hidden the viewport is
449
+ a DOM authoring surface and presents as one. Re-evaluated on every
450
+ notify via this component's useSyncExternalStore subscription. */}
451
+ {showThreejsTools ? (
452
+ <>
453
+ <ToolStrip />
454
+ <ViewportOverlay />
455
+ <CameraInfo />
456
+ </>
457
+ ) : threejsSurface ? (
458
+ <ViewportOverlay statsOnly />
459
+ ) : null}
460
+ {threejsSurface && store.showStats && <StatsOverlay />}
461
+ {/* W4 (inventory row V9): the duplicated play-time NetworkSection
462
+ overlay is gone from the scene document — compact connection state
463
+ lives in the status bar (`status-contributions.tsx`); the detailed
464
+ overlay stays contextual in the Game document (`CenterDocuments`'s
465
+ GamePanel), where play actually renders. */}
367
466
  </div>
368
467
  );
369
468
  }