@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,152 @@
1
+ /**
2
+ * E1.a (spec-27 §7 E1 / §9 "UIEditor chrome, palette, … MIGRATE" row) — the
3
+ * UI-node creation palette, surfaced in the MAIN editor (`editor.html`) for
4
+ * whichever world's scene-UI child is currently live. Reuses `ui-editor/
5
+ * palette.tsx`'s `UIPalette` (the harness component) UNCHANGED — its only
6
+ * prop is `store: UIEditStore`, so pointing it at the SAME `UIEditStore`
7
+ * instance `edit-mode-authoring.ts`'s `buildUIChild` constructed for the open
8
+ * scene wires creation through the real undo timeline (`UIEditStore`'s
9
+ * constructor already calls `registerUndoAdapter(this)`) and the real
10
+ * `.vscn` autosave (`onCommit` → `EditorStore.setSceneUI`) for free — no new
11
+ * store, no new undo plumbing.
12
+ *
13
+ * Also carries the E1.c "instantiate a saved prefab" surface (§8 "Scene-UI
14
+ * extras: … prefabs"): a list of `.uiprefab.json` assets under `ui-prefabs/`
15
+ * with a one-click instantiate into the current selection (or the first
16
+ * root) — the SAME "target the selected container, else first root" rule
17
+ * `scene-ui-layer.ts`'s registry component-insert strip (`componentStrip`)
18
+ * uses: a selection that isn't a container falls straight through to the
19
+ * first root. This is NOT the palette's own `add()` (`ui-editor/palette.tsx`),
20
+ * which has an extra middle tier — a selection that isn't a container falls
21
+ * back to that selection's PARENT first, and only THEN to the first root.
22
+ * (The SAVE half of the prefab flow is the hierarchy context-menu item,
23
+ * `ui-prefab-hierarchy-menu.ts` + `ui-prefab-dialog-host.tsx`.)
24
+ *
25
+ * Renders nothing when no scene-UI child is installed (§3.1's install gate —
26
+ * a ui-less scene has nothing to author here yet; see `ui-hierarchy-menu.ts`'s
27
+ * "Add UI Canvas" action for how a scene gets its first one).
28
+ */
29
+
30
+ import { useEffect, useState } from 'react';
31
+ import type { UIAuthoringAdapter } from '../authoring/ui-authoring-adapter';
32
+ import { Panel } from '../components/primitives/Panel';
33
+ import { findNode } from '../ui-editor/node-ops';
34
+ import { UIPalette } from '../ui-editor/palette';
35
+ import { instantiateUIPrefab } from '../ui-editor/ui-prefab';
36
+ import { useActiveSceneUIAdapter } from './active-scene-ui';
37
+ import {
38
+ fetchUIPrefabFile,
39
+ listUIPrefabAssets,
40
+ UI_PREFAB_SAVED_EVENT,
41
+ type UIPrefabAssetEntry,
42
+ } from './ui-prefab-io';
43
+
44
+ /** Mirrors `ui-editor/palette.tsx`'s own unexported `isContainer` / the
45
+ * identical set in `authoring/scene-ui-layer.ts` — an instantiated prefab
46
+ * nests under the selected container, else the first root. */
47
+ const CONTAINER_KINDS = new Set([
48
+ 'canvas',
49
+ 'box',
50
+ 'grid',
51
+ 'stack',
52
+ 'scroll',
53
+ 'panel',
54
+ 'center',
55
+ 'wrap',
56
+ 'aspect',
57
+ 'sizebox',
58
+ 'tab',
59
+ ]);
60
+
61
+ export function SceneUIPalettePanel(): React.ReactNode {
62
+ const adapter = useActiveSceneUIAdapter();
63
+ if (!adapter) return null;
64
+ return (
65
+ <Panel
66
+ name="UI Palette"
67
+ data-testid="scene-ui-palette-panel"
68
+ style={{ width: 200, borderRight: '1px solid #333', overflowY: 'auto' }}
69
+ >
70
+ <UIPalette store={adapter.uiStore} />
71
+ <ScenePrefabsSection adapter={adapter} />
72
+ </Panel>
73
+ );
74
+ }
75
+
76
+ function ScenePrefabsSection({ adapter }: { adapter: UIAuthoringAdapter }): React.ReactNode {
77
+ const [prefabs, setPrefabs] = useState<UIPrefabAssetEntry[]>([]);
78
+
79
+ useEffect(() => {
80
+ let cancelled = false;
81
+ const refresh = () => {
82
+ void listUIPrefabAssets().then((list) => {
83
+ if (!cancelled) setPrefabs(list);
84
+ });
85
+ };
86
+ refresh();
87
+ window.addEventListener(UI_PREFAB_SAVED_EVENT, refresh);
88
+ return () => {
89
+ cancelled = true;
90
+ window.removeEventListener(UI_PREFAB_SAVED_EVENT, refresh);
91
+ };
92
+ }, []);
93
+
94
+ const store = adapter.uiStore;
95
+
96
+ const instantiate = async (entry: UIPrefabAssetEntry): Promise<void> => {
97
+ const prefab = await fetchUIPrefabFile(entry.path);
98
+ if (!prefab) return;
99
+ const instance = instantiateUIPrefab(prefab);
100
+ const primary = store.primary;
101
+ const primaryLoc = primary ? findNode(store.roots, primary) : null;
102
+ const parentId =
103
+ primaryLoc && CONTAINER_KINDS.has(primaryLoc.node.kind)
104
+ ? primary!
105
+ : ((store.roots[0]?.id as string | undefined) ?? null);
106
+ if (parentId) store.addNodeValue(parentId, instance);
107
+ };
108
+
109
+ return (
110
+ <div data-testid="scene-ui-prefabs" style={{ padding: 8, fontFamily: 'monospace' }}>
111
+ <div
112
+ style={{
113
+ fontSize: 10,
114
+ textTransform: 'uppercase',
115
+ color: '#888',
116
+ margin: '4px 0',
117
+ letterSpacing: 0.5,
118
+ }}
119
+ >
120
+ Prefabs
121
+ </div>
122
+ {prefabs.length === 0 && (
123
+ <div style={{ color: '#666', fontSize: 10 }}>
124
+ none yet — right-click a node → "Save UI Node as Prefab..."
125
+ </div>
126
+ )}
127
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
128
+ {prefabs.map((entry) => (
129
+ <button
130
+ key={entry.path}
131
+ type="button"
132
+ data-testid={`ui-prefab-instantiate-${entry.name}`}
133
+ title={`instantiate ${entry.name}`}
134
+ onClick={() => void instantiate(entry)}
135
+ style={{
136
+ background: '#262626',
137
+ color: '#ccc',
138
+ border: '1px solid #3a3a3a',
139
+ borderRadius: 3,
140
+ padding: '3px 7px',
141
+ fontSize: 11,
142
+ cursor: 'pointer',
143
+ fontFamily: 'monospace',
144
+ }}
145
+ >
146
+ {entry.name}
147
+ </button>
148
+ ))}
149
+ </div>
150
+ </div>
151
+ );
152
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * E1.b (spec-27 §7 E1 / §9 "…, timeline, …" MIGRATE row) — the keyframe/dope-
3
+ * sheet editor (`ui-editor/timeline-panel.tsx`'s `UITimeline`, reused
4
+ * UNCHANGED) mounted as a dockable bottom-panel tab in the unified editor
5
+ * (`components/BottomPanel.tsx`), for the currently active scene-UI child.
6
+ *
7
+ * Shares the SAME `UIEditStore` (so keyframe/track/clip edits are undoable +
8
+ * autosaved through the exact path the palette/inspector use) AND the SAME
9
+ * `UIAnimationController` the design-time stage plays back against
10
+ * (`UIAuthoringAdapter.anim` — see that field's doc comment for why the
11
+ * stage and the timeline can't each own their own controller instance:
12
+ * scrubbing here must move the SAME preview the stage renders).
13
+ */
14
+
15
+ import { UITimeline } from '../ui-editor/timeline-panel';
16
+ import { useActiveSceneUIAdapter } from './active-scene-ui';
17
+
18
+ export function SceneUITimelineTab(): React.ReactNode {
19
+ const adapter = useActiveSceneUIAdapter();
20
+ if (!adapter) {
21
+ return (
22
+ <div style={{ padding: 12, color: '#777', fontSize: 12, fontFamily: 'monospace' }}>
23
+ No scene-UI world is open in this project — right-click a scene root in the hierarchy and
24
+ choose "Add UI Canvas" to author one.
25
+ </div>
26
+ );
27
+ }
28
+ return <UITimeline store={adapter.uiStore} anim={adapter.anim} />;
29
+ }
@@ -0,0 +1,74 @@
1
+ /**
2
+ * E1.c (spec-27 §7 E1) — the "Save UI Node as Prefab..." dialog, mounted once
3
+ * (see `components/DefaultEditorLayout.tsx`) alongside the 3D
4
+ * `PrefabSaveDialog` it shares its component with (parameterized — `extension`/
5
+ * `defaultFolder`/`title`, see that file). Listens for
6
+ * `ui-prefab-hierarchy-menu.ts`'s `UI_SAVE_AS_PREFAB_EVENT`, extracts the
7
+ * chosen node from the ACTIVE scene-UI child's live tree at save time (not at
8
+ * event-dispatch time — the tree may have changed since the context-menu
9
+ * click), and writes a `.uiprefab.json` via `ui-prefab-io.ts`.
10
+ */
11
+
12
+ import { useCallback, useEffect, useState } from 'react';
13
+ import { PrefabSaveDialog } from '../components/PrefabSaveDialog';
14
+ import { findNode } from '../ui-editor/node-ops';
15
+ import { resolveActiveSceneUIAdapter } from './active-scene-ui';
16
+ import { UI_SAVE_AS_PREFAB_EVENT, type UISaveAsPrefabDetail } from './ui-prefab-hierarchy-menu';
17
+ import {
18
+ buildUIPrefabFile,
19
+ saveUIPrefabToServer,
20
+ UI_PREFAB_DIR,
21
+ UI_PREFAB_EXTENSION,
22
+ UI_PREFAB_SAVED_EVENT,
23
+ } from './ui-prefab-io';
24
+
25
+ /** `a-z0-9-` filename hint from a node's display name — purely cosmetic
26
+ * (the folder/filename fields stay freely editable), mirrors the 3D dialog's
27
+ * own `entity.name` default-name usage (`DefaultEditorLayout.tsx`). */
28
+ function slugify(name: string): string {
29
+ const slug = name
30
+ .toLowerCase()
31
+ .replace(/[^a-z0-9]+/g, '-')
32
+ .replace(/^-+|-+$/g, '');
33
+ return slug || 'ui-prefab';
34
+ }
35
+
36
+ export function UIPrefabDialogHost(): React.ReactNode {
37
+ const [pending, setPending] = useState<UISaveAsPrefabDetail | null>(null);
38
+
39
+ useEffect(() => {
40
+ const handler = (e: Event) => {
41
+ setPending((e as CustomEvent<UISaveAsPrefabDetail>).detail);
42
+ };
43
+ window.addEventListener(UI_SAVE_AS_PREFAB_EVENT, handler);
44
+ return () => window.removeEventListener(UI_SAVE_AS_PREFAB_EVENT, handler);
45
+ }, []);
46
+
47
+ const handleSave = useCallback(
48
+ (relPath: string) => {
49
+ const detail = pending;
50
+ setPending(null);
51
+ if (!detail) return;
52
+ const adapter = resolveActiveSceneUIAdapter();
53
+ const loc = adapter ? findNode(adapter.uiStore.roots, detail.nodeId) : null;
54
+ if (!loc) return;
55
+ const prefab = buildUIPrefabFile(detail.name, loc.node);
56
+ void saveUIPrefabToServer(prefab, relPath).then((ok) => {
57
+ if (ok) window.dispatchEvent(new CustomEvent(UI_PREFAB_SAVED_EVENT));
58
+ });
59
+ },
60
+ [pending],
61
+ );
62
+
63
+ if (!pending) return null;
64
+ return (
65
+ <PrefabSaveDialog
66
+ defaultName={slugify(pending.name)}
67
+ extension={UI_PREFAB_EXTENSION}
68
+ defaultFolder={UI_PREFAB_DIR}
69
+ title="Save UI Node as Prefab"
70
+ onSave={handleSave}
71
+ onCancel={() => setPending(null)}
72
+ />
73
+ );
74
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * "Save UI Node as Prefab..." hierarchy context-menu item (E1.c, spec-27 §7
3
+ * E1) — the scene-UI counterpart of `authoring/first-party-hierarchy-menu.ts`'s
4
+ * 3D prefab Save-as-Prefab action, registered through the SAME permanent
5
+ * escape hatch (`hierarchy-menu-registry.ts`, C2-blessed): `GameHierarchy`
6
+ * (the contract-only shell) never learns a UI tree exists, it only renders
7
+ * whatever this registration contributes for a matched row.
8
+ *
9
+ * Scoped to a node the ACTIVE scene-UI child's `UIEditStore` actually owns
10
+ * (`resolveActiveSceneUIAdapter` + `findNode`) — never matches a 3D entity or
11
+ * a foreign ingest/boundary node, and never fires when no scene-UI child is
12
+ * installed at all.
13
+ */
14
+
15
+ import { registerHierarchyMenuItems } from '../hierarchy-menu-registry';
16
+ import { findNode } from '../ui-editor/node-ops';
17
+ import { resolveActiveSceneUIAdapter } from './active-scene-ui';
18
+
19
+ /** Dispatched (on `window`) when the user picks "Save UI Node as Prefab..." —
20
+ * `scene-ui-mode/ui-prefab-dialog-host.tsx` is the one listener, mirroring
21
+ * `first-party-hierarchy-menu.ts`'s `editor:save-as-prefab` convention. */
22
+ export const UI_SAVE_AS_PREFAB_EVENT = 'editor:ui-save-as-prefab';
23
+
24
+ export interface UISaveAsPrefabDetail {
25
+ readonly nodeId: string;
26
+ readonly name: string;
27
+ }
28
+
29
+ registerHierarchyMenuItems(
30
+ (ctx) => {
31
+ const adapter = resolveActiveSceneUIAdapter();
32
+ return !!adapter && !!findNode(adapter.uiStore.roots, ctx.nodeId);
33
+ },
34
+ (ctx) => {
35
+ const adapter = resolveActiveSceneUIAdapter();
36
+ const loc = adapter ? findNode(adapter.uiStore.roots, ctx.nodeId) : null;
37
+ if (!loc) return [];
38
+ return [
39
+ {
40
+ label: 'Save UI Node as Prefab...',
41
+ color: '#60a5fa',
42
+ action: () => {
43
+ const detail: UISaveAsPrefabDetail = { nodeId: ctx.nodeId, name: loc.node.name };
44
+ window.dispatchEvent(new CustomEvent(UI_SAVE_AS_PREFAB_EVENT, { detail }));
45
+ },
46
+ },
47
+ ];
48
+ },
49
+ );
@@ -0,0 +1,76 @@
1
+ /**
2
+ * E1.c (spec-27 §7 E1 / §8 "Scene-UI extras: prefabs" / §9 `ui-prefab.ts`
3
+ * MIGRATE row) — the save/list/fetch I/O the pure `ui-editor/ui-prefab.ts`
4
+ * functions (`instantiateUIPrefab`, unit-tested, no UI surface) never got: the
5
+ * harness exposed `instantiateUIPrefab` on `window.__uiRuntime` for e2e/proof
6
+ * scripts only (`ui-editor/harness.tsx`) — no author-facing save/instantiate
7
+ * flow existed anywhere before this unit. Mirrors the 3D prefab I/O
8
+ * (`components/DefaultEditorLayout.tsx`'s `savePrefabToServer`/
9
+ * `fetchPrefabFile`) but for `.uiprefab.json` (`UIPrefabFileSchema`,
10
+ * `packages/engine/src/scene/schema/ui.ts:590`) under a sibling `ui-prefabs/`
11
+ * folder convention (the 3D convention is `prefabs/`, `PrefabSaveDialog.tsx`'s
12
+ * default folder).
13
+ */
14
+
15
+ import type { UINode } from '@engine/scene/schema';
16
+ import { type UIPrefabFile, UIPrefabFileSchema } from '@engine/scene/schema';
17
+ import { listAssets, readProjectTextFile, saveFile } from '../editor-api';
18
+
19
+ /** Default folder a UI prefab saves to / is listed from — sibling of the 3D
20
+ * `prefabs/` convention, never mixed with it (different file schema). */
21
+ export const UI_PREFAB_DIR = 'ui-prefabs';
22
+ export const UI_PREFAB_EXTENSION = 'uiprefab.json';
23
+
24
+ /** Validate a raw parsed JSON object as a `.uiprefab.json` file. Throws a
25
+ * `ZodError` on an invalid shape — callers decide how to surface that. */
26
+ export function parseUIPrefabFile(json: unknown): UIPrefabFile {
27
+ return UIPrefabFileSchema.parse(json);
28
+ }
29
+
30
+ /** Build a fresh (version 1) `UIPrefabFile` around a UI node subtree. The
31
+ * node's OWN ids are kept as authored — `instantiateUIPrefab` (`ui-prefab.ts`)
32
+ * is what regenerates fresh ids, at INSTANTIATE time, not save time. */
33
+ export function buildUIPrefabFile(name: string, root: UINode): UIPrefabFile {
34
+ return { version: 1, name, root };
35
+ }
36
+
37
+ /** Save a `.uiprefab.json` to disk via the editor server API (same
38
+ * storage-backend seam every other project file write uses). */
39
+ export async function saveUIPrefabToServer(prefab: UIPrefabFile, path: string): Promise<boolean> {
40
+ return saveFile(path, JSON.stringify(prefab, null, 2));
41
+ }
42
+
43
+ /** Fetch + validate a `.uiprefab.json` for an instantiate action. `null` on a
44
+ * missing file OR one that fails schema validation (never throws). */
45
+ export async function fetchUIPrefabFile(path: string): Promise<UIPrefabFile | null> {
46
+ const text = await readProjectTextFile(path);
47
+ if (text === null) return null;
48
+ try {
49
+ return parseUIPrefabFile(JSON.parse(text));
50
+ } catch {
51
+ return null;
52
+ }
53
+ }
54
+
55
+ export interface UIPrefabAssetEntry {
56
+ /** Display name (filename minus the `.uiprefab.json` suffix). */
57
+ name: string;
58
+ /** Project-relative path, ready for `fetchUIPrefabFile`. */
59
+ path: string;
60
+ }
61
+
62
+ /** Every `.uiprefab.json` file in {@link UI_PREFAB_DIR} (empty array if the
63
+ * folder doesn't exist yet — `listAssets` already degrades that way). */
64
+ export async function listUIPrefabAssets(): Promise<UIPrefabAssetEntry[]> {
65
+ const entries = await listAssets('', UI_PREFAB_DIR);
66
+ return entries
67
+ .filter((e) => e.type === 'file' && e.name.endsWith(`.${UI_PREFAB_EXTENSION}`))
68
+ .map((e) => ({
69
+ name: e.name.slice(0, -(UI_PREFAB_EXTENSION.length + 1)),
70
+ path: `${UI_PREFAB_DIR}/${e.name}`,
71
+ }));
72
+ }
73
+
74
+ /** Dispatched after a UI prefab is successfully saved, so any open "instantiate"
75
+ * picker (the palette's prefabs section) refreshes its list without a reopen. */
76
+ export const UI_PREFAB_SAVED_EVENT = 'editor:ui-prefab-saved';
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Script loader — dynamically imports project scripts via Vite.
3
+ *
4
+ * When a project is open, Vite serves the project directory via /@fs/ paths
5
+ * (enabled by server.fs.allow in vite.config.ts). This module imports the
6
+ * project's main script entry point and extracts the setup function.
7
+ *
8
+ * W1 — editor parity (docs/R3F-FIRST-PARTY-DESIGN.md §1.A/§2 W1). The
9
+ * standalone path's `defaultThreeKindFactory`
10
+ * (`packages/engine/src/runtime/mount-game.ts:174-186`) already accepts a
11
+ * first-party entry module that exports EITHER a pre-built `adapter`
12
+ * (`GameAdapter`) OR a `setup` function, and only hard-errors when a module
13
+ * exports neither. This module used to lag that contract — it hard-threw on
14
+ * ANY module lacking `setup`, so an `adapter`-export entry that mounted fine
15
+ * standalone failed here. `ProjectScripts` now carries both as optional
16
+ * fields and `loadProjectScripts`/`reloadProjectScripts` error only when
17
+ * NEITHER is present, with a message mirroring
18
+ * `defaultThreeKindFactory`'s wording so the two paths fail identically.
19
+ */
20
+
21
+ import type { GameAdapter } from '@engine/adapter';
22
+ import type { GameComponentClass } from '@engine/ecs/game-component';
23
+ import type { GameSetupFn } from '@engine/runtime/types';
24
+
25
+ export interface ProjectScripts {
26
+ /** Present when the entry module exports a pre-built `GameAdapter` — the
27
+ * same `mod.adapter` shape `defaultThreeKindFactory` prefers
28
+ * (mount-game.ts:176). Optional: a project may instead export `setup`. */
29
+ adapter?: GameAdapter | undefined;
30
+ /** Present when the entry module exports a `setup()` function — the
31
+ * long-standing first-party contract. Optional as of W1: a project may
32
+ * instead export `adapter` (see above). At least one of `adapter`/`setup`
33
+ * is always present on a successfully loaded `ProjectScripts` — callers
34
+ * that only support `setup` (e.g. `resolveDefaultPixiAdapter`, which is
35
+ * OUT of W1's scope — docs/R3F-FIRST-PARTY-DESIGN.md §0.1's kind-parity
36
+ * matrix) must check for `undefined` explicitly rather than assume it. */
37
+ setup?: GameSetupFn | undefined;
38
+ components?: Record<string, GameComponentClass> | undefined;
39
+ }
40
+
41
+ /** Runtime shape a `GameAdapter` export must satisfy before either load
42
+ * function will accept it in place of `setup` — mirrors `guardModuleShape`'s
43
+ * check in `packages/editor/src/adapter-resolver.ts` (`{ module }` adapters):
44
+ * a non-null object with a non-empty string `id` and a function `mount`.
45
+ * Kept local (rather than imported) because `adapter-resolver.ts` already
46
+ * imports FROM this module (`loadProjectScripts`) — importing back would be
47
+ * circular. */
48
+ function isGameAdapterShaped(candidate: unknown): candidate is GameAdapter {
49
+ if (candidate === null || typeof candidate !== 'object') return false;
50
+ const obj = candidate as Record<string, unknown>;
51
+ return (
52
+ typeof obj['id'] === 'string' && obj['id'].length > 0 && typeof obj['mount'] === 'function'
53
+ );
54
+ }
55
+
56
+ /**
57
+ * Extract `{ adapter?, setup?, components? }` from a dynamically imported
58
+ * entry module, throwing the shared "neither export" error (worded to match
59
+ * `defaultThreeKindFactory`'s own error — mount-game.ts:179-183) when the
60
+ * module satisfies neither contract. Shared by `loadProjectScripts` and
61
+ * `reloadProjectScripts` so the two functions can never drift apart on this
62
+ * check.
63
+ */
64
+ function extractProjectScripts(
65
+ // biome-ignore lint/suspicious/noExplicitAny: a dynamically `import()`ed module's shape is unknown until checked
66
+ mod: any,
67
+ scriptEntry: string,
68
+ ): ProjectScripts {
69
+ const adapter = isGameAdapterShaped(mod?.adapter) ? (mod.adapter as GameAdapter) : undefined;
70
+ const setup = typeof mod?.setup === 'function' ? (mod.setup as GameSetupFn) : undefined;
71
+
72
+ if (!adapter && !setup) {
73
+ throw new Error(
74
+ `Project script at ${scriptEntry} exports neither \`adapter\` nor \`setup\` — every ` +
75
+ 'first-party entry module exports at least one (see ' +
76
+ 'packages/editor/template/src/scripts/main.ts).',
77
+ );
78
+ }
79
+
80
+ const components = mod.components as Record<string, GameComponentClass> | undefined;
81
+ loadedComponents = components ?? {};
82
+ return { adapter, setup, components };
83
+ }
84
+
85
+ /**
86
+ * The most recently loaded project component classes, keyed by class name.
87
+ * Populated by `loadProjectScripts`/`reloadProjectScripts` so non-play surfaces
88
+ * (e.g. the schema-driven inspector) can read a component's `static schema`
89
+ * without re-importing project code. Empty until the first load.
90
+ */
91
+ let loadedComponents: Record<string, GameComponentClass> = {};
92
+
93
+ /** Get the component classes from the most recent project script load. */
94
+ export function getLoadedComponents(): Record<string, GameComponentClass> {
95
+ return loadedComponents;
96
+ }
97
+
98
+ /**
99
+ * Set the loaded component classes directly. Used by the browser build's
100
+ * play-mode path (`browser-project-scripts.ts`), which obtains components from
101
+ * the in-browser transpiled bundle rather than a dev-server import, but still
102
+ * wants inspector surfaces to read their `static schema`s via
103
+ * `getLoadedComponents()`.
104
+ */
105
+ export function setLoadedComponents(components: Record<string, GameComponentClass>): void {
106
+ loadedComponents = components;
107
+ }
108
+
109
+ /**
110
+ * Load project scripts from the given project path.
111
+ *
112
+ * The project's entry point is `src/scripts/main.ts`, which must export
113
+ * EITHER (or both — the EDITOR prefers `setup`, which alone threads
114
+ * `editorPreview` + the firstParty HMR brand, see
115
+ * `resolveDefaultThreeAdapter`; standalone prefers `adapter`,
116
+ * mount-game.ts:176):
117
+ * - adapter: GameAdapter — a pre-built adapter (W1, docs/R3F-FIRST-PARTY-
118
+ * DESIGN.md §1.A — the same `mod.adapter` shape `defaultThreeKindFactory`
119
+ * accepts standalone, mount-game.ts:176)
120
+ * - setup(ctx: GameContext): Promise<GameCleanup>
121
+ */
122
+ export async function loadProjectScripts(
123
+ projectPath: string,
124
+ scriptEntry = 'src/scripts/main.ts',
125
+ ): Promise<ProjectScripts> {
126
+ const modulePath = `/@fs/${projectPath}/${scriptEntry}`;
127
+ const mod = await import(/* @vite-ignore */ modulePath);
128
+ return extractProjectScripts(mod, scriptEntry);
129
+ }
130
+
131
+ /**
132
+ * Re-import project scripts with a cache-busting timestamp.
133
+ * Used by the HMR handler to pick up updated code without a full page reload.
134
+ * Same `adapter`-or-`setup` contract as `loadProjectScripts` above.
135
+ */
136
+ export async function reloadProjectScripts(
137
+ projectPath: string,
138
+ scriptEntry = 'src/scripts/main.ts',
139
+ ): Promise<ProjectScripts> {
140
+ const modulePath = `/@fs/${projectPath}/${scriptEntry}?t=${Date.now()}`;
141
+ const mod = await import(/* @vite-ignore */ modulePath);
142
+ return extractProjectScripts(mod, scriptEntry);
143
+ }
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Browser `StorageBackend` over a real on-disk folder, via the File System
3
+ * Access API (`showDirectoryPicker()` → `FileSystemDirectoryHandle`).
4
+ *
5
+ * Unlike OPFS (sandboxed), this reads and writes the actual directory the user
6
+ * picked — so the in-browser editor can edit a project that also lives on disk /
7
+ * in git. Same handle API as OPFS, so all logic is inherited from
8
+ * `HandleStorage`; this subclass adds the read/write permission gate the FSA
9
+ * spec requires before touching a user-granted handle.
10
+ *
11
+ * Chromium-only today (Firefox/Safari lack `showDirectoryPicker`); callers fall
12
+ * back to `OpfsStorage` where it is unavailable.
13
+ */
14
+
15
+ import { HandleStorage } from './handle-storage';
16
+
17
+ /** Subset of the FSA permission API (not in older TS DOM libs). */
18
+ interface PermissionedHandle {
19
+ queryPermission?(d: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;
20
+ requestPermission?(d: { mode: 'read' | 'readwrite' }): Promise<PermissionState>;
21
+ }
22
+
23
+ export class FsaStorage extends HandleStorage {
24
+ readonly id = 'fsa';
25
+
26
+ private permission: Promise<void> | null = null;
27
+
28
+ constructor(private readonly handle: FileSystemDirectoryHandle) {
29
+ super();
30
+ }
31
+
32
+ protected async rootHandle(): Promise<FileSystemDirectoryHandle> {
33
+ await this.ensurePermission();
34
+ return this.handle;
35
+ }
36
+
37
+ /** Ensure read/write permission on the picked handle (prompts at most once). */
38
+ private ensurePermission(): Promise<void> {
39
+ if (!this.permission) {
40
+ this.permission = (async () => {
41
+ const h = this.handle as FileSystemDirectoryHandle & PermissionedHandle;
42
+ // A handle with no permission API is already usable as-is (e.g. OPFS).
43
+ if (!h.queryPermission && !h.requestPermission) return;
44
+ const opts = { mode: 'readwrite' } as const;
45
+ if ((await h.queryPermission?.(opts)) === 'granted') return;
46
+ if ((await h.requestPermission?.(opts)) === 'granted') return;
47
+ throw new Error('FsaStorage: read/write permission to the chosen folder was denied.');
48
+ })().catch((err) => {
49
+ this.permission = null; // allow a retry after a denial
50
+ throw err;
51
+ });
52
+ }
53
+ return this.permission;
54
+ }
55
+ }