@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,211 @@
1
+ import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs';
2
+ import { join, resolve } from 'node:path';
3
+ import { collectAssetPaths } from '@vgai/engine/scene/asset-paths';
4
+ import {
5
+ type ComponentRegistry,
6
+ isGameComponentClass,
7
+ } from '@vgai/engine/scene/component-registry';
8
+ import { parsePrefabFile, parseSceneFile, SceneParseError } from '@vgai/engine/scene/parse';
9
+ import type { SceneEntity } from '@vgai/engine/scene/scene-types';
10
+ import { ASSET_CONTENT_EXTENSIONS, validateAssetContentFile } from './validate-asset-content';
11
+
12
+ // ---------------------------------------------------------------------------
13
+ // Usage: npx tsx validate-scenes.ts [folder] [--components]
14
+ //
15
+ // Single source of truth for scene validation, shipped IN the project template so
16
+ // every scaffolded game gets a working `npm run validate-scenes` (the engine is
17
+ // imported via the published `@vgai/engine/*` subpath exports, which resolve in
18
+ // both this monorepo and a generated project — unlike the old `../scripts/...`
19
+ // path that only existed inside the monorepo).
20
+ //
21
+ // Validates all .vscn.json and .prefab.json files found recursively under
22
+ // <folder>/public/ — plus all .mat.json/.inputmap.json/
23
+ // .instances.json files, CONTENT-parsed through the engine's asset-content
24
+ // Zod schemas the runtime rejects them with (previously these formats were
25
+ // only existence-checked when a scene referenced them; a present-but-
26
+ // malformed asset passed this command and failed at load).
27
+ // Defaults to the current working directory.
28
+ //
29
+ // Checks:
30
+ // 1. Structural validity (Zod schema parse) — scenes, prefabs, AND the
31
+ // fetched-asset formats (.mat.json/.inputmap.json/.instances.json)
32
+ // 2. Asset references point to files that exist on disk
33
+ // 3. Components exist in the registry (if --components flag provided)
34
+ // ---------------------------------------------------------------------------
35
+
36
+ const args = process.argv.slice(2);
37
+ const rootDir = resolve(args[0] ?? '.');
38
+ const publicDir = join(rootDir, 'public');
39
+
40
+ if (!existsSync(publicDir)) {
41
+ console.error(`No public/ directory found in ${rootDir}`);
42
+ process.exit(1);
43
+ }
44
+
45
+ // Optionally load component registry for component-level validation.
46
+ // Pass --components to enable (requires boilerplate examples to be available).
47
+ const validateComponents = args.includes('--components');
48
+ let componentRegistry: ComponentRegistry | null = null;
49
+
50
+ if (validateComponents) {
51
+ // Import the project's component registry.
52
+ // The registry path is relative to the root folder being validated.
53
+ const registryPath = join(rootDir, 'src/scripts/registry');
54
+ try {
55
+ const mod = await import(registryPath);
56
+ componentRegistry = mod.componentRegistry;
57
+ if (!componentRegistry) {
58
+ console.error(`Registry at ${registryPath} does not export "componentRegistry"`);
59
+ process.exit(1);
60
+ }
61
+ } catch (e) {
62
+ console.error(
63
+ `--components requires a project with src/scripts/registry.ts exporting componentRegistry.\n` +
64
+ ` Looked in: ${registryPath}\n` +
65
+ ` Error: ${e}`,
66
+ );
67
+ process.exit(1);
68
+ }
69
+ }
70
+
71
+ // ---------------------------------------------------------------------------
72
+
73
+ function collectFiles(dir: string, ext: string): string[] {
74
+ const result: string[] = [];
75
+ if (!existsSync(dir)) return result;
76
+ for (const entry of readdirSync(dir)) {
77
+ const full = join(dir, entry);
78
+ if (statSync(full).isDirectory()) {
79
+ result.push(...collectFiles(full, ext));
80
+ } else if (entry.endsWith(ext)) {
81
+ result.push(full);
82
+ }
83
+ }
84
+ return result;
85
+ }
86
+
87
+ function validateEntity(
88
+ entity: SceneEntity,
89
+ registry: ComponentRegistry,
90
+ file: string,
91
+ errors: string[],
92
+ ): void {
93
+ if (entity.components) {
94
+ for (const [name, data] of Object.entries(entity.components)) {
95
+ const Klass = registry[name];
96
+ if (!Klass) {
97
+ errors.push(`${file}: entity "${entity.name}" has unknown component "${name}"`);
98
+ continue;
99
+ }
100
+ // GameComponent v2: a registry entry is a GameComponentClass whose editable
101
+ // fields are declared on its `static schema` (a Zod object). Derive the valid
102
+ // field names from the schema's shape. Components with no schema (tag
103
+ // components like Player) accept no fields.
104
+ const validFields =
105
+ isGameComponentClass(Klass) && Klass.schema ? Object.keys(Klass.schema.shape) : [];
106
+ for (const field of Object.keys(data)) {
107
+ if (!validFields.includes(field)) {
108
+ errors.push(
109
+ `${file}: entity "${entity.name}" component "${name}" has unknown field "${field}" (valid: ${validFields.join(', ') || '(none)'})`,
110
+ );
111
+ }
112
+ }
113
+ }
114
+ }
115
+ if (entity.children) {
116
+ for (const child of entity.children) {
117
+ validateEntity(child, registry, file, errors);
118
+ }
119
+ }
120
+ }
121
+
122
+ function validateAssetPaths(assetPaths: Set<string>, file: string, errors: string[]): void {
123
+ for (const assetPath of assetPaths) {
124
+ const diskPath = join(publicDir, assetPath.replace(/^\//, ''));
125
+ if (!existsSync(diskPath)) {
126
+ errors.push(`${file}: broken asset reference "${assetPath}" — file not found`);
127
+ }
128
+ }
129
+ }
130
+
131
+ // ---------------------------------------------------------------------------
132
+
133
+ const errors: string[] = [];
134
+
135
+ const sceneFiles = collectFiles(publicDir, '.vscn.json');
136
+ const prefabFiles = collectFiles(publicDir, '.prefab.json');
137
+ const totalFiles = sceneFiles.length + prefabFiles.length;
138
+
139
+ // Content-parse the three fetched-asset formats through their engine Zod
140
+ // schemas (previously only existence-checked, and only when referenced by
141
+ // a scene — a present-but-malformed .mat/.inputmap file passed
142
+ // this command and failed at runtime fetch).
143
+ const assetContentFiles = ASSET_CONTENT_EXTENSIONS.flatMap((ext) => collectFiles(publicDir, ext));
144
+ for (const path of assetContentFiles) {
145
+ errors.push(...validateAssetContentFile(path));
146
+ }
147
+
148
+ // Validate scene files
149
+ for (const path of sceneFiles) {
150
+ const raw = JSON.parse(readFileSync(path, 'utf-8'));
151
+
152
+ let data;
153
+ try {
154
+ data = parseSceneFile(raw);
155
+ } catch (e) {
156
+ if (e instanceof SceneParseError) {
157
+ for (const issue of e.issues) {
158
+ errors.push(`${path}: ${issue.path.join('.')}: ${issue.message}`);
159
+ }
160
+ } else {
161
+ errors.push(`${path}: ${e}`);
162
+ }
163
+ continue;
164
+ }
165
+
166
+ if (componentRegistry) {
167
+ for (const entity of data.entities) {
168
+ validateEntity(entity, componentRegistry, path, errors);
169
+ }
170
+ }
171
+
172
+ const assetPaths = collectAssetPaths(data.entities, data.environment);
173
+ validateAssetPaths(assetPaths, path, errors);
174
+ }
175
+
176
+ // Validate prefab files
177
+ for (const path of prefabFiles) {
178
+ const raw = JSON.parse(readFileSync(path, 'utf-8'));
179
+
180
+ let data;
181
+ try {
182
+ data = parsePrefabFile(raw);
183
+ } catch (e) {
184
+ if (e instanceof SceneParseError) {
185
+ for (const issue of e.issues) {
186
+ errors.push(`${path}: ${issue.path.join('.')}: ${issue.message}`);
187
+ }
188
+ } else {
189
+ errors.push(`${path}: ${e}`);
190
+ }
191
+ continue;
192
+ }
193
+
194
+ const assetPaths = collectAssetPaths([data.root]);
195
+ validateAssetPaths(assetPaths, path, errors);
196
+ }
197
+
198
+ // ---------------------------------------------------------------------------
199
+
200
+ if (errors.length > 0) {
201
+ console.error(`Scene validation failed with ${errors.length} error(s):\n`);
202
+ for (const err of errors) {
203
+ console.error(` ${err}`);
204
+ }
205
+ process.exit(1);
206
+ } else {
207
+ console.log(
208
+ `Validated ${totalFiles} scene/prefab file(s) and ${assetContentFiles.length} asset file(s) ` +
209
+ `(.mat/.inputmap/.instances) in ${rootDir} — all OK`,
210
+ );
211
+ }
@@ -0,0 +1,21 @@
1
+ {
2
+ "manifestVersion": 1,
3
+ "name": "my-game",
4
+ "version": "0.1.0",
5
+ "engine": {
6
+ "version": "0.2.0"
7
+ },
8
+ "worlds": [
9
+ {
10
+ "id": "main",
11
+ "kind": "threejs",
12
+ "adapter": "default",
13
+ "entry": "src/scripts/main.ts",
14
+ "scene": "scenes/default.vscn.json"
15
+ }
16
+ ],
17
+ "resolution": {
18
+ "width": 1280,
19
+ "height": 720
20
+ }
21
+ }
@@ -0,0 +1,128 @@
1
+ import path from 'path';
2
+ import { defineConfig } from 'vite';
3
+ // ENGINE-RELATIVE import, deliberately NOT '@vgai/engine/data/vite-plugin-data'
4
+ // (the scaffolder rewrites this path to each project's engine location): Vite's
5
+ // config loader (esbuild) only BUNDLES relative imports — a bare specifier is
6
+ // externalized and loaded by plain node at config time, which cannot load the
7
+ // engine's shipped TypeScript (ERR_UNKNOWN_FILE_EXTENSION: .ts).
8
+ import { vgaiDataCheck } from '../../engine/src/data/vite-plugin-data';
9
+ // Config-relative TS imports get bundled the same way — that is what lets the
10
+ // project's executable Zod schemas reach the build-time check below.
11
+ import { dataAssets } from './src/data/assets';
12
+
13
+ // P2P builds (launched with `?net=p2p-host` / `?net=p2p-join`) route the Colyseus
14
+ // packages onto @vgai/p2p-colyseus's browser-safe compat shims: `browser.ts` exposes
15
+ // `configureP2PColyseusClient`, and `platform.ts` stands in for the node-only
16
+ // `colyseus`/`@colyseus/core` server runtime so `../server/rooms` can be imported
17
+ // into the browser for the in-page host.
18
+ //
19
+ // Default builds must NOT alias — they resolve the REAL Colyseus SDK from
20
+ // node_modules so `net.connect('ws://…')` speaks the real Colyseus wire protocol
21
+ // to a `npm run server` Colyseus server. The compat client's `ws://` branch speaks
22
+ // its own JSON envelope protocol, so aliasing unconditionally silently breaks every
23
+ // non-P2P multiplayer game. Gate per docs/P2P-COLYSEUS-MIGRATION.md Rule 5:
24
+ // "Alias Colyseus packages to @vgai/p2p-colyseus only in P2P builds."
25
+ const p2pBuild = process.env.VGAI_P2P === '1' || process.env.VGAI_P2P === 'true';
26
+
27
+ const p2pAliases = p2pBuild
28
+ ? {
29
+ '@colyseus/sdk': path.resolve(__dirname, '../../p2p-colyseus/src/browser.ts'),
30
+ '@colyseus/schema': path.resolve(__dirname, '../../p2p-colyseus/src/schema.ts'),
31
+ '@colyseus/core': path.resolve(__dirname, '../../p2p-colyseus/src/platform.ts'),
32
+ colyseus: path.resolve(__dirname, '../../p2p-colyseus/src/platform.ts'),
33
+ }
34
+ : {};
35
+
36
+ export default defineConfig({
37
+ // Build-path validation (docs/DATA-TOOLS-DESIGN.md §6.7, build-only): every
38
+ // registered data asset must still parse through its schema (the EXACT
39
+ // parse defineData runs at load), every `"file#key"` ref must resolve, and
40
+ // no `src/tools/` module may reach the shipped bundle (§4: tools are
41
+ // editor-only — never serve testers a build with dev/cheat surfaces).
42
+ plugins: [
43
+ vgaiDataCheck({
44
+ root: __dirname,
45
+ assets: dataAssets.map((a) => ({
46
+ schema: a.schema,
47
+ sourcePath: `src/data/${a.name}.data.json`,
48
+ })),
49
+ }),
50
+ ],
51
+ resolve: {
52
+ alias: {
53
+ '@engine': path.resolve(__dirname, '../../engine/src'),
54
+ ...p2pAliases,
55
+ },
56
+ },
57
+ optimizeDeps: {
58
+ // Pre-bundle deps that are only reached through the dynamically imported
59
+ // example modules (or injected by the JSX transform, like the React
60
+ // runtimes) so Vite does not discover them at runtime, re-optimize, and
61
+ // hard-reload the standalone runtime the first time a game selects them —
62
+ // that mid-session reload resets `window.__vgaiScene`/the menu out from
63
+ // under whatever is driving the page (the e2e dep-optimize-reload flake the
64
+ // 04 spec hit: the first cold select of `editor-tutorial` reloaded on
65
+ // `react/jsx-dev-runtime`). Verified against a cold scaffold: with these
66
+ // listed, a full sweep of the switcher games triggers zero page reloads.
67
+ include: [
68
+ 'gsap',
69
+ 'react/jsx-runtime',
70
+ 'react/jsx-dev-runtime',
71
+ 'react-dom/client',
72
+ 'postprocessing',
73
+ 'three.quarks',
74
+ 'quarks.core',
75
+ '@dimforge/rapier3d-compat',
76
+ ],
77
+ // In a default (non-P2P) build, `main.ts`'s `?net=p2p-host` branch statically
78
+ // references `import('../server/rooms')` — server room classes that pull the
79
+ // node-only `colyseus`/`@colyseus/core` runtime (which imports `@pm2/io`). That
80
+ // branch never executes without `?net=p2p-host`, but vite's dep pre-bundler
81
+ // still follows the dynamic import while scanning and crashes trying to bundle
82
+ // the server runtime for the browser. Excluding the server-only packages makes
83
+ // scanning them a no-op; the branch stays inert at runtime. In P2P builds the
84
+ // alias above already routes these names to browser-safe shims, so no exclude.
85
+ ...(p2pBuild ? {} : { exclude: ['colyseus', '@colyseus/core'] }),
86
+ },
87
+ server: {
88
+ port: 5180,
89
+ // Fail loudly instead of silently rebinding to 5181+ if 5180 is still
90
+ // held by a lingering prior instance. Without this, a stale server left
91
+ // over from an earlier e2e spec file answers the NEW server's readiness
92
+ // probe (which targets the fixed 5180 URL) while the real new instance
93
+ // sits on a different port nobody is talking to -- a hazard previously
94
+ // observed causing a CI e2e spec to hang waiting on the wrong server. See
95
+ // packages/editor/e2e/helpers/server.ts's `startStandaloneServer`.
96
+ strictPort: true,
97
+ // VGAI_WATCH_POLL=1 (or =<ms>): poll for file changes instead of relying
98
+ // on inotify — REQUIRED on WSL when the project lives on a Windows drive
99
+ // (/mnt/c, drvfs: inotify never fires there), or edits to
100
+ // src/data/*.data.json silently never hot-reload into the running game
101
+ // (docs/DATA-TOOLS-DESIGN.md §2.3). Same env var the EDITOR dev server
102
+ // honors, so one knob covers both surfaces. Safe to enable here: this
103
+ // standalone server only watches the project tree, which is small.
104
+ ...(process.env.VGAI_WATCH_POLL
105
+ ? {
106
+ watch: {
107
+ usePolling: true,
108
+ interval:
109
+ Number(process.env.VGAI_WATCH_POLL) > 1 ? Number(process.env.VGAI_WATCH_POLL) : 1000,
110
+ },
111
+ }
112
+ : {}),
113
+ allowedHosts:
114
+ process.env.VITE_ALLOWED_HOSTS === 'all'
115
+ ? true
116
+ : process.env.VITE_ALLOWED_HOSTS?.split(',').filter(Boolean),
117
+ },
118
+ build: {
119
+ target: 'es2022',
120
+ // Same story as optimizeDeps.exclude above, but for the PRODUCTION build:
121
+ // rollup follows `main.ts`'s `import('../server/rooms')` and dies resolving
122
+ // the node-only colyseus runtime (`@pm2/io`) for the browser. Externalizing
123
+ // it leaves a bare dynamic `import('colyseus')` in the never-executed
124
+ // ?net=p2p-host branch (guarded by `configureP2PColyseusClient`, which only
125
+ // exists in aliased P2P builds). P2P builds resolve via the alias instead.
126
+ ...(p2pBuild ? {} : { rollupOptions: { external: ['colyseus', '@colyseus/core'] } }),
127
+ },
128
+ });
@@ -0,0 +1,60 @@
1
+ /**
2
+ * vite-plugin-example-assets — copy every `examples/<id>/public/*` (repo root)
3
+ * into the `--mode web` build's output under `<outDir>/examples/<id>/...`, so
4
+ * a bundled example's relative asset fetches (e.g. `models/LittlestTokyo.glb`)
5
+ * resolve once namespaced by `setAssetPrefix('/examples/<id>/')` (see
6
+ * `packages/editor/src/browser-play-entry.ts`).
7
+ *
8
+ * Since each `examples/<id>/` (T3.5/D11 — docs/EXAMPLES-AS-PROJECTS-DESIGN.md
9
+ * §4) is now an independent sibling package, not a shared package tree, each example's
10
+ * OWN `public/` would otherwise collide at a flat deploy root (two examples
11
+ * both shipping a `models/` folder). Namespacing under `/examples/<id>/`
12
+ * mirrors `scripts/bundle-project-scripts.mjs`'s per-example dispatch id.
13
+ *
14
+ * Build-only, `--mode web` only (see `projectScriptsBundlePlugin` in
15
+ * vite.config.ts — same gating). Copies in `closeBundle` (not `buildStart` —
16
+ * the build's `outDir` doesn't exist yet at that point; not `writeBundle`
17
+ * either, to run strictly after Vite's own `publicDir` copy so our
18
+ * `examples/` subtree is never raced/clobbered) — verified empirically via
19
+ * `npm run build:web` producing `dist/examples/<id>/...`.
20
+ */
21
+ import { cpSync, existsSync, mkdirSync, readdirSync } from 'node:fs';
22
+ import { isAbsolute, join, resolve } from 'node:path';
23
+ import type { Plugin, ResolvedConfig } from 'vite';
24
+
25
+ export function exampleAssetsPlugin(repoRoot: string): Plugin {
26
+ let resolvedConfig: ResolvedConfig;
27
+ return {
28
+ name: 'vgai:example-assets',
29
+ apply: 'build',
30
+ configResolved(config) {
31
+ resolvedConfig = config;
32
+ },
33
+ closeBundle() {
34
+ const examplesDir = resolve(repoRoot, 'examples');
35
+ const outDir = isAbsolute(resolvedConfig.build.outDir)
36
+ ? resolvedConfig.build.outDir
37
+ : resolve(resolvedConfig.root, resolvedConfig.build.outDir);
38
+
39
+ if (!existsSync(examplesDir)) return;
40
+ const folderIds = readdirSync(examplesDir, { withFileTypes: true })
41
+ .filter((d) => d.isDirectory())
42
+ .map((d) => d.name)
43
+ .sort();
44
+
45
+ for (const id of folderIds) {
46
+ const publicDir = join(examplesDir, id, 'public');
47
+ const manifest = join(examplesDir, id, 'vgai.game.json');
48
+ const dest = join(outDir, 'examples', id);
49
+ // some examples ship no assets of their own
50
+ if (existsSync(publicDir)) cpSync(publicDir, dest, { recursive: true });
51
+ // The manifest is what `?project=<id>` routing resolves a deep link
52
+ // against (hosted-example.ts) — stage it beside the public/ contents.
53
+ if (existsSync(manifest)) {
54
+ mkdirSync(dest, { recursive: true });
55
+ cpSync(manifest, join(dest, 'vgai.game.json'));
56
+ }
57
+ }
58
+ },
59
+ };
60
+ }
@@ -0,0 +1,46 @@
1
+ /**
2
+ * The `vgai-game-globals` transform — prepends `GAME_GLOBALS_PRELUDE` to game
3
+ * source so raw `window`/`document` input listeners resolve to the gated proxies
4
+ * (`gated-globals.ts`) instead of the real globals, making them focus-gated.
5
+ *
6
+ * Shared by BOTH Vite configs, which is the point:
7
+ * - `server/dev.ts` registers it over an opened project's root PLUS the
8
+ * same-realm ingest-fixture roots;
9
+ * - the repo-root `vite.config.ts` registers it over the ingest-fixture roots
10
+ * alone, which is what the hosted `--mode web` static build needs. Before
11
+ * this existed the plugin lived only in `dev.ts`, so a same-realm ingested
12
+ * game ran UNSHADOWED in the hosted editor — its input listeners fired
13
+ * regardless of editor focus. The browser-side analogue of this seam is
14
+ * `applyGameGlobalsShadow` (`browser-transpile.ts`, A3).
15
+ *
16
+ * `dev.ts` builds its Vite server with `configFile: <repo-root>/vite.config.ts`
17
+ * AND adds its own plugins, so in dev BOTH registrations are live and both
18
+ * `transform` hooks see the ingest-fixture modules. The prelude check below is
19
+ * what makes that safe: whichever runs first prepends, the other sees the
20
+ * prelude already there and no-ops. Prepending twice would shadow the shadow —
21
+ * the inner `const window` would resolve to the outer `const window` rather than
22
+ * to the gated proxy — and `const` redeclaration in one scope is a SyntaxError
23
+ * besides.
24
+ */
25
+ import type { Plugin } from 'vite';
26
+ import { shouldShadowGameGlobals } from './server/game-globals-shadow';
27
+ // The DOM-free prelude module, NOT `src/gated-globals.ts`: this file is reachable
28
+ // from `tsconfig.server.json` (via `server/dev.ts`), which compiles without the
29
+ // DOM lib and so cannot see `window`/`document`/`EventListenerOrEventListenerObject`.
30
+ import { GAME_GLOBALS_PRELUDE } from './src/game-globals-prelude';
31
+
32
+ /**
33
+ * `getRoots` is a thunk, not a snapshot: `dev.ts` mutates its root set when a
34
+ * project opens after boot (`onProjectOpened`), and the transform must see that.
35
+ */
36
+ export function gameGlobalsShadowPlugin(getRoots: () => Iterable<string>): Plugin {
37
+ return {
38
+ name: 'vgai-game-globals',
39
+ transform(code: string, id: string) {
40
+ const file = id.split('?')[0]!;
41
+ if (!shouldShadowGameGlobals(file, getRoots())) return null;
42
+ if (code.startsWith(GAME_GLOBALS_PRELUDE)) return null; // already shadowed
43
+ return { code: GAME_GLOBALS_PRELUDE + code, map: null };
44
+ },
45
+ };
46
+ }
@@ -0,0 +1,71 @@
1
+ /**
2
+ * vite-plugin-game-static — serve vendored real-game build output VERBATIM (no vite
3
+ * transform) so an externalized game bundle keeps its bare `import 'pixi.js'` specifiers
4
+ * (resolved by the iframe importmap, not rewritten to /node_modules/.vite/deps) and its
5
+ * AssetPack `.json` spritesheets stay raw JSON (not converted to ESM modules).
6
+ *
7
+ * Requests to `/pixi-game-static/<id>/<path>` are served from
8
+ * `vendor/pixi-games/_host/<id>-dist/<path>` with a correct content-type and zero
9
+ * transformation. This is pure HOST wiring — it never touches the vendored game source.
10
+ */
11
+ import { readFile } from 'node:fs/promises';
12
+ import { extname, join, normalize, resolve } from 'node:path';
13
+ import type { Plugin } from 'vite';
14
+
15
+ // Exported so `vite-plugin-project-game-static.ts` (D-X2, the external-folder
16
+ // verbatim-serving sibling of this plugin) reuses the SAME MIME table rather
17
+ // than drifting a second copy — both plugins serve the identical class of
18
+ // content (a pixi bundle + AssetPack JSON + image/audio/font assets), just
19
+ // rooted at a different directory.
20
+ export const MIME: Record<string, string> = {
21
+ '.mjs': 'text/javascript',
22
+ '.js': 'text/javascript',
23
+ '.json': 'application/json',
24
+ '.png': 'image/png',
25
+ '.webp': 'image/webp',
26
+ '.jpg': 'image/jpeg',
27
+ '.jpeg': 'image/jpeg',
28
+ '.svg': 'image/svg+xml',
29
+ '.mp3': 'audio/mpeg',
30
+ '.ogg': 'audio/ogg',
31
+ '.wav': 'audio/wav',
32
+ '.woff2': 'font/woff2',
33
+ '.woff': 'font/woff',
34
+ '.ttf': 'font/ttf',
35
+ };
36
+
37
+ export function gameStaticPlugin(repoRoot: string): Plugin {
38
+ const distRoot = resolve(repoRoot, 'vendor/pixi-games/_host');
39
+ return {
40
+ name: 'vgai-game-static',
41
+ configureServer(server) {
42
+ server.middlewares.use('/pixi-game-static', async (req, res, next) => {
43
+ try {
44
+ const url = (req.url ?? '').split('?')[0] ?? '';
45
+ // url is like /<id>/<path...>
46
+ const m = /^\/([^/]+)\/(.+)$/.exec(url);
47
+ if (!m) return next();
48
+ // Both groups are guaranteed present by the regex match above
49
+ // (two required capture groups) — `noUncheckedIndexedAccess`
50
+ // types tuple-destructured array elements as possibly-undefined
51
+ // regardless, so assert what the match already proved.
52
+ const id = m[1]!;
53
+ const rel = m[2]!;
54
+ const filePath = normalize(join(distRoot, `${id}-dist`, rel));
55
+ // path-traversal guard: stay inside distRoot
56
+ if (!filePath.startsWith(distRoot)) {
57
+ res.statusCode = 403;
58
+ return res.end('forbidden');
59
+ }
60
+ const buf = await readFile(filePath);
61
+ res.setHeader('Content-Type', MIME[extname(filePath)] ?? 'application/octet-stream');
62
+ res.setHeader('Cache-Control', 'no-cache');
63
+ res.statusCode = 200;
64
+ res.end(buf);
65
+ } catch {
66
+ next();
67
+ }
68
+ });
69
+ },
70
+ };
71
+ }
@@ -0,0 +1,84 @@
1
+ /**
2
+ * vite-plugin-project-game-static — D-X2
3
+ * (docs/EXTERNAL-FOLDER-INGEST-DESIGN.md §2/§6 slice S1): the external-folder
4
+ * sibling of `vite-plugin-game-static.ts`. Serves the CURRENTLY OPEN
5
+ * project's own folder VERBATIM (no vite transform) at
6
+ * `/project-game-static/<path>` -> `<projectRoot>/<path>`, so an external
7
+ * pixi game's manifest can point `bundleUrl`/`assetBaseUrl`/`pixiModuleUrl`
8
+ * at a route that keeps its bare `import 'pixi.js'` un-rewritten (resolved by
9
+ * the iframe importmap instead) and its AssetPack `.json` spritesheets raw —
10
+ * the SAME R-X1 rationale `vite-plugin-game-static.ts`'s header records for
11
+ * the vendored-dist route, just rooted at an arbitrary user folder instead of
12
+ * `vendor/pixi-games/_host`.
13
+ *
14
+ * `getProjectRoot` is a THUNK, not a captured string: `open-project` can
15
+ * switch the current project live (`server/dev.ts`'s `onProjectOpened`
16
+ * callback), and this route must follow the CURRENT project root, not a
17
+ * boot-time snapshot — every request re-reads it. No project open (thunk
18
+ * returns `undefined`) -> `next()`, never a 5xx.
19
+ *
20
+ * `configureServer`-only (local dev server; hosted/browser mode is out of
21
+ * scope by construction — both manifest ingest routes already short-circuit
22
+ * on `getEditorMode() === 'browser'` before ever building a URL that would
23
+ * point here, `ingest-mode.ts:944,994`).
24
+ */
25
+ import { readFile } from 'node:fs/promises';
26
+ import { extname, join, normalize, resolve, sep } from 'node:path';
27
+ import type { Plugin } from 'vite';
28
+ import { MIME } from './vite-plugin-game-static';
29
+
30
+ /**
31
+ * Resolve `rel` against `projectRoot`, refusing anything that escapes it.
32
+ * Plain `startsWith(root)` would wrongly admit a sibling folder that merely
33
+ * shares `root` as a string prefix (e.g. root `/a/b` and filePath `/a/bc/evil`)
34
+ * — the boundary must fall on a real path separator (or be an exact match).
35
+ * Returns `null` for an escape attempt.
36
+ */
37
+ function resolveInsideProjectRoot(projectRoot: string, rel: string): string | null {
38
+ const root = resolve(projectRoot);
39
+ const filePath = normalize(join(root, rel));
40
+ if (filePath === root || filePath.startsWith(root + sep)) return filePath;
41
+ return null;
42
+ }
43
+
44
+ /** `req.url`'s path portion (query stripped), project-root-relative (leading
45
+ * slash removed). Empty for the bare mount path (`/project-game-static`
46
+ * itself, with nothing after it). */
47
+ function requestRelPath(url: string | undefined): string {
48
+ // `.split('?')` always yields at least one element — `noUncheckedIndexedAccess`
49
+ // types index access as possibly-undefined regardless, so fall back to ''.
50
+ const path = (url ?? '').split('?')[0] ?? '';
51
+ return path.startsWith('/') ? path.slice(1) : path;
52
+ }
53
+
54
+ export function projectGameStaticPlugin(getProjectRoot: () => string | undefined): Plugin {
55
+ return {
56
+ name: 'vgai-project-game-static',
57
+ configureServer(server) {
58
+ server.middlewares.use('/project-game-static', async (req, res, next) => {
59
+ const projectRoot = getProjectRoot();
60
+ if (!projectRoot) return next(); // no project open — nothing to serve
61
+
62
+ const rel = requestRelPath(req.url);
63
+ if (!rel) return next();
64
+
65
+ const filePath = resolveInsideProjectRoot(projectRoot, rel);
66
+ if (!filePath) {
67
+ res.statusCode = 403;
68
+ res.end('forbidden');
69
+ return;
70
+ }
71
+
72
+ try {
73
+ const buf = await readFile(filePath);
74
+ res.setHeader('Content-Type', MIME[extname(filePath)] ?? 'application/octet-stream');
75
+ res.setHeader('Cache-Control', 'no-cache');
76
+ res.statusCode = 200;
77
+ res.end(buf);
78
+ } catch {
79
+ next();
80
+ }
81
+ });
82
+ },
83
+ };
84
+ }