@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,21 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "type": "object",
4
+ "properties": {
5
+ "boxSpinSpeed": {
6
+ "default": 0.8,
7
+ "description": "Demo box spin speed, radians/sec — edit tuning.data.json while playing to see live tuning",
8
+ "type": "number",
9
+ "minimum": -5,
10
+ "maximum": 5
11
+ },
12
+ "boxColor": {
13
+ "default": "#4488ff",
14
+ "description": "Demo box color (hex)",
15
+ "type": "string"
16
+ },
17
+ "$schema": {
18
+ "type": "string"
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Tuning schema — the engineer-owned half of a data asset
3
+ * (docs/DATA-TOOLS-DESIGN.md §2.1).
4
+ *
5
+ * Every data asset is a trio in this folder:
6
+ *
7
+ * tuning.schema.ts ← this file: the Zod schema. Code owns the shape.
8
+ * tuning.schema.json ← EMITTED artifact (`npm run emit-schemas`) — gives
9
+ * VS Code validation/autocomplete on the data file.
10
+ * tuning.data.json ← the VALUES. Designer/editor/agent-owned; its first
11
+ * line `"$schema": "./tuning.schema.json"` wires the
12
+ * two together with no dev server running.
13
+ *
14
+ * Annotate every field with `.min/.max/.default/.describe` — the same
15
+ * annotations `GameComponent.static schema` uses. They document the file,
16
+ * constrain edits, and (W2+) become editor widgets.
17
+ *
18
+ * Rule of thumb (project CLAUDE.md carries the doctrine): if a designer
19
+ * should be able to change it, it belongs here — NOT as a constant in a
20
+ * component. Per-entity placement config stays on component schemas.
21
+ */
22
+ import { z } from 'zod';
23
+
24
+ export const TuningSchema = z.object({
25
+ boxSpinSpeed: z
26
+ .number()
27
+ .min(-5)
28
+ .max(5)
29
+ .default(0.8)
30
+ .describe('Demo box spin speed, radians/sec — edit tuning.data.json while playing to see live tuning'),
31
+ boxColor: z.string().default('#4488ff').describe('Demo box color (hex)'),
32
+ });
33
+
34
+ export type Tuning = z.infer<typeof TuningSchema>;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * The live handle for the `tuning` data asset — the blessed consumption shape
3
+ * (docs/DATA-TOOLS-DESIGN.md §2.3, engine `@engine/data/data-asset`).
4
+ *
5
+ * Three lines is the whole wiring:
6
+ * 1. statically import the JSON (bundler-friendly, works in dev and build),
7
+ * 2. `defineData` — Zod parse-on-load (fails loud naming file + field),
8
+ * returning `get()`/`subscribe()`,
9
+ * 3. the `import.meta.hot` line — Vite HMR needs the literal dep path in
10
+ * THIS module, so an edit to tuning.data.json (VS Code, an agent, the
11
+ * future editor Data tab) hot-swaps values into the RUNNING game, no
12
+ * remount, no reload.
13
+ *
14
+ * Consume with `tuning.get()` INSIDE your update loop every frame (never
15
+ * cache fields across frames, or live tuning silently stops working for that
16
+ * field) — see `src/scripts/components/data-spinner.ts` for the worked use.
17
+ */
18
+ import { defineData } from '@engine/data/data-asset';
19
+ import raw from './tuning.data.json';
20
+ import { TuningSchema } from './tuning.schema';
21
+
22
+ export const tuning = defineData(TuningSchema, raw, 'src/data/tuning.data.json');
23
+
24
+ import.meta.hot?.accept('./tuning.data.json', (m) => tuning.hotSwap(m?.['default']));
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Standalone entry point.
3
+ *
4
+ * One `mountGameFromManifest` call (E4, docs/unified-world-editor/
5
+ * 27-visual-react-editing.md §7 E4 — the S-D composer): mounts every world
6
+ * `vgai.game.json` declares — just the one `default`-adapter threejs world
7
+ * for a plain scaffold — through the engine's `registerWorldKind` registry,
8
+ * no example switcher, no HUD dropdown, no discovery glob (that in-app
9
+ * gallery machinery has been deleted), and no hand-built `WorldMountSpec[]`.
10
+ * The default scene is the simple box from `src/scripts/main.ts`.
11
+ *
12
+ * `registerWorldKind('threejs', defaultThreeKindFactory)` opts into the
13
+ * engine's stock threejs resolution (entry module's `adapter`/`setup` export,
14
+ * or a scene-driven world) — see `mount-game.ts`'s doc comment for why this
15
+ * is a plain function the CALLER registers rather than something the engine
16
+ * wires up for you (pixijs/react worlds need the SAME one-line registration
17
+ * with your own factory; see `examples/tri-world/src/main.ts`).
18
+ *
19
+ * `host.loadEntryModule` resolves a manifest-declared `entry` path to an
20
+ * ALREADY-IMPORTED module — a static lookup table keyed by that exact path
21
+ * string, not a runtime `import(path)` (a static bundler can't resolve a
22
+ * computed specifier for a real `dist/` build; see `mount-game.ts`'s
23
+ * `ManifestHost.loadEntryModule` doc comment).
24
+ *
25
+ * No Scene UI setup here (C1, docs/AI-NATIVE-AUTHORING-IMPLEMENTATION-SPEC.md
26
+ * §9): a plain scaffold's default game UI is ordinary React, mounted via
27
+ * `mountUI(ctx.uiContainer, ...)` from `./scripts/main.ts`'s `setup()` (see
28
+ * `./ui/mount-default-hud.tsx`) — no `@vgai/scene-ui` import, no `.vscn`
29
+ * `ui` field. A project that opts INTO the (still-supported, being retired
30
+ * under C5) Scene UI data-language authoring path can wire
31
+ * `registerSceneUIRenderer()` back in itself; the template no longer does
32
+ * it by default.
33
+ */
34
+
35
+ import type { ManifestHost } from '@engine/runtime/mount-game';
36
+ import {
37
+ defaultThreeKindFactory,
38
+ isWorldKindRegistered,
39
+ mountGameFromManifest,
40
+ registerWorldKind,
41
+ } from '@engine/runtime/mount-game';
42
+ import manifest from '../vgai.game.json';
43
+ import * as mainScript from './scripts/main';
44
+
45
+ const DEFAULT_SIGNALING_URL = 'https://vgai-p2p-colyseus.aaron-0ed.workers.dev';
46
+
47
+ // The manifest's `entry`-declaring worlds' modules, statically imported and
48
+ // keyed by the EXACT `entry` path each world declares in `vgai.game.json` —
49
+ // see this file's header comment for why this is a static table, not a
50
+ // dynamic `import(path)`. Add an entry here if you add another entry-driven
51
+ // world to the manifest.
52
+ const ENTRY_MODULES: Readonly<Record<string, unknown>> = {
53
+ 'src/scripts/main.ts': mainScript,
54
+ };
55
+
56
+ // Reopen fix (E4.R1): `registerWorldKind` throws on a double-registration of
57
+ // the same kind (an accidental duplicate is far more likely than an
58
+ // intentional swap — see its own doc comment) — but this module's top level
59
+ // re-runs whenever a hot-accept boundary re-executes it WITHOUT a full page
60
+ // reload, which would otherwise turn a live-reload edit into a hard crash.
61
+ // Guard so a re-execution is a no-op instead of bricking the reload.
62
+ if (!isWorldKindRegistered('threejs')) {
63
+ registerWorldKind('threejs', defaultThreeKindFactory);
64
+ }
65
+
66
+ async function main() {
67
+ try {
68
+ const res = await fetch('project.json');
69
+ if (res.ok) {
70
+ const cfg = await res.json();
71
+ if (cfg.name) document.title = cfg.name;
72
+ }
73
+ } catch {
74
+ /* keep default title */
75
+ }
76
+
77
+ await configureNetworkModeFromUrl();
78
+
79
+ const container = document.getElementById('game-canvas') as HTMLElement;
80
+ const host: ManifestHost = {
81
+ container,
82
+ async loadEntryModule(path) {
83
+ const mod = ENTRY_MODULES[path];
84
+ if (!mod) {
85
+ throw new Error(
86
+ `main.ts: unknown entry module "${path}" declared by vgai.game.json — add it to ` +
87
+ 'ENTRY_MODULES above.',
88
+ );
89
+ }
90
+ return mod;
91
+ },
92
+ };
93
+ const session = await mountGameFromManifest(manifest, host);
94
+ session.resize(window.innerWidth, window.innerHeight);
95
+
96
+ window.addEventListener('resize', () => {
97
+ session.resize(window.innerWidth, window.innerHeight);
98
+ });
99
+ }
100
+
101
+ main().catch(console.error);
102
+
103
+ async function configureNetworkModeFromUrl(): Promise<void> {
104
+ const params = new URLSearchParams(window.location.search);
105
+ const mode = params.get('net');
106
+ if (mode !== 'p2p-host' && mode !== 'p2p-join') return;
107
+
108
+ const sdk = (await import('@colyseus/sdk')) as unknown as {
109
+ configureP2PColyseusClient?: (options: {
110
+ mode:
111
+ | {
112
+ kind: 'p2p-host';
113
+ signalingUrl: string;
114
+ iceServers: RTCIceServer[];
115
+ forceRelay?: boolean;
116
+ }
117
+ | {
118
+ kind: 'p2p-join';
119
+ signalingUrl: string;
120
+ roomId?: string;
121
+ iceServers: RTCIceServer[];
122
+ forceRelay?: boolean;
123
+ };
124
+ rooms?: Record<string, unknown>;
125
+ maxLocalClientsPerRoom?: number;
126
+ }) => void;
127
+ };
128
+ if (!sdk.configureP2PColyseusClient) return;
129
+
130
+ const signalingUrl = params.get('signal') ?? DEFAULT_SIGNALING_URL;
131
+ const forceRelay = params.get('relay') === '1';
132
+ const iceServers: RTCIceServer[] = [{ urls: 'stun:stun.l.google.com:19302' }];
133
+
134
+ if (mode === 'p2p-host') {
135
+ const { rooms } = (await import('../server/rooms')) as {
136
+ rooms: Array<{ name: string; handler: unknown }>;
137
+ };
138
+ sdk.configureP2PColyseusClient({
139
+ mode: { kind: 'p2p-host', signalingUrl, iceServers, forceRelay },
140
+ rooms: Object.fromEntries(rooms.map((room) => [room.name, room.handler])),
141
+ ...(forceRelay ? { maxLocalClientsPerRoom: 1 } : {}),
142
+ });
143
+ return;
144
+ }
145
+
146
+ const roomId = params.get('roomId') ?? params.get('room');
147
+ sdk.configureP2PColyseusClient({
148
+ mode: {
149
+ kind: 'p2p-join',
150
+ signalingUrl,
151
+ ...(roomId !== null ? { roomId } : {}),
152
+ iceServers,
153
+ forceRelay,
154
+ },
155
+ maxLocalClientsPerRoom: 1,
156
+ });
157
+ }
@@ -0,0 +1,45 @@
1
+ import { GameComponent } from '@engine/ecs/game-component';
2
+ import type * as THREE from 'three';
3
+ import { z } from 'zod';
4
+ import { tuning } from '../../data/tuning';
5
+
6
+ /**
7
+ * The template's worked data-asset consumer (docs/DATA-TOOLS-DESIGN.md §6.2
8
+ * — the pattern ships working at birth): spins the default scene's Box at
9
+ * `boxSpinSpeed` and tints it `boxColor`, both read from
10
+ * `src/data/tuning.data.json` through the `tuning` handle.
11
+ *
12
+ * Try it: press ▶, then edit `src/data/tuning.data.json` and save — the box
13
+ * changes speed/color LIVE (Vite HMR, no reload; see src/data/tuning.ts).
14
+ *
15
+ * This component demonstrates BOTH halves of the data-vs-config split
16
+ * (§3.3/§6.2): `boxSpinSpeed`/`boxColor` are PROJECT-WIDE feel, so they live
17
+ * in the data asset and read through `tuning.get()` (every frame — cache a
18
+ * field across frames and live tuning silently stops reaching it). `spinScale`
19
+ * is THIS entity's own knob — per-entity config authored on the component
20
+ * itself via `static schema` (the same shape SceneCamera uses), read live by
21
+ * an inspector tool through `useSelection()` (see `spin.tool.tsx`).
22
+ */
23
+ export class DataSpinner extends GameComponent {
24
+ static schema = z.object({
25
+ spinScale: z
26
+ .number()
27
+ .default(1)
28
+ .describe(
29
+ 'Per-entity multiplier on the shared boxSpinSpeed — the project-wide feel stays in ' +
30
+ "src/data/tuning.data.json; this is the one knob that is THIS entity's own.",
31
+ ),
32
+ });
33
+
34
+ spinScale = 1;
35
+
36
+ update(dt: number) {
37
+ const t = tuning.get();
38
+ this.object3D.rotation.y += t.boxSpinSpeed * this.spinScale * dt;
39
+
40
+ const material = (this.object3D as THREE.Mesh).material as
41
+ | THREE.MeshStandardMaterial
42
+ | undefined;
43
+ if (material?.color) material.color.set(t.boxColor);
44
+ }
45
+ }
@@ -0,0 +1,40 @@
1
+ import { z } from 'zod';
2
+ import { GameComponent } from '@engine/ecs/game-component';
3
+ import type { GameContext } from '@engine/runtime/types';
4
+
5
+ /**
6
+ * Positions the runtime camera on init.
7
+ *
8
+ * Configure x/y/z (camera position) and lookAtX/lookAtY/lookAtZ (look-at
9
+ * target) in the scene JSON. Runs in preRender phase so it can adjust the
10
+ * camera before rendering. One-time framing in init() only — this is the
11
+ * blessed "static scene setup" camera-via-component shape (not a per-frame
12
+ * follow), same category as the one-time `ctx.camera.position.set(...)` calls
13
+ * example `setup()` functions make directly.
14
+ */
15
+ export class SceneCamera extends GameComponent {
16
+ static phase = 'preRender' as const;
17
+
18
+ static schema = z.object({
19
+ x: z.number().default(3).describe('Camera position X'),
20
+ y: z.number().default(3).describe('Camera position Y'),
21
+ z: z.number().default(3).describe('Camera position Z'),
22
+ lookAtX: z.number().default(0).describe('Look-at target X'),
23
+ lookAtY: z.number().default(0.5).describe('Look-at target Y'),
24
+ lookAtZ: z.number().default(0).describe('Look-at target Z'),
25
+ });
26
+
27
+ x = 3;
28
+ y = 3;
29
+ z = 3;
30
+ lookAtX = 0;
31
+ lookAtY = 0.5;
32
+ lookAtZ = 0;
33
+
34
+ init(ctx: GameContext) {
35
+ ctx.camera.position.set(this.x, this.y, this.z);
36
+ ctx.camera.lookAt(this.lookAtX, this.lookAtY, this.lookAtZ);
37
+ }
38
+
39
+ update() {}
40
+ }
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Default game script.
3
+ *
4
+ * When launched from the editor, loads the scene the user is editing.
5
+ * When running standalone, loads the default scene from disk.
6
+ *
7
+ * If the scene matches a known example, delegates to that example's setup
8
+ * function so gameplay systems (movement, camera, AI, etc.) are registered.
9
+ */
10
+
11
+ import type { GameAdapter } from '@engine/adapter';
12
+ import { fromSetup } from '@engine/adapter';
13
+ import type { EditorPreview, GameCleanup, GameContext } from '@engine/runtime/types';
14
+ import { loadScene, loadSceneFromData } from '@engine/scene/scene-loader';
15
+ import { applyScenePostProcessing } from '@engine/setup/setup-renderer';
16
+ import { mountDefaultHud } from '../ui/mount-default-hud';
17
+ import { componentRegistry, gameComponents } from './registry';
18
+
19
+ const DEFAULT_SCENE = 'scenes/default.vscn.json';
20
+
21
+ /** Exported for true HMR — play-mode swaps prototypes on live instances. */
22
+ export const components = gameComponents;
23
+
24
+ // HMR boundary — play-mode.ts handles script hot-reloading via custom events.
25
+ // This prevents Vite from triggering full page reloads when component files change.
26
+ if (import.meta.hot) {
27
+ import.meta.hot.accept();
28
+ }
29
+
30
+ export async function setup(ctx: GameContext, editor?: EditorPreview): Promise<GameCleanup> {
31
+ // Generic fallback: load the scene with the full component registry
32
+ // so ECS components are applied, but no gameplay systems are registered.
33
+ const loadOpts = {
34
+ scene: ctx.scene,
35
+ rapierWorld: ctx.rapierWorld,
36
+ rapier: ctx.rapier,
37
+ physics: ctx.physics,
38
+ audioListener: ctx.audio.listener,
39
+ componentRegistry,
40
+ componentManager: ctx.components,
41
+ // Required for runtime fidelity (P1.4): particles need the batched renderer,
42
+ // skybox/IBL needs the WebGLRenderer for PMREM.
43
+ particleRenderer: ctx.particles.batchedRenderer,
44
+ renderer: ctx.composer.getRenderer(),
45
+ };
46
+
47
+ // Set the INITIAL camera viewpoint BEFORE loading the scene, so that any
48
+ // scene-defined camera or SceneCamera component (which run during loadScene)
49
+ // takes precedence over the editor viewport camera. Generic scenes with no
50
+ // camera component keep this viewpoint.
51
+ if (editor?.viewportCamera) {
52
+ ctx.camera.position.fromArray(editor.viewportCamera.position);
53
+ ctx.camera.quaternion.fromArray(editor.viewportCamera.quaternion);
54
+ } else {
55
+ ctx.camera.position.set(3, 3, 3);
56
+ ctx.camera.lookAt(0, 0.5, 0);
57
+ }
58
+
59
+ const sceneInstance = editor
60
+ ? await loadSceneFromData(editor.sceneData, loadOpts)
61
+ : await loadScene(DEFAULT_SCENE, loadOpts);
62
+
63
+ // Apply scene-defined post-processing (bloom, vignette, DoF, …) if present.
64
+ if (sceneInstance.environment?.postProcessing) {
65
+ applyScenePostProcessing(
66
+ ctx.composer,
67
+ ctx.composer.getRenderer(),
68
+ ctx.scene,
69
+ ctx.camera,
70
+ sceneInstance.environment,
71
+ );
72
+ }
73
+
74
+ // Tick scene animations (GLTF mixers, etc.)
75
+ ctx.systems.add('postPhysics', (dt) => sceneInstance.update(dt));
76
+
77
+ // Default game HUD (C1, docs/AI-NATIVE-AUTHORING-IMPLEMENTATION-SPEC.md §9):
78
+ // ordinary React mounted through the existing `mountUI(ctx.uiContainer, ...)`
79
+ // overlay seam — see `../ui/DefaultHud.tsx`. Delete this line (and the HUD
80
+ // files under `../ui/`) if your game doesn't want it; it is not required by
81
+ // anything else in the template.
82
+ const unmountHud = mountDefaultHud(ctx.uiContainer);
83
+
84
+ return {
85
+ dispose() {
86
+ unmountHud();
87
+ sceneInstance.dispose();
88
+ },
89
+ };
90
+ }
91
+
92
+ /** This example as a {@link GameAdapter} — first-party is one implementer of
93
+ * the same interface an external game implements (no change to setup's body). */
94
+ export const adapter: GameAdapter = fromSetup('default', setup);
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Project component registry.
3
+ *
4
+ * Every component used in .vscn.json files must be registered here. The scene
5
+ * loader and validator use this to map component names to GameComponent classes.
6
+ *
7
+ * This file is copied VERBATIM into every scaffolded project (it lives inside
8
+ * `packages/editor/template/`) — it must never import anything outside the
9
+ * template's own tree (no reaching into repo-root `examples/<id>/`, which
10
+ * doesn't exist once copied elsewhere; a real scaffold's own
11
+ * `scaffold-typecheck` gate catches this).
12
+ *
13
+ * When you delete an example, remove its imports and entries below.
14
+ */
15
+
16
+ import type { GameComponentClass } from '@engine/ecs/game-component';
17
+ import type { ComponentRegistry } from '@engine/scene/component-registry';
18
+ import { DataSpinner } from './components/data-spinner';
19
+ import { SceneCamera } from './components/scene-camera';
20
+
21
+ export const componentRegistry = {
22
+ // GameComponent classes (OOP components with true HMR)
23
+ DataSpinner,
24
+ SceneCamera,
25
+ } satisfies ComponentRegistry;
26
+
27
+ /**
28
+ * Union of all registered component names — discoverable + type-checked rather
29
+ * than stringly-typed. Derived from the registry so it can never drift.
30
+ */
31
+ export type ComponentName = keyof typeof componentRegistry;
32
+
33
+ /**
34
+ * Typed accessor for a registered component class. Prefer this over indexing
35
+ * `componentRegistry` directly: `name` is constrained to `ComponentName`, so
36
+ * typos are caught at compile time and editors can autocomplete the options.
37
+ */
38
+ export function getComponent(name: ComponentName): GameComponentClass {
39
+ return componentRegistry[name];
40
+ }
41
+
42
+ /** GameComponent classes for true HMR. Keyed by class name. */
43
+ export const gameComponents: Record<string, GameComponentClass> = {
44
+ DataSpinner,
45
+ SceneCamera,
46
+ };
@@ -0,0 +1,14 @@
1
+ # src/tools — project tools
2
+
3
+ Editor-docked dev/tuning panels live here: `<name>.tool.tsx` exporting
4
+ `tool = defineTool({...})` plus a default React component — the editor
5
+ discovers them automatically. Two placements: `'dock'` (⚙ tab after the
6
+ built-in bottom tabs — `example.tool.tsx`, the shipped worked example) and
7
+ `'inspector'` (⚙ section in the Inspector when its `match(node, adapter)`
8
+ accepts the selection, e.g. `adapter.components?.list(node.id)` —
9
+ `spin.tool.tsx`). Inside an inspector tool, `useSelection()` (from
10
+ `@engine/react/use-selection`) returns the selected node, its attached
11
+ components, and each component's own authored config, live. Build panels
12
+ from `@editor/widgets` (the editor's own widget kit + tokens) so they look
13
+ native. Editor-only: `npm run build` never bundles this folder — see
14
+ `docs/DATA-TOOLS-DESIGN.md` §3–§4 in the engine checkout.
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Example dock tool — the worked project-tool pattern
3
+ * (docs/DATA-TOOLS-DESIGN.md §3, in the engine checkout). Slightly
4
+ * over-commented on purpose: this file is read far more than it runs.
5
+ *
6
+ * A tool is ONE file, `src/tools/<name>.tool.tsx`, exporting exactly two
7
+ * things:
8
+ * 1. `tool` — metadata via `defineTool` (id, tab title, placement),
9
+ * 2. a default-exported plain React component.
10
+ * The editor discovers it automatically (folder scan — no registration
11
+ * anywhere; delete the file and the tab is gone) and docks it as a ⚙ tab
12
+ * after the built-in bottom tabs. It stays interactive during play mode, so
13
+ * you tune while the game runs.
14
+ *
15
+ * Tools are EDITOR-ONLY dev surfaces: nothing in game code imports
16
+ * `src/tools/`, so `npm run build` never bundles this file — cheats and
17
+ * tuning panels can't leak into a shipped game. Keep game logic out of here.
18
+ *
19
+ * This example closes the full live-tuning loop on the `tuning` data asset
20
+ * (`src/data/` — the trio this project ships working):
21
+ *
22
+ * slider → writeData() → src/data/tuning.data.json written on disk
23
+ * → Vite HMR → tuning.hotSwap() → useData re-renders this panel
24
+ * → and the RUNNING game (DataSpinner reads tuning.get() per frame)
25
+ * picks the value up the same instant, no reload.
26
+ *
27
+ * That's the whole architecture: the file is the document; the editor, this
28
+ * panel, VS Code, and the game are all just views of it.
29
+ */
30
+
31
+ import { useData, writeData } from '@engine/react/use-data';
32
+ import { defineTool } from '@engine/tools/define-tool';
33
+ // A tool imports the project's OWN data handles — the same ones the game
34
+ // reads. One handle, every surface agrees on the values.
35
+ import { tuning } from '../data/tuning';
36
+
37
+ /** The editor reads this export to place the tool (docs/DATA-TOOLS-DESIGN.md
38
+ * §3.1). `id` keys the tab (test id `bottom-tab-tool-tuning`); `title` is
39
+ * the label; `'dock'` is the project-global bottom-tab placement (for the
40
+ * selection-scoped `'inspector'` placement, see `spin.tool.tsx`). */
41
+ export const tool = defineTool({
42
+ id: 'tuning',
43
+ title: 'Tuning',
44
+ placement: 'dock',
45
+ });
46
+
47
+ /** The panel itself: plain React, inline styles (editor chrome around it is
48
+ * editor-styled; what's inside is yours). No providers needed. */
49
+ export default function TuningTool() {
50
+ // useData = live values: re-renders once per accepted edit of the data
51
+ // file, whoever made it (this slider, the editor's Data tab, VS Code, an
52
+ // agent). Read it every render — never copy fields into component state.
53
+ const values = useData(tuning);
54
+
55
+ return (
56
+ <div style={{ padding: 12, fontSize: 12, fontFamily: 'ui-monospace, monospace' }}>
57
+ {/* Live readout — proves the round trip: drag the slider (or edit the
58
+ JSON anywhere else) and watch these values follow the file. */}
59
+ <div style={{ marginBottom: 10, color: '#8c8c8c' }}>
60
+ src/data/tuning.data.json (live):
61
+ <pre style={{ margin: '4px 0 0', color: 'inherit' }}>{JSON.stringify(values, null, 2)}</pre>
62
+ </div>
63
+
64
+ <label style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
65
+ boxSpinSpeed
66
+ <input
67
+ type="range"
68
+ min={-5}
69
+ max={5}
70
+ step={0.1}
71
+ data-testid="tool-slider-boxSpinSpeed"
72
+ // Uncontrolled (defaultValue) on purpose: the authoritative value
73
+ // round-trips through the FILE (write → HMR → useData), which can
74
+ // lag a beat behind the pointer — controlling the thumb from that
75
+ // would make it fight the drag. The readout above is the live view.
76
+ defaultValue={values.boxSpinSpeed}
77
+ // writeData writes the WHOLE file through the editor dev server
78
+ // (only src/data/**/*.data.json is accepted): spread the current
79
+ // values, replace one field. It's async — fire and forget here.
80
+ onChange={(e) =>
81
+ void writeData('src/data/tuning.data.json', {
82
+ ...values,
83
+ boxSpinSpeed: Number(e.target.value),
84
+ })
85
+ }
86
+ />
87
+ <span>{values.boxSpinSpeed}</span>
88
+ </label>
89
+ </div>
90
+ );
91
+ }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Example INSPECTOR tool — the selection-scoped project-tool pattern
3
+ * (docs/DATA-TOOLS-DESIGN.md §3, W3/W3.1, in the engine checkout). Slightly
4
+ * over-commented on purpose: this file is read far more than it runs.
5
+ *
6
+ * Same one-file physics as the dock example (`example.tool.tsx`), different
7
+ * placement: `placement: 'inspector'` + a `match` predicate. Instead of a
8
+ * bottom-dock tab, the editor renders this as a ⚙-marked SECTION in the
9
+ * Inspector rail whenever the current selection satisfies `match` — select
10
+ * the Box (it has a `DataSpinner`) and it appears under the built-in
11
+ * sections; select the Ground (a mesh, but no `DataSpinner`) or a light and
12
+ * it's gone. It stays live during play mode, so you tune while the game runs.
13
+ *
14
+ * Three things this file demonstrates beyond the dock example:
15
+ *
16
+ * 1. `match(node, adapter)` — the selection predicate, now COMPONENT-based
17
+ * rather than shape-based: it asks `adapter.components?.list(node.id)`
18
+ * for the attached component types instead of guessing from `node.kind`.
19
+ * Keep it cheap and pure, it runs on every inspector render.
20
+ * 2. `useSelection()` (`@engine/react/use-selection`, W3.1) — inside the
21
+ * component, this returns the selected node PLUS its attached components
22
+ * and each one's own authored config (`ComponentsProvider.config`). Here
23
+ * it reads `DataSpinner`'s per-entity `spinScale` — read-only in v1;
24
+ * per-entity WRITE-from-tool is future `useTunable`/inspector-set
25
+ * territory. `useSelection` only works inside an inspector tool (it
26
+ * throws a teaching error from a dock tool — there is no selection there).
27
+ * 3. `@editor/widgets` — the editor's OWN widget kit + design tokens,
28
+ * public for project tools (§3.4 "tools are editor citizens"): panels
29
+ * built from it sit flush with the built-in inspector sections and
30
+ * re-skin with the editor. `NumberInput` here is the exact
31
+ * scrub-or-type field the built-in Transform section uses.
32
+ *
33
+ * The component receives `{ node, nodeId }` as props (see
34
+ * `InspectorToolProps` in `@engine/tools/define-tool`) — unchanged by W3.1;
35
+ * richer data arrives through `useSelection()`, not more props. Like every
36
+ * tool, it's editor-only: `npm run build` never bundles this file.
37
+ */
38
+
39
+ import { NumberInput, THEME } from '@editor/widgets';
40
+ import { useData, writeData } from '@engine/react/use-data';
41
+ import { useSelection } from '@engine/react/use-selection';
42
+ import { defineTool, type InspectorToolProps } from '@engine/tools/define-tool';
43
+ // The same project data handle the game (DataSpinner) and the dock example
44
+ // read — one handle, every surface agrees on the values.
45
+ import { tuning } from '../data/tuning';
46
+
47
+ export const tool = defineTool({
48
+ id: 'spin',
49
+ title: 'Spin tuning',
50
+ placement: 'inspector',
51
+ // Show this section only on an entity with a DataSpinner attached — the
52
+ // component-based idiom (rather than guessing from `node.kind`). `node` is
53
+ // null in the no-selection state — return false to stay out of the panel.
54
+ match: (node, adapter) =>
55
+ node !== null &&
56
+ (adapter.components?.list(node.id) ?? []).some((c) => c.type === 'DataSpinner'),
57
+ });
58
+
59
+ export default function SpinInspector({ node }: InspectorToolProps) {
60
+ // Live values: re-renders on every accepted edit of the data file,
61
+ // whoever made it (this field, the dock tool, the DATA tab, VS Code).
62
+ const values = useData(tuning);
63
+
64
+ // The selected entity's own components + their authored config, live
65
+ // (W3.1) — distinct from `values` above: `tuning` is project-wide feel,
66
+ // `spinScale` here is THIS entity's own per-entity knob.
67
+ const { components } = useSelection();
68
+ const spinner = components.find((c) => c.type === 'DataSpinner');
69
+ const spinScale =
70
+ typeof spinner?.config?.['spinScale'] === 'number'
71
+ ? (spinner.config['spinScale'] as number)
72
+ : 1;
73
+
74
+ return (
75
+ <div style={{ display: 'flex', flexDirection: 'column', gap: 6, fontSize: 11 }}>
76
+ <div style={{ color: THEME.textMuted }}>
77
+ {node ? `Selected: ${node.label}` : 'Nothing selected'} — boxSpinSpeed drives the Box's
78
+ DataSpinner (src/data/tuning.data.json, live)
79
+ </div>
80
+ <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
81
+ <span style={{ color: THEME.textMuted, width: 90 }}>boxSpinSpeed</span>
82
+ {/* The editor's own scrub-or-type number field (@editor/widgets):
83
+ drag horizontally to scrub, click to type. writeData writes the
84
+ whole data file through the dev server; HMR flows it back into
85
+ useData above AND the running game. */}
86
+ <NumberInput
87
+ value={values.boxSpinSpeed}
88
+ step={0.1}
89
+ testId="spin-tool-speed"
90
+ style={{ flex: 1 }}
91
+ onChange={(v) =>
92
+ void writeData('src/data/tuning.data.json', { ...values, boxSpinSpeed: v })
93
+ }
94
+ />
95
+ </div>
96
+ <div style={{ color: THEME.textMuted }} data-testid="spin-tool-entity-scale">
97
+ this entity's spinScale: {spinScale} ×
98
+ </div>
99
+ </div>
100
+ );
101
+ }