@vgai/editor 0.2.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 (519) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +35 -0
  3. package/dist/a/b/c/test-nested.json +1 -0
  4. package/dist/assets/CCDIKSolver-Dz-ysxZF.js +1 -0
  5. package/dist/assets/SelectionBox-BXsv_6aH.js +1 -0
  6. package/dist/assets/browser-project-scripts-DOru4L1x.js +2 -0
  7. package/dist/assets/browserAll-CEkWYlFk.js +1 -0
  8. package/dist/assets/collision-world-DniMB0qq.glb +0 -0
  9. package/dist/assets/create-runtime-DYIWr3va.js +1 -0
  10. package/dist/assets/data-asset-Bb9sIM7i.js +5 -0
  11. package/dist/assets/esbuild-BHljloGq.wasm +0 -0
  12. package/dist/assets/fake-three-BMTQ9rl_.js +1 -0
  13. package/dist/assets/game-8SDZPPgH.js +1 -0
  14. package/dist/assets/game-BI111o5f.js +10 -0
  15. package/dist/assets/game-CevorxyV.js +1 -0
  16. package/dist/assets/game-Di1H0gFj.js +1 -0
  17. package/dist/assets/game-Di7XlywD.js +1 -0
  18. package/dist/assets/game-DjnCaw8R.js +1 -0
  19. package/dist/assets/game-Q9A0aXSV.js +1 -0
  20. package/dist/assets/game-pt_1lQLv.js +1 -0
  21. package/dist/assets/game-source-CvidN7AB.js +1 -0
  22. package/dist/assets/game-xsOho_wg.js +83 -0
  23. package/dist/assets/index-Bp5cecD6.js +12 -0
  24. package/dist/assets/index-CPVV6pjw.js +1 -0
  25. package/dist/assets/index-CpOnYyeD.js +213 -0
  26. package/dist/assets/index-Cs8xwv3e.js +1 -0
  27. package/dist/assets/index-DL7RcQ0n.js +27 -0
  28. package/dist/assets/index-Dr8H_6Rl.js +1 -0
  29. package/dist/assets/index-_bCaipUk.js +13650 -0
  30. package/dist/assets/ingest-siblings-Dz8eEqrV.js +1 -0
  31. package/dist/assets/mount-manifest-Bhk0ohsl.js +1 -0
  32. package/dist/assets/recast-navigation.wasm-compat-CvP4UGK6.js +1 -0
  33. package/dist/assets/scene-query-BivAHhJu.js +1 -0
  34. package/dist/assets/stats.module--VATS4Kh.js +1 -0
  35. package/dist/assets/webworkerAll-BLrH0F6F.js +1 -0
  36. package/dist/assets/xstate-animation-binding-1NGLmw6y.js +1 -0
  37. package/dist/assets/xstate.esm-BgUD33nl.js +3 -0
  38. package/dist/index.html +37 -0
  39. package/dist/ingest/racing-game/UPSTREAM.md +68 -0
  40. package/dist/ingest/racing-game/a/Inter-Black.woff +0 -0
  41. package/dist/ingest/racing-game/a/Inter-Black.woff2 +0 -0
  42. package/dist/ingest/racing-game/a/Inter-BlackItalic.woff +0 -0
  43. package/dist/ingest/racing-game/a/Inter-BlackItalic.woff2 +0 -0
  44. package/dist/ingest/racing-game/a/Inter-Bold.woff +0 -0
  45. package/dist/ingest/racing-game/a/Inter-Bold.woff2 +0 -0
  46. package/dist/ingest/racing-game/a/Inter-BoldItalic.woff +0 -0
  47. package/dist/ingest/racing-game/a/Inter-BoldItalic.woff2 +0 -0
  48. package/dist/ingest/racing-game/a/Inter-ExtraBold.woff +0 -0
  49. package/dist/ingest/racing-game/a/Inter-ExtraBold.woff2 +0 -0
  50. package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff +0 -0
  51. package/dist/ingest/racing-game/a/Inter-ExtraBoldItalic.woff2 +0 -0
  52. package/dist/ingest/racing-game/a/Inter-ExtraLight.woff +0 -0
  53. package/dist/ingest/racing-game/a/Inter-ExtraLight.woff2 +0 -0
  54. package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff +0 -0
  55. package/dist/ingest/racing-game/a/Inter-ExtraLightItalic.woff2 +0 -0
  56. package/dist/ingest/racing-game/a/Inter-Italic.woff +0 -0
  57. package/dist/ingest/racing-game/a/Inter-Italic.woff2 +0 -0
  58. package/dist/ingest/racing-game/a/Inter-Light.woff +0 -0
  59. package/dist/ingest/racing-game/a/Inter-Light.woff2 +0 -0
  60. package/dist/ingest/racing-game/a/Inter-LightItalic.woff +0 -0
  61. package/dist/ingest/racing-game/a/Inter-LightItalic.woff2 +0 -0
  62. package/dist/ingest/racing-game/a/Inter-Medium.woff +0 -0
  63. package/dist/ingest/racing-game/a/Inter-Medium.woff2 +0 -0
  64. package/dist/ingest/racing-game/a/Inter-MediumItalic.woff +0 -0
  65. package/dist/ingest/racing-game/a/Inter-MediumItalic.woff2 +0 -0
  66. package/dist/ingest/racing-game/a/Inter-Regular.woff +0 -0
  67. package/dist/ingest/racing-game/a/Inter-Regular.woff2 +0 -0
  68. package/dist/ingest/racing-game/a/Inter-SemiBold.woff +0 -0
  69. package/dist/ingest/racing-game/a/Inter-SemiBold.woff2 +0 -0
  70. package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff +0 -0
  71. package/dist/ingest/racing-game/a/Inter-SemiBoldItalic.woff2 +0 -0
  72. package/dist/ingest/racing-game/a/Inter-Thin.woff +0 -0
  73. package/dist/ingest/racing-game/a/Inter-Thin.woff2 +0 -0
  74. package/dist/ingest/racing-game/a/Inter-ThinItalic.woff +0 -0
  75. package/dist/ingest/racing-game/a/Inter-ThinItalic.woff2 +0 -0
  76. package/dist/ingest/racing-game/a/Inter-italic.var.woff2 +0 -0
  77. package/dist/ingest/racing-game/a/Inter-roman.var.woff2 +0 -0
  78. package/dist/ingest/racing-game/a/Inter.var.woff2 +0 -0
  79. package/dist/ingest/racing-game/a/index.css +1 -0
  80. package/dist/ingest/racing-game/images/bronze.png +0 -0
  81. package/dist/ingest/racing-game/images/gold.png +0 -0
  82. package/dist/ingest/racing-game/images/silver.png +0 -0
  83. package/dist/ingest/racing-game/index.html +15 -0
  84. package/dist/ingest/racing-game/models/chassis-draco.glb +0 -0
  85. package/dist/ingest/racing-game/models/track-draco.glb +0 -0
  86. package/dist/ingest/racing-game/models/wheel-draco.glb +0 -0
  87. package/dist/ingest/racing-game/rg-entry.js +1 -0
  88. package/dist/ingest/racing-game/sounds/accelerate.mp3 +0 -0
  89. package/dist/ingest/racing-game/sounds/boost.mp3 +0 -0
  90. package/dist/ingest/racing-game/sounds/crash.mp3 +0 -0
  91. package/dist/ingest/racing-game/sounds/engine.mp3 +0 -0
  92. package/dist/ingest/racing-game/sounds/honk.mp3 +0 -0
  93. package/dist/ingest/racing-game/sounds/tire-brake.mp3 +0 -0
  94. package/dist/ingest/racing-game/sounds/train.mp3 +0 -0
  95. package/dist/ingest/racing-game/sounds/water.mp3 +0 -0
  96. package/dist/ingest/racing-game/textures/cursor.svg +3 -0
  97. package/dist/ingest/racing-game/textures/dikhololo_night_1k.hdr +0 -0
  98. package/dist/ingest/racing-game/textures/heightmap_1024.png +0 -0
  99. package/dist/ingest/racing-game/textures/mask.svg +11 -0
  100. package/dist/ingest/racing-game/textures/no_sound.png +0 -0
  101. package/dist/ingest/racing-game/three-r139.module.js +50617 -0
  102. package/dist/ingest/racing-game/vendor.js +179 -0
  103. package/dist/ingest/racing-game/vgai.game.json +20 -0
  104. package/dist/ingest/simcity/UPSTREAM.md +65 -0
  105. package/dist/ingest/simcity/favicon.ico +0 -0
  106. package/dist/ingest/simcity/fonts/Bebas-Regular.otf +0 -0
  107. package/dist/ingest/simcity/fonts/Digitalt.otf +0 -0
  108. package/dist/ingest/simcity/fonts/Stanberry.ttf +0 -0
  109. package/dist/ingest/simcity/icons/bulldozer-color.png +0 -0
  110. package/dist/ingest/simcity/icons/bulldozer.png +0 -0
  111. package/dist/ingest/simcity/icons/calendar.png +0 -0
  112. package/dist/ingest/simcity/icons/factory-color.png +0 -0
  113. package/dist/ingest/simcity/icons/factory.png +0 -0
  114. package/dist/ingest/simcity/icons/house-color.png +0 -0
  115. package/dist/ingest/simcity/icons/house.png +0 -0
  116. package/dist/ingest/simcity/icons/job.png +0 -0
  117. package/dist/ingest/simcity/icons/office.png +0 -0
  118. package/dist/ingest/simcity/icons/pause-color.png +0 -0
  119. package/dist/ingest/simcity/icons/pause.png +0 -0
  120. package/dist/ingest/simcity/icons/person.png +0 -0
  121. package/dist/ingest/simcity/icons/play-color.png +0 -0
  122. package/dist/ingest/simcity/icons/play.png +0 -0
  123. package/dist/ingest/simcity/icons/power-color.png +0 -0
  124. package/dist/ingest/simcity/icons/power-line-color.png +0 -0
  125. package/dist/ingest/simcity/icons/power.png +0 -0
  126. package/dist/ingest/simcity/icons/road-color.png +0 -0
  127. package/dist/ingest/simcity/icons/road.png +0 -0
  128. package/dist/ingest/simcity/icons/select-color.png +0 -0
  129. package/dist/ingest/simcity/icons/select.png +0 -0
  130. package/dist/ingest/simcity/icons/store-color.png +0 -0
  131. package/dist/ingest/simcity/index.html +94 -0
  132. package/dist/ingest/simcity/index.js +58 -0
  133. package/dist/ingest/simcity/main.css +233 -0
  134. package/dist/ingest/simcity/models/armored-truck.glb +0 -0
  135. package/dist/ingest/simcity/models/building-block-4floor-corner.glb +0 -0
  136. package/dist/ingest/simcity/models/building-block-4floor-short.glb +0 -0
  137. package/dist/ingest/simcity/models/building-block-5floor.glb +0 -0
  138. package/dist/ingest/simcity/models/building-burger-joint.glb +0 -0
  139. package/dist/ingest/simcity/models/building-cafe.glb +0 -0
  140. package/dist/ingest/simcity/models/building-casino.glb +0 -0
  141. package/dist/ingest/simcity/models/building-cinema.glb +0 -0
  142. package/dist/ingest/simcity/models/building-house-block-big.glb +0 -0
  143. package/dist/ingest/simcity/models/building-house-family-large.glb +0 -0
  144. package/dist/ingest/simcity/models/building-house-family-small.glb +0 -0
  145. package/dist/ingest/simcity/models/building-office-balcony.glb +0 -0
  146. package/dist/ingest/simcity/models/building-office-big.glb +0 -0
  147. package/dist/ingest/simcity/models/building-office-pyramid.glb +0 -0
  148. package/dist/ingest/simcity/models/building-office-tall.glb +0 -0
  149. package/dist/ingest/simcity/models/building-office.glb +0 -0
  150. package/dist/ingest/simcity/models/building-restaurant.glb +0 -0
  151. package/dist/ingest/simcity/models/building-skyscraper.glb +0 -0
  152. package/dist/ingest/simcity/models/car-ambulance-pickup.glb +0 -0
  153. package/dist/ingest/simcity/models/car-baywatch.glb +0 -0
  154. package/dist/ingest/simcity/models/car-hippie-van.glb +0 -0
  155. package/dist/ingest/simcity/models/car-passenger-race.glb +0 -0
  156. package/dist/ingest/simcity/models/car-passenger.glb +0 -0
  157. package/dist/ingest/simcity/models/car-police.glb +0 -0
  158. package/dist/ingest/simcity/models/car-taxi.glb +0 -0
  159. package/dist/ingest/simcity/models/car-tow-truck.glb +0 -0
  160. package/dist/ingest/simcity/models/car-truck-dump.glb +0 -0
  161. package/dist/ingest/simcity/models/car-veteran.glb +0 -0
  162. package/dist/ingest/simcity/models/construction-small.glb +0 -0
  163. package/dist/ingest/simcity/models/data-center.glb +0 -0
  164. package/dist/ingest/simcity/models/industry-factory-old.glb +0 -0
  165. package/dist/ingest/simcity/models/industry-factory.glb +0 -0
  166. package/dist/ingest/simcity/models/industry-refinery.glb +0 -0
  167. package/dist/ingest/simcity/models/industry-warehouse.glb +0 -0
  168. package/dist/ingest/simcity/models/power_line_pole_modified.glb +0 -0
  169. package/dist/ingest/simcity/models/tile-plain_grass.glb +0 -0
  170. package/dist/ingest/simcity/models/tile-road-curve.glb +0 -0
  171. package/dist/ingest/simcity/models/tile-road-end.glb +0 -0
  172. package/dist/ingest/simcity/models/tile-road-intersection-t.glb +0 -0
  173. package/dist/ingest/simcity/models/tile-road-intersection.glb +0 -0
  174. package/dist/ingest/simcity/models/tile-road-straight.glb +0 -0
  175. package/dist/ingest/simcity/models/truck.glb +0 -0
  176. package/dist/ingest/simcity/statusIcons/no-power.png +0 -0
  177. package/dist/ingest/simcity/statusIcons/no-road-access.png +0 -0
  178. package/dist/ingest/simcity/textures/base.png +0 -0
  179. package/dist/ingest/simcity/textures/grid.png +0 -0
  180. package/dist/ingest/simcity/textures/specular.png +0 -0
  181. package/dist/ingest/simcity/three-r155.module.js +51466 -0
  182. package/dist/ingest/simcity/vgai.game.json +20 -0
  183. package/dist/ingest/tanks/UPSTREAM.md +69 -0
  184. package/dist/ingest/tanks/index.html +214 -0
  185. package/dist/ingest/tanks/models/pixelTank.fbx +0 -0
  186. package/dist/ingest/tanks/tanks-entry.js +92 -0
  187. package/dist/ingest/tanks/three-r170.module.js +54571 -0
  188. package/dist/ingest/tanks/vgai.game.json +20 -0
  189. package/dist/inputmaps/default.inputmap.json +41 -0
  190. package/dist/jsm/libs/draco/gltf/draco_decoder.js +33 -0
  191. package/dist/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
  192. package/dist/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
  193. package/dist/scenes/untitled.vscn.json +29 -0
  194. package/dist/textures/waternormals.jpg +0 -0
  195. package/dist-server/packaged.mjs +18155 -0
  196. package/package.json +36 -0
  197. package/server/asset-library-routes.ts +833 -0
  198. package/server/canonical-path.ts +50 -0
  199. package/server/cloud-asset-catalog.ts +101 -0
  200. package/server/dev.ts +350 -0
  201. package/server/editor-server.ts +1537 -0
  202. package/server/editor-sse.ts +73 -0
  203. package/server/game-globals-shadow.ts +58 -0
  204. package/server/local-asset-catalog.ts +163 -0
  205. package/server/packaged.ts +571 -0
  206. package/server/prod.ts +75 -0
  207. package/server/project-optimize-deps-entries.ts +149 -0
  208. package/server/project-view.ts +65 -0
  209. package/server/server-utils.ts +430 -0
  210. package/server/session-registry.ts +93 -0
  211. package/src/EditorContext.tsx +87 -0
  212. package/src/achieved-tier.ts +126 -0
  213. package/src/action-registry.ts +268 -0
  214. package/src/adapter-resolver.ts +1858 -0
  215. package/src/asset-events.ts +100 -0
  216. package/src/authoring/active-adapter.ts +98 -0
  217. package/src/authoring/active-systems.ts +50 -0
  218. package/src/authoring/boundary-authoring-adapter.ts +178 -0
  219. package/src/authoring/composite-authoring-adapter.ts +891 -0
  220. package/src/authoring/design-time-layers.ts +776 -0
  221. package/src/authoring/edit-mode-authoring.ts +643 -0
  222. package/src/authoring/embed-only-adapter.ts +57 -0
  223. package/src/authoring/ephemeral-persistence.ts +26 -0
  224. package/src/authoring/eyedropper-session.ts +69 -0
  225. package/src/authoring/first-party-hierarchy-menu.ts +74 -0
  226. package/src/authoring/first-party-inspector/AddSectionMenu.tsx +118 -0
  227. package/src/authoring/first-party-inspector/EnvironmentSection.tsx +550 -0
  228. package/src/authoring/first-party-inspector/HeaderSection.tsx +329 -0
  229. package/src/authoring/first-party-inspector/is-first-party-selection.ts +44 -0
  230. package/src/authoring/first-party-inspector/register.tsx +108 -0
  231. package/src/authoring/ingest-authoring-adapter.ts +475 -0
  232. package/src/authoring/ingest-capture.ts +94 -0
  233. package/src/authoring/ingest-game-adapter.ts +291 -0
  234. package/src/authoring/ingest-iframe-adapter.ts +38 -0
  235. package/src/authoring/ingest-iframe-reachable-adapter.ts +401 -0
  236. package/src/authoring/ingest-overlay.ts +193 -0
  237. package/src/authoring/layered-pick.ts +87 -0
  238. package/src/authoring/mount-failure-report.ts +112 -0
  239. package/src/authoring/overlay-report.ts +52 -0
  240. package/src/authoring/project-scenes.ts +111 -0
  241. package/src/authoring/react-dom-authoring-adapter.ts +795 -0
  242. package/src/authoring/react-inspector-section.tsx +1041 -0
  243. package/src/authoring/react-world-authoring-adapter.ts +2400 -0
  244. package/src/authoring/root-activation.ts +61 -0
  245. package/src/authoring/scene-ui-layer.ts +485 -0
  246. package/src/authoring/story-overlay.ts +48 -0
  247. package/src/authoring/ui-authoring-adapter.ts +325 -0
  248. package/src/authoring/ui-hierarchy-menu.ts +40 -0
  249. package/src/authoring/ui-inspector-sections.tsx +156 -0
  250. package/src/authoring/undo-timeline.ts +145 -0
  251. package/src/authoring/vgai-scene-authoring-adapter.ts +746 -0
  252. package/src/authoring/viewport-pick-context.ts +31 -0
  253. package/src/authoring/viewport-raycast.ts +80 -0
  254. package/src/authoring/world-overlay.ts +76 -0
  255. package/src/authoring/world-pan-state.ts +115 -0
  256. package/src/authoring/world-session-state.ts +102 -0
  257. package/src/authoring/world2d-authoring-adapter.ts +313 -0
  258. package/src/browser-external-watch.ts +98 -0
  259. package/src/browser-play-entry.ts +112 -0
  260. package/src/browser-project-scripts.ts +112 -0
  261. package/src/browser-transpile.ts +246 -0
  262. package/src/browser-watch-reload.ts +100 -0
  263. package/src/command-listener.ts +326 -0
  264. package/src/components/AlignToolbar.tsx +189 -0
  265. package/src/components/AppRoot.tsx +73 -0
  266. package/src/components/AssetBrowser.tsx +502 -0
  267. package/src/components/BottomPanel.tsx +326 -0
  268. package/src/components/BuildPanel.tsx +455 -0
  269. package/src/components/CameraInfo.tsx +56 -0
  270. package/src/components/CommandPalette.tsx +265 -0
  271. package/src/components/ConsolePanel.tsx +272 -0
  272. package/src/components/DataPanel.tsx +1268 -0
  273. package/src/components/DefaultEditorLayout.tsx +509 -0
  274. package/src/components/FolderTree.tsx +179 -0
  275. package/src/components/GameHierarchy.tsx +1349 -0
  276. package/src/components/Inspector.tsx +671 -0
  277. package/src/components/InspectorToolSection.tsx +74 -0
  278. package/src/components/MountFailureBanner.tsx +110 -0
  279. package/src/components/NetworkSection.tsx +109 -0
  280. package/src/components/OnlineAssetBrowser.tsx +448 -0
  281. package/src/components/OverlayReportBanner.tsx +122 -0
  282. package/src/components/PlayBar.tsx +356 -0
  283. package/src/components/PrefabSaveDialog.tsx +201 -0
  284. package/src/components/ProjectHeader.tsx +174 -0
  285. package/src/components/ProjectScreen.tsx +1004 -0
  286. package/src/components/RightPanel.tsx +166 -0
  287. package/src/components/RootActivateDialog.tsx +105 -0
  288. package/src/components/SaveDialog.tsx +197 -0
  289. package/src/components/SaveStatus.tsx +69 -0
  290. package/src/components/StatsOverlay.tsx +35 -0
  291. package/src/components/ToolHost.tsx +80 -0
  292. package/src/components/Toolbar.tsx +307 -0
  293. package/src/components/ViewportOverlay.tsx +234 -0
  294. package/src/components/ViewportPanel.tsx +369 -0
  295. package/src/components/WorldContextMenu.tsx +257 -0
  296. package/src/components/WorldSelectionOverlay.tsx +2003 -0
  297. package/src/components/WorldTextEditor.tsx +86 -0
  298. package/src/components/asset-viewers/AssetViewer.tsx +38 -0
  299. package/src/components/asset-viewers/AudioViewer.tsx +149 -0
  300. package/src/components/asset-viewers/GenericJsonViewer.tsx +77 -0
  301. package/src/components/asset-viewers/ImageViewer.tsx +65 -0
  302. package/src/components/asset-viewers/InputMapViewer.tsx +206 -0
  303. package/src/components/asset-viewers/ModelViewer.tsx +180 -0
  304. package/src/components/asset-viewers/OnlineAssetDetail.tsx +236 -0
  305. package/src/components/data-csv.ts +301 -0
  306. package/src/components/entity-name-commit.ts +18 -0
  307. package/src/components/inspector-property-grouping.ts +65 -0
  308. package/src/components/inspector-widgets/AlignmentGrid.tsx +188 -0
  309. package/src/components/inspector-widgets/BorderEditor.tsx +305 -0
  310. package/src/components/inspector-widgets/ColorPicker.tsx +548 -0
  311. package/src/components/inspector-widgets/FilterEditor.tsx +108 -0
  312. package/src/components/inspector-widgets/FontPicker.tsx +196 -0
  313. package/src/components/inspector-widgets/GradientEditor.tsx +277 -0
  314. package/src/components/inspector-widgets/ScrubbableInput.tsx +152 -0
  315. package/src/components/inspector-widgets/ShadowEditor.tsx +332 -0
  316. package/src/components/inspector-widgets/color-utils.ts +202 -0
  317. package/src/components/inspector-widgets/gallery-main.tsx +167 -0
  318. package/src/components/inspector-widgets/index.ts +22 -0
  319. package/src/components/inspector-widgets/shared.tsx +135 -0
  320. package/src/components/inspectors/AnimationSection.tsx +143 -0
  321. package/src/components/inspectors/AudioSection.tsx +215 -0
  322. package/src/components/inspectors/BoneAttachmentSection.tsx +82 -0
  323. package/src/components/inspectors/CameraSection.tsx +105 -0
  324. package/src/components/inspectors/ComponentsSection.tsx +358 -0
  325. package/src/components/inspectors/JointsSection.tsx +255 -0
  326. package/src/components/inspectors/LightSection.tsx +116 -0
  327. package/src/components/inspectors/MaterialSection.tsx +1189 -0
  328. package/src/components/inspectors/MeshSection.tsx +99 -0
  329. package/src/components/inspectors/NavigationSection.tsx +58 -0
  330. package/src/components/inspectors/ParticleSection.tsx +681 -0
  331. package/src/components/inspectors/PhysicsSection.tsx +326 -0
  332. package/src/components/inspectors/PostProcessingSection.tsx +1290 -0
  333. package/src/components/inspectors/RenderingSection.tsx +109 -0
  334. package/src/components/inspectors/ShadowSection.tsx +84 -0
  335. package/src/components/inspectors/SplineSection.tsx +191 -0
  336. package/src/components/inspectors/TransformSection.tsx +191 -0
  337. package/src/components/inspectors/json-schema-fields.ts +281 -0
  338. package/src/components/inspectors/multi-edit-utils.ts +49 -0
  339. package/src/components/inspectors/zod-fields.ts +184 -0
  340. package/src/components/primitives/ColorInput.tsx +109 -0
  341. package/src/components/primitives/JsonInput.tsx +98 -0
  342. package/src/components/primitives/NumberInput.tsx +164 -0
  343. package/src/components/primitives/Panel.tsx +68 -0
  344. package/src/components/primitives/SectionHeader.tsx +45 -0
  345. package/src/components/primitives/Tooltip.tsx +82 -0
  346. package/src/components/primitives/Vec3Input.tsx +62 -0
  347. package/src/components/world-overlay-gestures.ts +1438 -0
  348. package/src/doctor/data-checks.ts +285 -0
  349. package/src/doctor/report.ts +386 -0
  350. package/src/editor-api.ts +700 -0
  351. package/src/editor-console.ts +194 -0
  352. package/src/editor-hotkeys.ts +441 -0
  353. package/src/editor-layers.ts +2 -0
  354. package/src/editor-mode.ts +28 -0
  355. package/src/editor-store.ts +2225 -0
  356. package/src/editor-viewport.ts +1924 -0
  357. package/src/entity-factory.ts +221 -0
  358. package/src/game-globals-prelude.ts +27 -0
  359. package/src/gated-globals.ts +131 -0
  360. package/src/generated/setup-by-example.generated.ts +53 -0
  361. package/src/gizmo-registry.ts +608 -0
  362. package/src/handle-system.ts +221 -0
  363. package/src/hierarchy-menu-registry.ts +70 -0
  364. package/src/hierarchy-rows.ts +73 -0
  365. package/src/hosted-example.ts +160 -0
  366. package/src/hotkeys.ts +142 -0
  367. package/src/ingest/discovery-react.ts +132 -0
  368. package/src/ingest/discovery.ts +205 -0
  369. package/src/ingest/discovery2d.ts +204 -0
  370. package/src/ingest/esm-entry-resolution.ts +102 -0
  371. package/src/ingest/games/bloom-composer/game.js +99 -0
  372. package/src/ingest/games/bloom-composer/vgai.game.json +21 -0
  373. package/src/ingest/games/embed-bundle/embed.html +10 -0
  374. package/src/ingest/games/embed-bundle/vgai.game.json +21 -0
  375. package/src/ingest/games/games-fps/assets/collision-world.glb +0 -0
  376. package/src/ingest/games/games-fps/game.js +479 -0
  377. package/src/ingest/games/games-fps/vgai.game.json +24 -0
  378. package/src/ingest/games/iframe-reachable/game-source.js +58 -0
  379. package/src/ingest/games/iframe-reachable/vgai.game.json +21 -0
  380. package/src/ingest/games/interactive-cubes/game.js +139 -0
  381. package/src/ingest/games/interactive-cubes/vgai.game.json +21 -0
  382. package/src/ingest/games/mismatched-three/fake-three.js +38 -0
  383. package/src/ingest/games/mismatched-three/game.js +10 -0
  384. package/src/ingest/games/mismatched-three/vgai.game.json +22 -0
  385. package/src/ingest/games/own-three-bundled/game.js +43 -0
  386. package/src/ingest/games/own-three-bundled/vgai.game.json +21 -0
  387. package/src/ingest/games/pointerlock-maze/game.js +288 -0
  388. package/src/ingest/games/pointerlock-maze/vgai.game.json +22 -0
  389. package/src/ingest/games-2d/_fixtures/first-party-scene.scn2d.json +124 -0
  390. package/src/ingest/games-2d/bubbo-bubbo/vgai.game.json +25 -0
  391. package/src/ingest/games-2d/bunnymark/game.ts +88 -0
  392. package/src/ingest/games-2d/bunnymark/vgai.game.json +21 -0
  393. package/src/ingest/games-2d/flappy/game.ts +148 -0
  394. package/src/ingest/games-2d/flappy/vgai.game.json +21 -0
  395. package/src/ingest/games-2d/flappy-pixi/vgai.game.json +25 -0
  396. package/src/ingest/games-2d/puzzling-potions/vgai.game.json +25 -0
  397. package/src/ingest/games-2d/tilemap-camera/game.ts +115 -0
  398. package/src/ingest/games-2d/tilemap-camera/vgai.game.json +21 -0
  399. package/src/ingest/games-react/react-rpg/entry.tsx +185 -0
  400. package/src/ingest/games-react/react-rpg/shims.d.ts +26 -0
  401. package/src/ingest/games-react/react-rpg/src/ui/game-state.tsx +129 -0
  402. package/src/ingest/games-react/react-rpg/vgai.game.json +21 -0
  403. package/src/ingest/host-namespace-registry.ts +196 -0
  404. package/src/ingest/types.ts +127 -0
  405. package/src/ingest-hierarchy-rows.ts +207 -0
  406. package/src/ingest-mode.ts +2092 -0
  407. package/src/ingest-siblings.ts +466 -0
  408. package/src/initial-scene.ts +194 -0
  409. package/src/inspector-section-registry.ts +117 -0
  410. package/src/interactive-edit-scope.ts +34 -0
  411. package/src/main.tsx +23 -0
  412. package/src/manifest-project.ts +43 -0
  413. package/src/model-thumbnail.ts +156 -0
  414. package/src/module-mode.ts +513 -0
  415. package/src/navmesh-handler.ts +94 -0
  416. package/src/packaged-runtime.ts +49 -0
  417. package/src/play-mode.ts +1148 -0
  418. package/src/playground/main.ts +206 -0
  419. package/src/project-manager.ts +173 -0
  420. package/src/scene-diff.ts +112 -0
  421. package/src/scene-serializer.ts +144 -0
  422. package/src/scene-sync.ts +587 -0
  423. package/src/scene-ui-mode/active-scene-ui.ts +58 -0
  424. package/src/scene-ui-mode/scene-ui-palette-panel.tsx +152 -0
  425. package/src/scene-ui-mode/scene-ui-timeline-tab.tsx +29 -0
  426. package/src/scene-ui-mode/ui-prefab-dialog-host.tsx +74 -0
  427. package/src/scene-ui-mode/ui-prefab-hierarchy-menu.ts +49 -0
  428. package/src/scene-ui-mode/ui-prefab-io.ts +76 -0
  429. package/src/script-loader.ts +143 -0
  430. package/src/storage/fsa-storage.ts +55 -0
  431. package/src/storage/handle-storage.ts +150 -0
  432. package/src/storage/handle-store.ts +116 -0
  433. package/src/storage/http-storage.ts +137 -0
  434. package/src/storage/index.ts +147 -0
  435. package/src/storage/mem-storage.ts +135 -0
  436. package/src/storage/opfs-storage.ts +25 -0
  437. package/src/storage/paths.ts +26 -0
  438. package/src/storage/seed.ts +106 -0
  439. package/src/storage/types.ts +110 -0
  440. package/src/tool-loader.ts +291 -0
  441. package/src/ui-editor/inspector.tsx +563 -0
  442. package/src/ui-editor/node-ops.ts +266 -0
  443. package/src/ui-editor/overlay.tsx +380 -0
  444. package/src/ui-editor/palette.tsx +114 -0
  445. package/src/ui-editor/react-store.ts +78 -0
  446. package/src/ui-editor/timeline-panel.tsx +443 -0
  447. package/src/ui-editor/ui-edit-store.ts +552 -0
  448. package/src/ui-editor/ui-prefab.ts +95 -0
  449. package/src/ui-source/inspect.ts +544 -0
  450. package/src/ui-source/oid-transform.ts +133 -0
  451. package/src/ui-source/source-write-backend.ts +203 -0
  452. package/src/ui-source/tailwind-map.ts +174 -0
  453. package/src/ui-source/writer.ts +1175 -0
  454. package/src/widgets/index.ts +73 -0
  455. package/template/.claude/skills/editor/SKILL.md +252 -0
  456. package/template/CLAUDE.md +512 -0
  457. package/template/LICENSE +30 -0
  458. package/template/emit-schemas.ts +42 -0
  459. package/template/index.html +17 -0
  460. package/template/package.json +53 -0
  461. package/template/project.json +9 -0
  462. package/template/public/audio/ambient-arena.wav +0 -0
  463. package/template/public/audio/ambient-music.wav +0 -0
  464. package/template/public/audio/ambient-nature.wav +0 -0
  465. package/template/public/audio/ambient-wind.wav +0 -0
  466. package/template/public/inputmaps/default.inputmap.json +41 -0
  467. package/template/public/jsm/libs/draco/gltf/draco_decoder.js +33 -0
  468. package/template/public/jsm/libs/draco/gltf/draco_decoder.wasm +0 -0
  469. package/template/public/jsm/libs/draco/gltf/draco_encoder.js +33 -0
  470. package/template/public/jsm/libs/draco/gltf/draco_wasm_wrapper.js +116 -0
  471. package/template/public/scenes/default.vscn.json +43 -0
  472. package/template/public/scenes/untitled.vscn.json +29 -0
  473. package/template/public/textures/waternormals.jpg +0 -0
  474. package/template/server/colyseus-setup.ts +85 -0
  475. package/template/server/main.ts +34 -0
  476. package/template/server/rooms/arena-room.ts +420 -0
  477. package/template/server/rooms/game-room.ts +162 -0
  478. package/template/server/rooms.ts +15 -0
  479. package/template/server/tsconfig.json +19 -0
  480. package/template/src/data/README.md +40 -0
  481. package/template/src/data/assets.ts +31 -0
  482. package/template/src/data/tuning.data.json +5 -0
  483. package/template/src/data/tuning.schema.json +21 -0
  484. package/template/src/data/tuning.schema.ts +34 -0
  485. package/template/src/data/tuning.ts +24 -0
  486. package/template/src/main.ts +157 -0
  487. package/template/src/scripts/components/data-spinner.ts +45 -0
  488. package/template/src/scripts/components/scene-camera.ts +40 -0
  489. package/template/src/scripts/main.ts +94 -0
  490. package/template/src/scripts/registry.ts +46 -0
  491. package/template/src/tools/README.md +14 -0
  492. package/template/src/tools/example.tool.tsx +91 -0
  493. package/template/src/tools/spin.tool.tsx +101 -0
  494. package/template/src/ui/Button.tsx +27 -0
  495. package/template/src/ui/DefaultHud.tsx +40 -0
  496. package/template/src/ui/KeyHint.tsx +25 -0
  497. package/template/src/ui/Panel.tsx +52 -0
  498. package/template/src/ui/RebindPanel.tsx +129 -0
  499. package/template/src/ui/Slider.tsx +28 -0
  500. package/template/src/ui/StatusDot.tsx +22 -0
  501. package/template/src/ui/Toggle.tsx +19 -0
  502. package/template/src/ui/game-state.tsx +37 -0
  503. package/template/src/ui/index.ts +7 -0
  504. package/template/src/ui/mount-default-hud.tsx +19 -0
  505. package/template/src/ui/mount-ui.ts +15 -0
  506. package/template/tsconfig.json +36 -0
  507. package/template/validate-asset-content.ts +78 -0
  508. package/template/validate-manifest.ts +55 -0
  509. package/template/validate-scenes.ts +211 -0
  510. package/template/vgai.game.json +21 -0
  511. package/template/vite.config.ts +128 -0
  512. package/vite-plugin-example-assets.ts +60 -0
  513. package/vite-plugin-game-globals.ts +46 -0
  514. package/vite-plugin-game-static.ts +71 -0
  515. package/vite-plugin-project-game-static.ts +84 -0
  516. package/vite-plugin-react-game-static.ts +98 -0
  517. package/vite-plugin-react-world-provider.ts +81 -0
  518. package/vite-plugin-react-world-runtime.ts +76 -0
  519. package/vite-plugin-ui-oid.ts +561 -0
@@ -0,0 +1,513 @@
1
+ /**
2
+ * Module-adapter auto-mount route (T3.4 slice 2, docs/ADAPTER-AUTHORING-
3
+ * DESIGN.md §2/§3 slice 2) — the `{ module }` sibling of `ingest-mode.ts`'s
4
+ * `tryManifestIngestRoute`/`tryManifestIngestRoute2D`.
5
+ *
6
+ * A manifest-backed project whose FIRST world declares a custom `{ module }`
7
+ * adapter auto-mounts it through the generic `GameAdapter.mount(HostContext)`
8
+ * contract — `createGameRuntime` (the SAME engine-side host `play-mode.ts`'s
9
+ * legacy single-world Play branch uses) — then installs the MOUNTED game's
10
+ * OWN `AuthoringAdapter` (`MountedGame.authoring`) as the active authoring
11
+ * override (`setActiveAuthoring`).
12
+ *
13
+ * FINDING (report prominently — see this slice's "Report back"): this is a
14
+ * gap `play-mode.ts`'s existing `resolveWorldAdapter` call (its single-world
15
+ * Play branch) does NOT close. That branch always calls
16
+ * `installPlayAuthoring(store)`, which drives the hierarchy/inspector from
17
+ * `getActiveAuthoring(store)` — i.e. the FIRST-PARTY `.vscn`-over-`store`
18
+ * adapter — never from `session.mounted.authoring`. That's the right choice
19
+ * for `default-three`/`default-pixi`/`default-react` worlds (their mounted
20
+ * scene mirrors the store's OWN document), but it is WRONG for a `{ module }`
21
+ * world: a custom adapter builds its scene from its own code, so driving the
22
+ * inspector from the store's `.vscn` document would show an unrelated (empty,
23
+ * for a fresh CLI-on-folder project) tree instead of the module's real
24
+ * objects. `play-mode.ts` is out of scope for this slice (sibling agent's
25
+ * region), so rather than leave `{ module }` worlds unreachable through the
26
+ * real editor UI at all, this file gives them their OWN auto-mount route —
27
+ * mirroring ingest mode's already-established shape (auto-mount on load,
28
+ * install the mounted adapter as the override, swap the viewport onto the
29
+ * live scene) instead of Play mode's.
30
+ *
31
+ * This route reuses the SAME generic, adapter-driven `GameHierarchy`/
32
+ * `Inspector` panels ingest mode renders — the hierarchy panel (A2) is
33
+ * unconditionally contract-only; the inspector (C2, `components/
34
+ * Inspector.tsx`) matches registered sections per SELECTED NODE via
35
+ * `inspector-section-registry.ts`, and a bare `{ module }` override is never
36
+ * a first-party `VgaiSceneAuthoringAdapter`, so its nodes never match the
37
+ * first-party registrations and always render the generic built-in blocks —
38
+ * so a module adapter's hierarchy/inspector/gizmo reach the SAME editor UI
39
+ * ingest games do, with NO
40
+ * new panel code. The one piece those panels need that ingest's OWN capture
41
+ * machinery provides for free — `userData.entityId` tags on every live
42
+ * `Object3D`, which the viewport's gizmo/selection binds on
43
+ * (`ViewportPanel.tsx`'s `viewport.objectMap = store.objectMap`, built by
44
+ * `EditorStore.enterPlayScene` from that tag) — this route tags itself, right
45
+ * after mount, straight from the adapter's OWN `HierarchyProvider` (walking
46
+ * `mounted.scene` and asking `idForObject3D` for each object) so a `{ module
47
+ * }` adapter author does not have to hand-roll that tagging themselves.
48
+ *
49
+ * D-Y1 (docs/EXTERNAL-REACT-AND-MODULE-DESIGN.md §1/§4 slice S1) adds the
50
+ * WORLDS-PATH sibling below (`enterModuleModeWorldsPath`): a single-world
51
+ * manifest whose first world is `kind: 'pixijs' | 'react'` with a `{ module }`
52
+ * adapter no longer dead-ends in the legacy `enterModuleModeFromManifestWorld`
53
+ * route above (whose `HostContext`/`mounted.scene` assumptions are threejs-
54
+ * only — the exact gap the D-X6 guard, still below, catches). The wave-2
55
+ * ground-truth study found `resolveAllWorlds` ALREADY resolves a `{ module }`
56
+ * world for every kind (its `'module'` switch case is kind-agnostic) and
57
+ * ALREADY hands it back as the correctly-kinded `WorldMountSpec` — so the fix
58
+ * is routing, not an engine change: mirror `ingest-mode.ts`'s
59
+ * `enterIngestModeReact` recipe (one-element `resolveAllWorlds` + worlds-path
60
+ * `createGameRuntime({ container, worlds })`) instead of the legacy single-
61
+ * adapter path. `tryManifestModuleRoute` below picks between the two based on
62
+ * `firstWorld.kind` — threejs module worlds keep calling
63
+ * `enterModuleModeFromManifestWorld` byte-for-byte; the D-X6 guard stays
64
+ * reachable (and its own tests unmodified) for any caller that still invokes
65
+ * that function directly with a non-three world.
66
+ */
67
+
68
+ import type { AuthoringAdapter } from '@engine/adapter';
69
+ import type { ResolvedGameManifest, ResolvedWorldEntry } from '@engine/manifest/load';
70
+ import type { GameSession, WorldMountSpec } from '@engine/runtime/create-runtime';
71
+ import { setUserData } from '@engine/scene/user-data';
72
+ import type * as THREE from 'three';
73
+ import {
74
+ computeAchievedTier,
75
+ computeTierShortfall,
76
+ formatTierShortfallMessage,
77
+ type TierShortfallReport,
78
+ } from './achieved-tier';
79
+ import { resolveAllWorlds, resolveWorldAdapter } from './adapter-resolver';
80
+ import { setActiveAuthoring } from './authoring/active-adapter';
81
+ import { setActiveSystems } from './authoring/active-systems';
82
+ import { makeEmbedOnlyAdapter } from './authoring/embed-only-adapter';
83
+ import { formatMountFailureMessage, setMountFailureReport } from './authoring/mount-failure-report';
84
+ import { editorConsole } from './editor-console';
85
+ import { getEditorMode } from './editor-mode';
86
+ import type { EditorStore } from './editor-store';
87
+ import { waitForGameContainer } from './ingest-mode';
88
+ import { fetchGameManifest } from './manifest-project';
89
+ import { getGameContainer } from './play-mode';
90
+ import { getCurrentProject } from './project-manager';
91
+
92
+ interface ModuleSession {
93
+ store: EditorStore;
94
+ stop: () => void;
95
+ /** Absent on the worlds-path route (D-Y1 below) — that route owns no
96
+ * standalone canvas of its own; `createGameRuntime`'s worlds-path session
97
+ * builds and tears down its own per-world surface(s) directly inside the
98
+ * game container. Present (and removed on exit) only for the legacy
99
+ * threejs single-adapter path's own hand-created canvas. */
100
+ canvas?: HTMLCanvasElement | undefined;
101
+ }
102
+
103
+ let _session: ModuleSession | null = null;
104
+
105
+ /** True if a `{ module }` adapter world is currently auto-mounted. */
106
+ export function isModuleModeActive(): boolean {
107
+ return _session !== null;
108
+ }
109
+
110
+ /** Tag every live object a module adapter's hierarchy actually knows about with
111
+ * `userData.entityId` (the SAME key `IngestAuthoringAdapter`'s own capture
112
+ * uses) so the viewport's existing objectMap-keyed gizmo/selection binds to
113
+ * this adapter's objects with no adapter-author effort. Best-effort: an
114
+ * adapter with no `hierarchy` (opaque) has nothing to walk. */
115
+ function tagObjectsWithEntityIds(
116
+ scene: THREE.Scene,
117
+ idForObject3D: (o: THREE.Object3D) => string | null,
118
+ ): void {
119
+ scene.traverse((o) => {
120
+ const id = idForObject3D(o);
121
+ if (id) setUserData(o, 'entityId', id);
122
+ });
123
+ }
124
+
125
+ /**
126
+ * Selection is editor-global UI state (the SAME rule `IngestAuthoringAdapter`'s
127
+ * own doc comment states) — a custom `{ module }` adapter has no `EditorStore`
128
+ * reference to delegate to (by design: `HostContext` is editor-neutral), so
129
+ * the hierarchy panel's row click (`adapter.selection?.set([node.id])`) would
130
+ * silently no-op without this. Wrap the mounted adapter with a store-backed
131
+ * `SelectionProvider` UNLESS it already declares its own (an adapter author
132
+ * who genuinely wants custom multi-select semantics is not overridden). This
133
+ * costs the adapter author nothing — they never see `EditorStore` at all.
134
+ */
135
+ export function withStoreSelection(
136
+ authoring: AuthoringAdapter,
137
+ store: EditorStore,
138
+ ): AuthoringAdapter {
139
+ if (authoring.selection) return authoring;
140
+ return {
141
+ ...authoring,
142
+ selection: {
143
+ get: () => [...store.selectedEntityIds],
144
+ set: (ids) => store.selectMultiple(ids),
145
+ },
146
+ };
147
+ }
148
+
149
+ /**
150
+ * Mount a manifest's `{ module }` world straight through `GameAdapter.mount`
151
+ * and install it as the editor's live authoring override. `world.capabilities
152
+ * .local` (always resolved/explicit for a module world — the loader requires
153
+ * it, `manifest/load.ts`'s `resolveCapabilities`) is the declared tier; the
154
+ * ACTUAL achieved tier is recomputed here (in addition to
155
+ * `resolveWorldAdapter`'s own internal, warn-only check, T3.4 slice 1) purely
156
+ * to expose the structured values on the `__vgaiModule` dev/e2e hook below —
157
+ * this does not log a second shortfall line, it only reads the same pure
158
+ * computation slice 1 already logs from inside the resolver.
159
+ */
160
+ export async function enterModuleModeFromManifestWorld(
161
+ store: EditorStore,
162
+ world: ResolvedWorldEntry,
163
+ projectRoot: string,
164
+ ): Promise<void> {
165
+ const gameContainer = getGameContainer();
166
+ if (!gameContainer) {
167
+ editorConsole.error('Game container not mounted — cannot auto-mount module adapter', 'adapter');
168
+ return;
169
+ }
170
+
171
+ store.setPlayState('playing');
172
+ editorConsole.log(`Mounting custom adapter module for world "${world.id}"`, 'adapter');
173
+
174
+ // `resolveWorldAdapter`'s `module` branch (T3.4 slice 1) already: path-guards
175
+ // + shape-checks the module, then wraps its `mount` with the achieved-tier
176
+ // warn-only check (`withAchievedTierCheck`) — reused verbatim here, not
177
+ // duplicated.
178
+ const adapter = await resolveWorldAdapter(world, projectRoot);
179
+
180
+ const canvas = document.createElement('canvas');
181
+ canvas.style.cssText = 'width:100%;height:100%;display:block;';
182
+ gameContainer.appendChild(canvas);
183
+ const w = gameContainer.clientWidth || 800;
184
+ const h = gameContainer.clientHeight || 600;
185
+
186
+ const { createGameRuntime } = await import('@engine/runtime/create-runtime');
187
+ const session = await createGameRuntime({ canvas, width: w, height: h, adapter });
188
+ const mounted = session.mounted;
189
+
190
+ // D-X6 honesty guard (docs/EXTERNAL-FOLDER-INGEST-DESIGN.md §2/§6 slice S4):
191
+ // this route only wires up THREE.Scene-shaped worlds — everything below
192
+ // (`tagObjectsWithEntityIds`, `store.enterPlayScene`) assumes `mounted.scene`
193
+ // exists. A pixi/react `{ module }` world's mounted result has NO `.scene`
194
+ // at all (`MountedPixiWorld`/`MountedReactWorld`, `@engine/adapter` —
195
+ // `guardModuleShape` only checks the ADAPTER's `id`/`mount` shape, never
196
+ // the resolved MOUNT result's kind), so continuing silently would pass
197
+ // `undefined` into `enterPlayScene` — exactly the silent dead end D-X6
198
+ // replaces with a loud, named error (anti-shim / degrade-loudly). Trust
199
+ // the mounted result's OWN self-declared `kind` tag over the manifest's
200
+ // `world.kind` — it is what the module ACTUALLY returned; an absent `kind`
201
+ // (a pre-T7.5 module adapter that never had to declare one) is treated as
202
+ // the historical threejs-only default, so existing module fixtures are
203
+ // unaffected.
204
+ const mountedKind = (mounted as unknown as { kind?: string }).kind;
205
+ if (mountedKind !== undefined && mountedKind !== 'threejs') {
206
+ canvas.remove();
207
+ store.setPlayState('stopped');
208
+ throw new Error(
209
+ `enterModuleModeFromManifestWorld: world "${world.id}" (kind: "${mountedKind}") is a ` +
210
+ '{ module } adapter whose mounted result is not a threejs world — v1 module-mount ' +
211
+ 'route only wires up THREE.Scene-shaped worlds into enterPlayScene ' +
212
+ '(docs/EXTERNAL-FOLDER-INGEST-DESIGN.md D-X6). Pixi/react { module } worlds have no ' +
213
+ 'mount plumbing yet — kind-parameterized HostContext + per-kind module mounting is ' +
214
+ 'wave 2, gated on its own design doc. This throws loudly instead of silently passing ' +
215
+ '`undefined` into enterPlayScene.',
216
+ );
217
+ }
218
+
219
+ _session = {
220
+ store,
221
+ canvas,
222
+ stop: () => session.stop(),
223
+ };
224
+
225
+ let activeAuthoring: AuthoringAdapter;
226
+ if (mounted.authoring) {
227
+ tagObjectsWithEntityIds(mounted.scene as THREE.Scene, (o) =>
228
+ mounted.authoring!.hierarchy.idForObject3D(o),
229
+ );
230
+ activeAuthoring = withStoreSelection(mounted.authoring, store);
231
+ } else {
232
+ // Honest floor for a module adapter that declares no authoring surface at
233
+ // all — same embed-only ceiling ingest's own capture-failed path uses.
234
+ activeAuthoring = makeEmbedOnlyAdapter(store, `${adapter.id} (no authoring surface)`);
235
+ }
236
+ setActiveAuthoring(activeAuthoring);
237
+ setActiveSystems(mounted.systems ?? {});
238
+ store.enterPlayScene(mounted.scene as THREE.Scene);
239
+ store.setActiveViewportTab('scene');
240
+ store.notifyIngestEdit();
241
+ setMountFailureReport(null); // D-W3: mount succeeded
242
+
243
+ const achievedTier = computeAchievedTier(mounted);
244
+ const shortfall: TierShortfallReport | null = computeTierShortfall(
245
+ world.id,
246
+ world.capabilities.local,
247
+ achievedTier,
248
+ );
249
+ // Belt-and-suspenders: resolveWorldAdapter's own wrapping already warned once
250
+ // via editorConsole (slice 1) — this does not warn again, only formats the
251
+ // SAME message onto the structured dev/e2e hook below for assertions.
252
+ if (shortfall) {
253
+ editorConsole.log(
254
+ `(already warned by the resolver) ${formatTierShortfallMessage(shortfall)}`,
255
+ 'adapter',
256
+ );
257
+ }
258
+
259
+ (window as unknown as Record<string, unknown>)['__vgaiModule'] = {
260
+ worldId: world.id,
261
+ adapterId: adapter.id,
262
+ declaredTier: world.capabilities.local,
263
+ achievedTier,
264
+ ...(shortfall ? { tierShortfall: shortfall } : {}),
265
+ // DEV-only: the ACTIVE authoring adapter (the store-backed-selection
266
+ // wrapper, when applied) + store, so e2e/proof can drive the SAME
267
+ // providers the hierarchy/inspector/gizmo use (mirrors `__vgaiIngest`).
268
+ ...(import.meta.env.DEV ? { adapter: activeAuthoring, store } : {}),
269
+ };
270
+ }
271
+
272
+ /** Tear down the auto-mounted module session (mirrors `ingest-mode.ts`'s
273
+ * `exitIngestMode`). Not called by any route today (no e2e navigates away
274
+ * from a module-mounted project mid-session) — provided for symmetry/hygiene
275
+ * and any future caller (e.g. a project switch). */
276
+ export function exitModuleMode(): void {
277
+ const s = _session;
278
+ if (!s) return;
279
+ _session = null;
280
+ setActiveAuthoring(null);
281
+ setActiveSystems(null);
282
+ setMountFailureReport(null); // D-W3: no session -> no banner to show (mirrors ingest-mode.ts's exitIngestMode)
283
+ try {
284
+ s.store.exitPlayScene();
285
+ } catch {
286
+ /* scene may not have been swapped if mount failed */
287
+ }
288
+ s.store.setPlayState('stopped');
289
+ try {
290
+ s.stop();
291
+ } catch {
292
+ /* ignore */
293
+ }
294
+ s.canvas?.remove();
295
+ delete (window as unknown as Record<string, unknown>)['__vgaiModule'];
296
+ editorConsole.log('Module adapter mode stopped', 'adapter');
297
+ }
298
+
299
+ // ---------------------------------------------------------------------------
300
+ // D-Y1 (docs/EXTERNAL-REACT-AND-MODULE-DESIGN.md §1/§4 slice S1) —
301
+ // worlds-path routing for non-three `{ module }` worlds
302
+ // ---------------------------------------------------------------------------
303
+
304
+ /**
305
+ * Mount a single-world manifest's `kind: 'pixijs' | 'react'` `{ module }`
306
+ * world through the WORLDS PATH — the D-Y1 sibling of
307
+ * `enterModuleModeFromManifestWorld` above, mirroring `ingest-mode.ts`'s
308
+ * `enterIngestModeReact` recipe exactly: one-element `resolveAllWorlds` +
309
+ * worlds-path `createGameRuntime({ container, worlds })`, which hands the
310
+ * module adapter its own native `World2DHost`/`ReactWorldHost` — no
311
+ * `HostContext`, no `.scene` assumption, no engine-side change needed (the
312
+ * wave-2 study's §0 finding: `resolveAllWorlds`'s `'module'` case is already
313
+ * kind-agnostic and already returns the correctly-kinded `WorldMountSpec`).
314
+ *
315
+ * Only THIS one world mounts (mirrors the legacy path's own "only ever the
316
+ * first world" behavior, `tryManifestModuleRoute` below): `manifest`'s other
317
+ * worlds (if any) are discarded via the one-element `{ ...manifest, worlds:
318
+ * [world] }` passed to `resolveAllWorlds` — a real multi-world manifest whose
319
+ * FIRST world is a non-three module adapter is out of scope here exactly as
320
+ * it always was for the legacy route.
321
+ *
322
+ * `withAchievedTierCheck` (already wrapped inside `resolveModuleAdapter`,
323
+ * reached identically through `resolveAllWorlds` -> `resolveWorldAdapter` ->
324
+ * the `'module'` case) runs completely unmodified here — an over-claiming
325
+ * module world still gets its shortfall logged loudly by the resolver itself;
326
+ * this function neither bypasses nor re-derives that check, only reads the
327
+ * SAME pure `computeAchievedTier`/`computeTierShortfall` result again to
328
+ * expose it on the `__vgaiModule` dev/e2e hook below (byte-identical
329
+ * "belt-and-suspenders" comment to the legacy path above).
330
+ *
331
+ * Authoring: whatever `mounted.authoring` the module adapter itself returns
332
+ * becomes the active override (store-selection-wrapped via `withStoreSelection`
333
+ * — the SAME policy the legacy path applies, kind-agnostic since it only
334
+ * touches `AuthoringAdapter.selection`); no authoring surface at all gets the
335
+ * same honest embed-only floor `enterModuleModeFromManifestWorld` uses. There
336
+ * is deliberately NO `tagObjectsWithEntityIds`/`store.enterPlayScene` call
337
+ * here (unlike the legacy threejs path): a pixi/react module world has no
338
+ * THREE scene for the editor viewport to adopt — its own canvas/DOM layer
339
+ * renders directly inside the game container, and the hierarchy/inspector
340
+ * panels are driven purely through the `AuthoringAdapter` seam (per this
341
+ * file's header doc comment: panel choice is adapter/node-identity-keyed,
342
+ * not scene-adoption-keyed) — exactly like `enterIngestModeReact`.
343
+ */
344
+ /**
345
+ * Mount a single `{ module }` world (any kind) through the SCOPED worlds-path
346
+ * recipe — a one-element `resolveAllWorlds` + worlds-path `createGameRuntime`
347
+ * — with NO global side effects of its own (no `setActiveAuthoring`, no
348
+ * `store`/viewport writes, no dev hooks). Extracted (D-V4,
349
+ * docs/WAVE5-MULTIWORLD-INGEST-DESIGN.md) so BOTH this file's own
350
+ * `enterModuleModeWorldsPath` below (which layers store/authoring/dev-hook
351
+ * wiring on top, mounting into the WHOLE shared game container) and a
352
+ * composite's `{ module }` SIBLING mount (`ingest-siblings.ts`, which mounts
353
+ * into its own isolated layer div beside an ingest world and adds none of
354
+ * that global wiring) share the identical resolve+mount step, rather than
355
+ * duplicating it.
356
+ */
357
+ export async function mountModuleWorldRuntime(
358
+ manifest: ResolvedGameManifest,
359
+ world: ResolvedWorldEntry,
360
+ projectRoot: string,
361
+ container: HTMLElement,
362
+ width: number,
363
+ height: number,
364
+ ): Promise<{ session: GameSession; specs: WorldMountSpec[] }> {
365
+ const { createGameRuntime } = await import('@engine/runtime/create-runtime');
366
+ const specs = await resolveAllWorlds({ ...manifest, worlds: [world] }, projectRoot);
367
+ const session = await createGameRuntime({ container, worlds: specs, width, height });
368
+ return { session, specs };
369
+ }
370
+
371
+ export async function enterModuleModeWorldsPath(
372
+ store: EditorStore,
373
+ manifest: ResolvedGameManifest,
374
+ world: ResolvedWorldEntry,
375
+ projectRoot: string,
376
+ ): Promise<void> {
377
+ const gameContainer = getGameContainer();
378
+ if (!gameContainer) {
379
+ editorConsole.error('Game container not mounted — cannot auto-mount module adapter', 'adapter');
380
+ return;
381
+ }
382
+
383
+ store.setPlayState('playing');
384
+ editorConsole.log(
385
+ `Mounting custom adapter module for world "${world.id}" (kind: ${world.kind}, worlds path)`,
386
+ 'adapter',
387
+ );
388
+
389
+ const w = gameContainer.clientWidth || 800;
390
+ const h = gameContainer.clientHeight || 600;
391
+ const { session, specs } = await mountModuleWorldRuntime(
392
+ manifest,
393
+ world,
394
+ projectRoot,
395
+ gameContainer,
396
+ w,
397
+ h,
398
+ );
399
+
400
+ const worldInstance = session.game.worlds[0]!;
401
+ const mounted = worldInstance.mounted;
402
+ const adapterId = specs[0]!.adapter.id;
403
+
404
+ _session = { store, stop: () => session.stop() };
405
+
406
+ let activeAuthoring: AuthoringAdapter;
407
+ if (mounted.authoring) {
408
+ activeAuthoring = withStoreSelection(mounted.authoring, store);
409
+ } else {
410
+ // Honest floor for a module adapter that declares no authoring surface at
411
+ // all — same embed-only ceiling the legacy path (and ingest) uses.
412
+ activeAuthoring = makeEmbedOnlyAdapter(store, `${adapterId} (no authoring surface)`);
413
+ }
414
+ setActiveAuthoring(activeAuthoring);
415
+ setActiveSystems(mounted.systems ?? {});
416
+ store.setActiveViewportTab('game');
417
+ store.notifyIngestEdit();
418
+ setMountFailureReport(null); // D-W3: mount succeeded
419
+
420
+ const achievedTier = computeAchievedTier(mounted);
421
+ const shortfall: TierShortfallReport | null = computeTierShortfall(
422
+ world.id,
423
+ world.capabilities.local,
424
+ achievedTier,
425
+ );
426
+ // Belt-and-suspenders: resolveWorldAdapter's own wrapping already warned once
427
+ // via editorConsole (T3.4 slice 1) — this does not warn again, only formats
428
+ // the SAME message onto the structured dev/e2e hook below for assertions.
429
+ if (shortfall) {
430
+ editorConsole.log(
431
+ `(already warned by the resolver) ${formatTierShortfallMessage(shortfall)}`,
432
+ 'adapter',
433
+ );
434
+ }
435
+
436
+ (window as unknown as Record<string, unknown>)['__vgaiModule'] = {
437
+ worldId: world.id,
438
+ adapterId,
439
+ declaredTier: world.capabilities.local,
440
+ achievedTier,
441
+ ...(shortfall ? { tierShortfall: shortfall } : {}),
442
+ ...(import.meta.env.DEV ? { adapter: activeAuthoring, store } : {}),
443
+ };
444
+ }
445
+
446
+ /**
447
+ * Route 0 of `ingest-mode.ts`'s `autoLaunchIngestFromURL` (checked before the
448
+ * ingest routes — see that function's doc comment): a manifest-backed project
449
+ * whose FIRST world declares a `{ module }` adapter auto-mounts it. Same
450
+ * "applied or not" contract as `tryManifestIngestRoute`.
451
+ *
452
+ * D-Y1: dispatches on `firstWorld.kind` — threejs keeps calling
453
+ * `enterModuleModeFromManifestWorld` byte-for-byte (its own D-X6 guard stays
454
+ * as the backstop for any OTHER path that might still funnel a non-three
455
+ * mount into it); `pixijs`/`react` route through the new
456
+ * `enterModuleModeWorldsPath` above instead.
457
+ *
458
+ * D-V2 (docs/WAVE5-MULTIWORLD-INGEST-DESIGN.md, F24 composite): if the
459
+ * manifest declares ANY `{ ingest }` world (anywhere in `worlds`, not just
460
+ * first), this route declines — composites are owned by the ingest routes
461
+ * (`ingest-mode.ts`'s `tryManifestIngestRoute*`), which mount the one ingest
462
+ * world through their own enter path and any `{ module }` SIBLING through
463
+ * the scoped runtime (`ingest-siblings.ts`, D-V4) instead of this
464
+ * single-world route's global-side-effect-laden mount. Without this guard, a
465
+ * composite whose FIRST world happens to be a `{ module }` sibling (array
466
+ * order is manifest order, independent of which world is the primary
467
+ * ingest) would be mis-mounted here as if it were a standalone module world.
468
+ * This route's own `firstWorld.adapter.type !== 'module'` behavior for a
469
+ * non-composite manifest is otherwise untouched.
470
+ */
471
+ export async function tryManifestModuleRoute(store: EditorStore): Promise<boolean> {
472
+ if (getEditorMode() === 'browser') return false; // no dev server to serve /vgai.game.json / /@fs/ from
473
+ const manifest = await fetchGameManifest().catch(() => null);
474
+ if (manifest?.worlds.some((w) => w.adapter.type === 'ingest')) return false;
475
+ const firstWorld = manifest?.worlds[0];
476
+ if (firstWorld === undefined || firstWorld.adapter.type !== 'module') return false;
477
+
478
+ const project = getCurrentProject();
479
+ if (!project) {
480
+ editorConsole.error(
481
+ 'Manifest declares a { module } world but no project is open — cannot auto-mount',
482
+ 'adapter',
483
+ );
484
+ return true;
485
+ }
486
+ const ready = await waitForGameContainer();
487
+ if (!ready) {
488
+ editorConsole.error(
489
+ 'Game container never mounted — cannot auto-mount module adapter',
490
+ 'adapter',
491
+ );
492
+ return true;
493
+ }
494
+ try {
495
+ if (firstWorld.kind === 'pixijs' || firstWorld.kind === 'react') {
496
+ await enterModuleModeWorldsPath(store, manifest!, firstWorld, project.rootPath);
497
+ } else {
498
+ await enterModuleModeFromManifestWorld(store, firstWorld, project.rootPath);
499
+ }
500
+ } catch (err) {
501
+ editorConsole.error(`Manifest { module } auto-mount failed: ${err}`, 'adapter');
502
+ // D-W3 (docs/WAVE4-FTUE-HARDENING-DESIGN.md §D-W3, F9) — the fourth of the
503
+ // four manifest-route catch blocks (three/pixi/react in ingest-mode.ts,
504
+ // this one for `{ module }` worlds); same loud-banner-slot population.
505
+ setMountFailureReport({
506
+ worldId: firstWorld.id,
507
+ kind: firstWorld.kind,
508
+ identity: firstWorld.adapter.identity,
509
+ message: formatMountFailureMessage(err),
510
+ });
511
+ }
512
+ return true;
513
+ }
@@ -0,0 +1,94 @@
1
+ import { initNavigation, NavMeshManager } from '@engine/ai/navigation';
2
+ import type * as THREE from 'three';
3
+ import { saveFile } from './editor-api';
4
+ import { editorConsole } from './editor-console';
5
+ import type { EditorStore } from './editor-store';
6
+ import type { EditorViewport } from './editor-viewport';
7
+
8
+ /**
9
+ * Set up navmesh bake/clear event listeners. Returns a dispose function.
10
+ */
11
+ export function setupNavMeshHandlers(
12
+ store: EditorStore,
13
+ viewport: EditorViewport,
14
+ scene: THREE.Scene,
15
+ ): () => void {
16
+ let navMeshManager: NavMeshManager | null = null;
17
+
18
+ function collectNavigationIds(role: 'walkable' | 'obstacle'): Set<string> {
19
+ const ids = new Set<string>();
20
+ for (const e of store.allEntities()) {
21
+ if (e.navigation?.role === role && e.id) ids.add(e.id);
22
+ }
23
+ return ids;
24
+ }
25
+
26
+ const onBake = async () => {
27
+ try {
28
+ await initNavigation();
29
+ } catch (err) {
30
+ editorConsole.error(`Failed to init recast-navigation WASM: ${err}`, 'navigation');
31
+ return;
32
+ }
33
+
34
+ const walkableIds = collectNavigationIds('walkable');
35
+ if (walkableIds.size === 0) {
36
+ editorConsole.warn(
37
+ 'No walkable entities found — tag meshes as "walkable" in the Navigation section',
38
+ 'navigation',
39
+ );
40
+ return;
41
+ }
42
+
43
+ const meshes: THREE.Mesh[] = [];
44
+ for (const id of walkableIds) {
45
+ const obj = store.objectMap.get(id);
46
+ if (!obj) continue;
47
+ obj.traverse((child) => {
48
+ if ((child as THREE.Mesh).isMesh) meshes.push(child as THREE.Mesh);
49
+ });
50
+ }
51
+ if (meshes.length === 0) return;
52
+
53
+ if (!navMeshManager) navMeshManager = new NavMeshManager();
54
+ const envNav = store.environment?.navigation;
55
+ const success = navMeshManager!.buildFromMeshes(meshes, envNav);
56
+ if (!success) {
57
+ editorConsole.error('NavMesh bake failed', 'navigation');
58
+ return;
59
+ }
60
+
61
+ const debugMesh = navMeshManager!.getDebugMesh(scene);
62
+ if (debugMesh) viewport.setNavMeshHelper(debugMesh);
63
+
64
+ store.markNavMeshBaked();
65
+
66
+ if (store.savePath) {
67
+ const navmeshPath = store.savePath.replace('.vscn.json', '.navmesh');
68
+ const data = navMeshManager!.exportData();
69
+ let binary = '';
70
+ for (let i = 0; i < data.length; i++) binary += String.fromCharCode(data[i]!);
71
+ const base64 = btoa(binary);
72
+ saveFile(navmeshPath, base64, 'base64').then((ok) => {
73
+ if (!ok) editorConsole.error('Failed to save .navmesh file', 'navigation');
74
+ });
75
+ }
76
+ };
77
+
78
+ const onClear = () => {
79
+ if (navMeshManager) {
80
+ navMeshManager.dispose(scene);
81
+ navMeshManager = null;
82
+ }
83
+ viewport.setNavMeshHelper(null);
84
+ };
85
+
86
+ window.addEventListener('editor:bake-navmesh', onBake);
87
+ window.addEventListener('editor:clear-navmesh', onClear);
88
+
89
+ return () => {
90
+ window.removeEventListener('editor:bake-navmesh', onBake);
91
+ window.removeEventListener('editor:clear-navmesh', onClear);
92
+ if (navMeshManager) navMeshManager.dispose(scene);
93
+ };
94
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Runtime detection of the PACKAGED editor server (`packages/editor/server/
3
+ * packaged.ts` — a `@vgai/editor` npm package with NO monorepo checkout on
4
+ * disk) from editor CLIENT code. Needed because a react world's mount
5
+ * (`adapter-resolver.ts`'s `resolveReactWorldMountRuntime`) must resolve its
6
+ * `react`/`react-dom/client`/`GameProvider`-fallback wrapper from the
7
+ * PROJECT's own module graph ONLY under this runtime — see that function's
8
+ * doc comment, and `packaged.ts`'s header doc comment ("React-world OID
9
+ * authoring parity"), for the dual-React-instance bug this closes. Under
10
+ * dev/Electron, this must resolve `false` so the existing static-import path
11
+ * stays byte-unchanged (one shared Vite instance already collapses both
12
+ * react copies via `resolve.dedupe`).
13
+ *
14
+ * Reads the server's own `packaged` flag off `/__editor/project`'s response
15
+ * (computed server-side via `isMonorepoScaffoldRoot`, `server-utils.ts`)
16
+ * rather than probing a URL that only resolves in packaged mode: an
17
+ * unconditional dynamic `import()` probe would 404 (and log an
18
+ * unsuppressible browser console error) on EVERY react-world mount under
19
+ * dev/Electron — the exact reasoning `adapter-resolver.ts`'s
20
+ * `loadProjectUIRegistry` doc comment already recorded for `hasUiRegistry`
21
+ * ("the client must not probe /@fs for it").
22
+ *
23
+ * Memoized for the session (module-level, not per-call): the runtime a
24
+ * server process is running under cannot change without a restart, so this
25
+ * fetches `/__editor/project` at most once per page load.
26
+ *
27
+ * A plain inline `fetch()`, not `editor-api.ts`'s `getCurrentServerProject`
28
+ * — same reasoning `loadProjectUIRegistry` documents: this module (via
29
+ * `adapter-resolver.ts`) must stay importable by headless Node/vitest
30
+ * suites, and `editor-api.ts`'s module top level pulls in `./storage`.
31
+ */
32
+
33
+ let cachedIsPackaged: Promise<boolean> | null = null;
34
+
35
+ export function isPackagedRuntime(): Promise<boolean> {
36
+ if (!cachedIsPackaged) {
37
+ cachedIsPackaged = fetch('/__editor/project')
38
+ .then((res) => res.json())
39
+ .then((data: { project?: { packaged?: boolean } | null }) => Boolean(data.project?.packaged))
40
+ .catch(() => false);
41
+ }
42
+ return cachedIsPackaged;
43
+ }
44
+
45
+ /** Test-only: reset the memoized result between tests (mirrors
46
+ * `setCachedProjectUIRegistry`'s test-seam shape). */
47
+ export function resetPackagedRuntimeCacheForTest(): void {
48
+ cachedIsPackaged = null;
49
+ }