@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,313 @@
1
+ /**
2
+ * World2DAuthoringAdapter — the editor's {@link AuthoringAdapter} implemented over a
3
+ * live PixiJS world2d tree (first-party scene OR an ingested unmodified game). It is
4
+ * the world2d peer of {@link IngestAuthoringAdapter}: the editor's REAL adapter-driven
5
+ * panels (GameHierarchy / Inspector) render world2d entities and edits apply
6
+ * live, with NO SceneEntity fabricated (anti-shim rule).
7
+ *
8
+ * It wraps the engine's backend-neutral {@link AuthoringAdapter2D} (tree + structural
9
+ * ids + overlay) and {@link PhysicsAdapter2D} (freeze→commit→unfreeze), and translates
10
+ * the engine's 2D transform (x, y, scalar rotation) to/from the editor's neutral
11
+ * {@link Transform} tuple (position[3] / quaternion-about-Z / scale[3]).
12
+ */
13
+
14
+ import type {
15
+ AuthoringAdapter,
16
+ AuthoringCapabilities,
17
+ EditorNode,
18
+ HierarchyProvider,
19
+ InspectorProvider,
20
+ PersistenceProvider,
21
+ PropertyDescriptor,
22
+ SelectionProvider,
23
+ Transform,
24
+ TransformOwner,
25
+ TransformProvider,
26
+ } from '@engine/adapter';
27
+ import {
28
+ AuthoringAdapter2D,
29
+ type Overlay2D,
30
+ type Transform2DValue,
31
+ } from '@engine/world2d/authoring-2d';
32
+ import type { PhysicsAdapter2D } from '@engine/world2d/system-adapters-2d';
33
+ import type { Container } from 'pixi.js';
34
+ import type { EditorStore } from '../editor-store';
35
+ import { notifyUndoableOp, registerUndoAdapter } from './undo-timeline';
36
+
37
+ function numToHex(n: number): string {
38
+ return `#${(n & 0xffffff).toString(16).padStart(6, '0')}`;
39
+ }
40
+ function hexToNum(hex: string): number {
41
+ return Number.parseInt(hex.replace('#', ''), 16);
42
+ }
43
+ /** Quaternion (xyzw) about Z ⇄ scalar radians. */
44
+ function angleToQuat(a: number): [number, number, number, number] {
45
+ return [0, 0, Math.sin(a / 2), Math.cos(a / 2)];
46
+ }
47
+ function quatToAngle(q: [number, number, number, number]): number {
48
+ return 2 * Math.atan2(q[2], q[3]);
49
+ }
50
+
51
+ /**
52
+ * Save callback + human-readable destination for a world2d session's persistence
53
+ * (design §2/§4). Mirrors `IngestAuthoringOptions` (`ingest-authoring-adapter.ts`):
54
+ * absence of `persist` means runtime-only (`capabilities.persist === false`).
55
+ */
56
+ export interface World2DAuthoringOptions {
57
+ /** Overlay to reapply once the tree is walked (edits from a previous session). */
58
+ overlay?: Overlay2D | undefined;
59
+ /** Save callback for `persistence.save()`. Absence ⇒ runtime-only (persist:false). */
60
+ persist?: ((overlay: Overlay2D) => Promise<void>) | undefined;
61
+ /** Human-readable destination (design §2), e.g. `.vgai/overlays/2d-<id>.json`. */
62
+ destination?: string | undefined;
63
+ }
64
+
65
+ export class World2DAuthoringAdapter implements AuthoringAdapter {
66
+ readonly capabilities: AuthoringCapabilities;
67
+ private readonly a2d: AuthoringAdapter2D;
68
+ private readonly persistFn: ((overlay: Overlay2D) => Promise<void>) | undefined;
69
+ /** Human-readable overlay destination (design §2) — see {@link World2DAuthoringOptions.destination}. */
70
+ private readonly destination: string;
71
+ private listeners = new Set<() => void>();
72
+
73
+ constructor(
74
+ root: Container,
75
+ private readonly physics: PhysicsAdapter2D,
76
+ private readonly store: EditorStore,
77
+ opts: World2DAuthoringOptions = {},
78
+ ) {
79
+ this.a2d = new AuthoringAdapter2D(root);
80
+ if (opts.overlay) this.a2d.applyOverlay(opts.overlay);
81
+ this.persistFn = opts.persist;
82
+ this.destination =
83
+ opts.destination ?? (opts.persist ? '(world2d overlay)' : '(runtime-only — not persisted)');
84
+ this.capabilities = {
85
+ transform: true,
86
+ material: true, // tint
87
+ inspectorFields: true,
88
+ // Structural authoring is not supported over a world2d tree today (mirrors
89
+ // the 3D ingest ceiling): create/delete/reparent would need to survive the
90
+ // next refresh()/remount, which neither surface implements.
91
+ create: false,
92
+ delete: false,
93
+ reparent: false,
94
+ persist: !!this.persistFn, // runtime-only unless a persist callback is wired
95
+ };
96
+ // v4 architecture-review §7.1-13 (was T3.2 slice 2 for the structural twin,
97
+ // `IngestAuthoringAdapter`, only): this adapter IS the undo/redo target the
98
+ // editor-global timeline dispatches to for entries it records below
99
+ // (`pushUndo`). The caller (`play-mode.ts`'s `exitMultiWorldAuthoring`,
100
+ // `ingest-mode.ts`'s `exitIngestMode2D`) unregisters it on teardown — see
101
+ // those call sites' comments for why an unregistered-but-still-recorded
102
+ // entry is dropped (with a warn) rather than undone against a torn-down tree.
103
+ registerUndoAdapter(this);
104
+ }
105
+
106
+ refresh(): { count: number; sprites: number } {
107
+ return this.a2d.refresh();
108
+ }
109
+
110
+ /** Reapply a saved/external overlay onto the live tree (see `AuthoringAdapter2D.applyOverlay`). */
111
+ applyOverlay(overlay: Overlay2D): { applied: number; orphaned: number } {
112
+ return this.a2d.applyOverlay(overlay);
113
+ }
114
+
115
+ // A class GETTER, not a field initializer — a field initializer runs before the
116
+ // constructor body assigns `this.persistFn`/`this.destination` (the slice-1
117
+ // ordering bug; see `vgai-scene-authoring-adapter.ts` for the pattern).
118
+ get persistence(): PersistenceProvider {
119
+ const a2d = this.a2d;
120
+ const persistFn = this.persistFn;
121
+ const destination = this.destination;
122
+ return {
123
+ isDirty: () => a2d.isDirty(),
124
+ save: async () => {
125
+ if (!persistFn) return;
126
+ await persistFn(a2d.serializeOverlay());
127
+ a2d.markClean();
128
+ },
129
+ serialize: () => a2d.serializeOverlay(),
130
+ destination,
131
+ // The reload contract (design §5): re-apply a changed overlay onto the live
132
+ // tree without a full remount — `AuthoringAdapter2D.applyOverlay` already IS
133
+ // that remount-free logic (used at construction too).
134
+ applyExternal: (content) => {
135
+ a2d.applyOverlay(content as Overlay2D);
136
+ },
137
+ };
138
+ }
139
+
140
+ private notify(): void {
141
+ for (const l of this.listeners) l();
142
+ }
143
+
144
+ readonly hierarchy: HierarchyProvider = {
145
+ roots: () =>
146
+ this.a2d
147
+ .roots()
148
+ .map((n) => this.toEditorNode(n.id))
149
+ .filter((n): n is EditorNode => n !== null),
150
+ node: (id) => this.toEditorNode(id),
151
+ object3D: () => null, // world2d entities are PIXI display objects, not THREE — no 3D gizmo binding
152
+ idForObject3D: () => null,
153
+ };
154
+
155
+ private toEditorNode(id: string): EditorNode | null {
156
+ const n = this.a2d.node(id);
157
+ if (!n) return null;
158
+ const disp = this.a2d.displayObject(id);
159
+ const owner: TransformOwner =
160
+ disp && this.physics.ownerOf(disp) === 'physics' ? 'physics' : 'editor';
161
+ return {
162
+ id: n.id,
163
+ label: n.label,
164
+ kind: n.kind,
165
+ parentId: n.parentId,
166
+ childIds: n.childIds,
167
+ flags: { runtimeOnly: true, transformOwner: owner },
168
+ };
169
+ }
170
+
171
+ readonly selection: SelectionProvider = {
172
+ get: () => [...this.store.selectedEntityIds],
173
+ set: (ids) => {
174
+ this.store.selectMultiple(ids);
175
+ this.notify();
176
+ },
177
+ };
178
+
179
+ readonly transforms: TransformProvider = {
180
+ get: (id): Transform => {
181
+ const t = this.a2d.getTransform(id);
182
+ if (!t) return { position: [0, 0, 0], rotation: [0, 0, 0, 1], scale: [1, 1, 1] };
183
+ return {
184
+ position: [t.position[0], t.position[1], 0],
185
+ rotation: angleToQuat(t.rotation),
186
+ scale: [t.scale[0], t.scale[1], 1],
187
+ };
188
+ },
189
+ owner: (id) => {
190
+ const disp = this.a2d.displayObject(id);
191
+ return disp && this.physics.ownerOf(disp) === 'physics' ? 'physics' : 'editor';
192
+ },
193
+ // Snapshot the PRE-drag transform for undo (mirrors IngestAuthoringAdapter's
194
+ // `editStartTransform`) — in the engine's own [x,y]/scalar-radians shape, not
195
+ // the editor's quaternion `Transform`, so undo/redo round-trip through the
196
+ // exact same primitives `setTransform`/`getTransform` already use (no lossy
197
+ // angle<->quaternion re-derivation on the undo path).
198
+ beginEdit: (id) => {
199
+ const disp = this.a2d.displayObject(id);
200
+ if (disp && this.physics.ownerOf(disp) === 'physics') this.physics.freeze(disp);
201
+ this.editStartTransform = this.a2d.getTransform(id) ?? undefined;
202
+ },
203
+ apply: (id, t) => {
204
+ const pos: [number, number] = [t.position[0], t.position[1]];
205
+ const angle = quatToAngle(t.rotation);
206
+ this.writeTransform(id, { position: pos, rotation: angle, scale: [t.scale[0], t.scale[1]] });
207
+ },
208
+ // Pushes exactly ONE undo entry per gesture: `apply` fires many times during
209
+ // a live drag (no undo push there — see `writeTransform`), `endEdit` fires
210
+ // once on commit, which is where the before/after pair is recorded.
211
+ endEdit: (id) => {
212
+ const disp = this.a2d.displayObject(id);
213
+ if (disp && this.physics.ownerOf(disp) === 'physics') this.physics.unfreeze(disp);
214
+ const prev = this.editStartTransform;
215
+ this.editStartTransform = undefined;
216
+ if (!prev) return;
217
+ const next = this.a2d.getTransform(id);
218
+ if (!next) return;
219
+ this.pushUndo({
220
+ undo: () => this.writeTransform(id, prev),
221
+ redo: () => this.writeTransform(id, next),
222
+ });
223
+ },
224
+ };
225
+
226
+ /** Write a 2D transform to the live tree (+ physics body, if owned) + the
227
+ * overlay (via `AuthoringAdapter2D.setTransform`) — no undo push; shared by
228
+ * `transforms.apply` (live-drag, no push) and the undo/redo thunks
229
+ * `endEdit` records (which DO push, exactly once, on commit). */
230
+ private writeTransform(id: string, t: Transform2DValue): void {
231
+ const disp = this.a2d.displayObject(id);
232
+ if (disp && this.physics.ownerOf(disp) === 'physics') {
233
+ this.physics.commit(disp, t.position, t.rotation);
234
+ }
235
+ this.a2d.setTransform(id, t);
236
+ this.notify();
237
+ }
238
+
239
+ readonly inspector: InspectorProvider = {
240
+ properties: (id): PropertyDescriptor[] => {
241
+ const props: PropertyDescriptor[] = [
242
+ { path: 'name', label: 'Name', type: 'string' },
243
+ { path: 'visible', label: 'Visible', type: 'boolean' },
244
+ { path: 'alpha', label: 'Alpha', type: 'number' },
245
+ ];
246
+ const disp = this.a2d.displayObject(id);
247
+ if (disp && 'tint' in disp) props.push({ path: 'tint', label: 'Tint', type: 'color' });
248
+ return props;
249
+ },
250
+ get: (id, path) => {
251
+ const disp = this.a2d.displayObject(id) as (Container & Record<string, unknown>) | null;
252
+ if (!disp) return undefined;
253
+ if (path === 'name') return disp.label;
254
+ if (path === 'tint') return numToHex((disp as { tint?: number }).tint ?? 0xffffff);
255
+ return disp[path];
256
+ },
257
+ set: (id, path, value) => {
258
+ const prev = this.inspector.get(id, path); // snapshot for undo
259
+ this.writeProp(id, path, value);
260
+ this.pushUndo({
261
+ undo: () => this.writeProp(id, path, prev),
262
+ redo: () => this.writeProp(id, path, value),
263
+ });
264
+ },
265
+ };
266
+
267
+ /** Mutate one reflectable field on the live display object (+ overlay for
268
+ * tint/visible, via `AuthoringAdapter2D.set`) — no undo push. */
269
+ private writeProp(id: string, path: string, value: unknown): void {
270
+ const disp = this.a2d.displayObject(id) as (Container & Record<string, unknown>) | null;
271
+ if (!disp) return;
272
+ if (path === 'name') disp.label = value as string;
273
+ else if (path === 'tint') this.a2d.set(id, 'tint', hexToNum(value as string));
274
+ else if (path === 'visible') this.a2d.set(id, 'visible', value as boolean);
275
+ else disp[path] = value;
276
+ this.notify();
277
+ }
278
+
279
+ // --- Overlay-backed undo/redo (v4 §7.1-13 — the world2d port of
280
+ // IngestAuthoringAdapter's "Overlay-backed undo/redo" section). ---
281
+ private readonly undoStack: Array<{ undo: () => void; redo: () => void }> = [];
282
+ private readonly redoStack: Array<{ undo: () => void; redo: () => void }> = [];
283
+ private editStartTransform: Transform2DValue | undefined;
284
+
285
+ private pushUndo(entry: { undo: () => void; redo: () => void }): void {
286
+ this.undoStack.push(entry);
287
+ this.redoStack.length = 0;
288
+ notifyUndoableOp(this); // tag this op on the global timeline
289
+ }
290
+
291
+ undo(): boolean {
292
+ // `false` tells the undo timeline "couldn't act — put the entry back" (see
293
+ // `UndoAdapter` in `./undo-timeline.ts`).
294
+ const e = this.undoStack.pop();
295
+ if (!e) return false;
296
+ e.undo();
297
+ this.redoStack.push(e);
298
+ return true;
299
+ }
300
+
301
+ redo(): boolean {
302
+ const e = this.redoStack.pop();
303
+ if (!e) return false;
304
+ e.redo();
305
+ this.undoStack.push(e);
306
+ return true;
307
+ }
308
+
309
+ subscribe(listener: () => void): () => void {
310
+ this.listeners.add(listener);
311
+ return () => this.listeners.delete(listener);
312
+ }
313
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Browser-mode EXTERNAL-change detection for the open scene (Phase A4).
3
+ *
4
+ * A2 (`browser-watch-reload.ts`) derives change events from the storage
5
+ * backend's OWN writes — perfect for the editor's autosave echo, but blind to
6
+ * edits made out-of-band: an agent's `apply-diff` writing the `.vscn.json`
7
+ * straight to disk, another editor, a `git checkout`. The File System Access
8
+ * backend points at a real on-disk folder that other tools can write, and the
9
+ * browser gets no OS file-change notification for it — so the only way to learn
10
+ * about an external edit is to poll.
11
+ *
12
+ * This polls the CURRENTLY-OPEN scene file's content on an interval and, when it
13
+ * differs from the last content we observed, delivers it through the SAME
14
+ * `onSceneChanged` consumer A2 feeds (`connectSceneEvents` →
15
+ * `applyExternalUpdate`). That reuse is deliberate: the store's own-echo guard
16
+ * (`applyExternalUpdate` skips when `rawContent === _lastSavedContent`) already
17
+ * drops the editor's own saves, so a self-write that the poll happens to catch
18
+ * is harmlessly ignored downstream, and only a genuine external edit reloads.
19
+ *
20
+ * Scope: the open scene only (the roadmap's A4). Component-source external edits
21
+ * (→ hot-swap) are a later extension; this is the piece the gate's "genuine
22
+ * external edits arrive once A4 lands" note refers to. Gated to the FSA backend
23
+ * by the caller — OPFS is a private-origin sandbox with no external writer.
24
+ */
25
+
26
+ import type { StorageBackend } from './storage';
27
+ import { getStorageBackend } from './storage';
28
+
29
+ export interface SceneWatchState {
30
+ /** Last scene content this watcher observed (null until the first poll). */
31
+ lastContent: string | null;
32
+ }
33
+
34
+ /**
35
+ * One poll step (exported for unit tests — the interval driver just repeats it).
36
+ *
37
+ * The FIRST observation only establishes a baseline and never delivers: on watch
38
+ * start the on-disk content equals what the editor already has loaded, so firing
39
+ * would be a spurious reload. Every later change from that baseline delivers.
40
+ */
41
+ export async function pollExternalSceneOnce(
42
+ state: SceneWatchState,
43
+ getScenePath: () => string | null,
44
+ backend: StorageBackend,
45
+ deliver: (event: { path: string; content: string }) => void,
46
+ ): Promise<void> {
47
+ const path = getScenePath();
48
+ if (!path) return;
49
+ let content: string;
50
+ try {
51
+ content = await backend.read(path);
52
+ } catch {
53
+ // File missing/unreadable this tick (mid-write, removed) — try again next.
54
+ return;
55
+ }
56
+ if (content === state.lastContent) return;
57
+ const isBaseline = state.lastContent === null;
58
+ state.lastContent = content;
59
+ if (isBaseline) return; // establish baseline silently
60
+ deliver({ path, content });
61
+ }
62
+
63
+ /**
64
+ * Poll the open scene for external edits. `getScenePath` is read live each tick
65
+ * (so switching the open scene re-baselines automatically). Returns an
66
+ * unsubscribe that stops the interval. Overlapping ticks are coalesced — a slow
67
+ * read never stacks up.
68
+ */
69
+ export function watchExternalSceneChanges(
70
+ getScenePath: () => string | null,
71
+ deliver: (event: { path: string; content: string }) => void,
72
+ opts: { backend?: StorageBackend; intervalMs?: number } = {},
73
+ ): () => void {
74
+ const backend = opts.backend ?? getStorageBackend();
75
+ const intervalMs = opts.intervalMs ?? 1500;
76
+ const state: SceneWatchState = { lastContent: null };
77
+ let stopped = false;
78
+ let inFlight = false;
79
+
80
+ const tick = async (): Promise<void> => {
81
+ if (inFlight) return;
82
+ inFlight = true;
83
+ try {
84
+ await pollExternalSceneOnce(state, getScenePath, backend, deliver);
85
+ } finally {
86
+ inFlight = false;
87
+ }
88
+ };
89
+
90
+ const timer = setInterval(() => {
91
+ if (!stopped) void tick();
92
+ }, intervalMs);
93
+
94
+ return () => {
95
+ stopped = true;
96
+ clearInterval(timer);
97
+ };
98
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Browser play entry — a STATIC, dynamic-import-free version of the template's
3
+ * `src/scripts/main.ts` for the static/browser editor build.
4
+ *
5
+ * The static host has no dev server, so play mode can't `import('/@fs/.../main.ts')`.
6
+ * Instead this file is pre-bundled (see `scripts/bundle-project-scripts.mjs`) into a
7
+ * single self-contained ESM (`public/project-scripts.bundle.js`) with all engine +
8
+ * vendor modules kept EXTERNAL, then loaded in-browser via esbuild-wasm transpile
9
+ * (`browser-project-scripts.ts` → `importFromSource`), which shims each external
10
+ * import to the editor's LIVE singleton.
11
+ *
12
+ * Dispatch is over `SETUP_BY_EXAMPLE_ID`, a plain exampleId → setup map of STATIC
13
+ * imports spanning `examples/<id>/src/...` (repo-root sibling packages — see
14
+ * docs/EXAMPLES-AS-PROJECTS-DESIGN.md §4), so the bundle has zero dynamic imports.
15
+ * That map is not hand-maintained: `generated/setup-by-example.generated.ts` is
16
+ * GENERATED from each example folder's `vgai.game.json` manifest by
17
+ * `scripts/bundle-project-scripts.mjs` — regenerate with
18
+ * `node scripts/bundle-project-scripts.mjs` after editing an example manifest.
19
+ *
20
+ * Design note (Adjudication H's plumbing point, closed by Track H): which
21
+ * example to run is threaded through `EditorPreview.exampleId`
22
+ * (`@engine/runtime/types`) — the ONLY channel a caller has into this bundle's
23
+ * exported `setup(ctx, editor)`, since `GameSetupFn`'s signature is fixed and
24
+ * used broadly. The caller is play-mode.ts's legacy browser branch, fed by the
25
+ * hosted `?project=<id>` routing (`hosted-example.ts`); undefined still means
26
+ * the generic scene-loader fallback. `loadBrowserProjectScripts()` itself
27
+ * stays a plain no-arg loader — it just returns whatever `setup` the bundle
28
+ * exports, unmodified.
29
+ */
30
+
31
+ import { setAssetPrefix } from '@engine/loader';
32
+ import type { EditorPreview, GameCleanup, GameContext } from '@engine/runtime/types';
33
+ import { loadScene, loadSceneFromData } from '@engine/scene/scene-loader';
34
+ import { applyScenePostProcessing } from '@engine/setup/setup-renderer';
35
+ // The generic fallback's component registry is still the template's (soon-to-be
36
+ // minimal) `src/scripts/registry.ts` — unrelated to example dispatch, and this
37
+ // migration wave does not touch the template layout (a later wave does).
38
+ import { componentRegistry, gameComponents } from '../template/src/scripts/registry';
39
+ import { SETUP_BY_EXAMPLE_ID } from './generated/setup-by-example.generated';
40
+
41
+ const DEFAULT_SCENE = 'scenes/default.vscn.json';
42
+
43
+ /** Exported so the editor inspector can read component `static schema`s. */
44
+ export const components = gameComponents;
45
+
46
+ export async function setup(ctx: GameContext, editor?: EditorPreview): Promise<GameCleanup> {
47
+ // If the caller (Track H's `?project=<id>` routing, or a future picker)
48
+ // asked for a specific bundled example, delegate to its setup (registers the
49
+ // correct gameplay systems + component registries) after namespacing
50
+ // relative asset fetches to that example's own copied public/ output (see
51
+ // vite-plugin-example-assets.ts). An exampleId with NO dispatch entry (e.g.
52
+ // a server-room example the static host can't run — see the generated
53
+ // file's header) falls through to the generic loader below, KEEPING the
54
+ // example prefix so the open scene's relative asset refs still resolve.
55
+ if (editor?.exampleId) {
56
+ setAssetPrefix(`/examples/${editor.exampleId}/`);
57
+ const exampleSetup = SETUP_BY_EXAMPLE_ID[editor.exampleId];
58
+ if (exampleSetup) {
59
+ return exampleSetup(ctx);
60
+ }
61
+ } else {
62
+ // Generic fallback prefix: `editor.assetPrefix` when the editor is playing
63
+ // a bundled example's non-default scene (Track H `?scene=` override),
64
+ // otherwise the deploy root — always reset here so a previously mounted
65
+ // example's namespaced prefix never leaks into this path.
66
+ setAssetPrefix(editor?.assetPrefix ?? '/');
67
+ }
68
+
69
+ // Generic fallback: load the scene with the full component registry so ECS
70
+ // components apply, but no example-specific gameplay systems are registered.
71
+ const loadOpts = {
72
+ scene: ctx.scene,
73
+ rapierWorld: ctx.rapierWorld,
74
+ rapier: ctx.rapier,
75
+ physics: ctx.physics,
76
+ audioListener: ctx.audio.listener,
77
+ componentRegistry,
78
+ componentManager: ctx.components,
79
+ particleRenderer: ctx.particles.batchedRenderer,
80
+ renderer: ctx.composer.getRenderer(),
81
+ };
82
+
83
+ if (editor?.viewportCamera) {
84
+ ctx.camera.position.fromArray(editor.viewportCamera.position);
85
+ ctx.camera.quaternion.fromArray(editor.viewportCamera.quaternion);
86
+ } else {
87
+ ctx.camera.position.set(3, 3, 3);
88
+ ctx.camera.lookAt(0, 0.5, 0);
89
+ }
90
+
91
+ const sceneInstance = editor
92
+ ? await loadSceneFromData(editor.sceneData, loadOpts)
93
+ : await loadScene(DEFAULT_SCENE, loadOpts);
94
+
95
+ if (sceneInstance.environment?.postProcessing) {
96
+ applyScenePostProcessing(
97
+ ctx.composer,
98
+ ctx.composer.getRenderer(),
99
+ ctx.scene,
100
+ ctx.camera,
101
+ sceneInstance.environment,
102
+ );
103
+ }
104
+
105
+ ctx.systems.add('postPhysics', (dt) => sceneInstance.update(dt));
106
+
107
+ return {
108
+ dispose() {
109
+ sceneInstance.dispose();
110
+ },
111
+ };
112
+ }
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Browser-build play-mode script loader.
3
+ *
4
+ * The static/browser build has no dev server, so play mode can't
5
+ * `import('/@fs/.../src/scripts/main.ts')`. Instead we ship the project scripts
6
+ * pre-bundled to a single self-contained ESM (`public/project-scripts.bundle.js`,
7
+ * built by `scripts/bundle-project-scripts.mjs`) with all engine + vendor modules
8
+ * kept EXTERNAL, and run it through the same in-browser transpile path the
9
+ * Playground uses (`importFromSource`). Each external bare import is shimmed to
10
+ * the editor's LIVE singleton via `registerRuntimeModules`, so the bundled
11
+ * examples share the editor's THREE / GameComponent / physics world.
12
+ */
13
+
14
+ import type { GameComponentClass } from '@engine/ecs/game-component';
15
+ import type { GameSetupFn } from '@engine/runtime/types';
16
+ import { importFromSource, registerRuntimeModules } from './browser-transpile';
17
+ import { type ProjectScripts, setLoadedComponents } from './script-loader';
18
+
19
+ let _modulesRegistered = false;
20
+
21
+ /**
22
+ * One lazy loader per external specifier the bundle imports — the keys ARE the
23
+ * registered-specifier list (exported below for the drift-guard test). Lazy
24
+ * (only awaited on first Play) so these heavy modules aren't pulled into the
25
+ * editor's startup chunk. The list must mirror the externals actually present
26
+ * in `project-scripts.bundle.js` — a new external missing here makes
27
+ * `importFromSource` throw "not a registered runtime module" AT PLAY TIME in
28
+ * the hosted build only, which is exactly how the Track E2 starfighter
29
+ * relocation silently broke hosted Play (caught live in Track H's H3
30
+ * verification: RGBELoader/BufferGeometryUtils were bundled but unregistered).
31
+ * `browser-bundle-runtime-modules.test.ts` now fails CI on that drift.
32
+ */
33
+ const BUNDLE_RUNTIME_MODULE_LOADERS: Record<string, () => Promise<unknown>> = {
34
+ three: () => import('three'),
35
+ 'three/addons/controls/OrbitControls.js': () => import('three/addons/controls/OrbitControls.js'),
36
+ 'three/addons/interactive/SelectionBox.js': () =>
37
+ import('three/addons/interactive/SelectionBox.js'),
38
+ 'three/addons/loaders/RGBELoader.js': () => import('three/addons/loaders/RGBELoader.js'),
39
+ 'three/addons/utils/BufferGeometryUtils.js': () =>
40
+ import('three/addons/utils/BufferGeometryUtils.js'),
41
+ 'three/addons/utils/SkeletonUtils.js': () => import('three/addons/utils/SkeletonUtils.js'),
42
+ 'three/examples/jsm/animation/CCDIKSolver.js': () =>
43
+ import('three/examples/jsm/animation/CCDIKSolver.js'),
44
+ 'three.quarks': () => import('three.quarks'),
45
+ 'quarks.core': () => import('quarks.core'),
46
+ postprocessing: () => import('postprocessing'),
47
+ gsap: () => import('gsap'),
48
+ zod: () => import('zod'),
49
+ react: () => import('react'),
50
+ 'react/jsx-runtime': () => import('react/jsx-runtime'),
51
+ 'react-dom/client': () => import('react-dom/client'),
52
+ '@engine/adapter': () => import('@engine/adapter'),
53
+ '@engine/ai/navigation': () => import('@engine/ai/navigation'),
54
+ '@engine/animation/xstate-animation-binding': () =>
55
+ import('@engine/animation/xstate-animation-binding'),
56
+ '@engine/audio': () => import('@engine/audio'),
57
+ '@engine/data/data-asset': () => import('@engine/data/data-asset'),
58
+ '@engine/ecs/game-component': () => import('@engine/ecs/game-component'),
59
+ '@engine/scene/scene-loader': () => import('@engine/scene/scene-loader'),
60
+ '@engine/scene/scene-query': () => import('@engine/scene/scene-query'),
61
+ '@engine/scene/user-data': () => import('@engine/scene/user-data'),
62
+ '@engine/setup/setup-renderer': () => import('@engine/setup/setup-renderer'),
63
+ // littlest-tokyo (GLTF quick-start) extras:
64
+ '@engine/loader': () => import('@engine/loader'),
65
+ '@engine/scene/parse': () => import('@engine/scene/parse'),
66
+ // E5 — XState-driven character animation (third-person/rts/third-person-arena):
67
+ xstate: () => import('xstate'),
68
+ };
69
+
70
+ /** The bundle-external specifiers the browser build can shim at Play time —
71
+ * exported for `browser-bundle-runtime-modules.test.ts` (bundle-drift guard). */
72
+ export const REGISTERED_BUNDLE_SPECIFIERS: readonly string[] = Object.keys(
73
+ BUNDLE_RUNTIME_MODULE_LOADERS,
74
+ );
75
+
76
+ async function ensureRuntimeModulesRegistered(): Promise<void> {
77
+ if (_modulesRegistered) return;
78
+ const entries = await Promise.all(
79
+ Object.entries(BUNDLE_RUNTIME_MODULE_LOADERS).map(
80
+ async ([specifier, load]) => [specifier, (await load()) as Record<string, unknown>] as const,
81
+ ),
82
+ );
83
+ const mods: Record<string, Record<string, unknown>> = Object.fromEntries(entries);
84
+ registerRuntimeModules(mods);
85
+ _modulesRegistered = true;
86
+ }
87
+
88
+ /**
89
+ * Load the pre-bundled project scripts in the browser build: register live
90
+ * runtime modules, fetch the bundle, transpile+import it in-browser, and return
91
+ * its `setup` / `components`. Mirrors `loadProjectScripts`'s return shape.
92
+ */
93
+ export async function loadBrowserProjectScripts(): Promise<ProjectScripts> {
94
+ await ensureRuntimeModulesRegistered();
95
+
96
+ const url = `${import.meta.env.BASE_URL}project-scripts.bundle.js`;
97
+ const res = await fetch(url);
98
+ if (!res.ok) {
99
+ throw new Error(
100
+ `Failed to fetch ${url} (${res.status}) — is the project-scripts bundle built?`,
101
+ );
102
+ }
103
+ const source = await res.text();
104
+
105
+ const mod = await importFromSource(source, 'project-scripts.bundle.js');
106
+ if (typeof mod['setup'] !== 'function') {
107
+ throw new Error('project-scripts bundle does not export a setup() function');
108
+ }
109
+ const components = mod['components'] as Record<string, GameComponentClass> | undefined;
110
+ setLoadedComponents(components ?? {});
111
+ return { setup: mod['setup'] as GameSetupFn, components };
112
+ }