@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,203 @@
1
+ /**
2
+ * SourceWriteBackend — the pending-write flush contract for JSX source edits (T3.2
3
+ * slice 3, design `docs/PERSISTENCE-PROVIDER-DESIGN.md` §4).
4
+ *
5
+ * `UIAuthoringAdapter.persistence.save()` is the de-stub target for this seam. What
6
+ * we found investigating the CURRENT write lifecycle (see `source-edit-panel.tsx`):
7
+ * there is NO pending-write queue — every source edit (a style tweak, a struct op)
8
+ * writes to disk IMMEDIATELY via a raw `fetch` call the instant the user commits it
9
+ * (Enter / blur), server-side through the SAME surgical writer used in unit tests
10
+ * (`ui-source/writer.ts`, invoked by `vite-plugin-ui-oid.ts`'s dev-server
11
+ * middleware). So `save()` has nothing to flush; it is an honest no-op REGARDLESS of
12
+ * whether a backend is wired (see `ui-authoring-adapter.ts`). This seam exists so:
13
+ * - the HTTP wire shape is reachable/testable through an injectable interface
14
+ * instead of a bare `fetch` scattered in the adapter, and
15
+ * - a future batching/offline queue (if one is ever built) has somewhere honest
16
+ * to route writes — this file does NOT invent that queue.
17
+ *
18
+ * The browser/hosted tier (no dev server, e.g. a `--mode web` hosted build) has NO
19
+ * implementation here. Backend absence is surfaced through
20
+ * `persistence.destination`, NOT by flipping `capabilities.persist` to false —
21
+ * see `ui-authoring-adapter.ts`'s doc comment for why that flag deliberately
22
+ * stays `true` (it also gates the separate ui-tree→`.vscn` path). We do NOT
23
+ * fake a save.
24
+ *
25
+ * Explicit non-goal (design §6): the vite plugin's raw `node:fs` writes are
26
+ * server-side and stay exactly as they are — this seam is the CLIENT-facing
27
+ * contract in front of them, not a reroute of their internals through
28
+ * `StorageBackend`.
29
+ *
30
+ * `index()` (T6.2 slice 2, `docs/REACT-WORLD-DESIGN.md` §1.D) is the read-side
31
+ * sibling added for `ReactWorldAuthoringAdapter`'s node labeling
32
+ * (`component:tag`): it exposes the SAME `/__ui-source/index` GET endpoint
33
+ * `vite-plugin-ui-oid.ts` already serves (previously only fetched ad hoc by
34
+ * `source-edit-panel.tsx`) through this one backend seam, rather than a second
35
+ * bespoke fetch call.
36
+ */
37
+
38
+ import type { OidEntry } from './oid-transform';
39
+
40
+ /** The source-write operations (+ the OID index read) the dev-server middleware
41
+ * already exposes. */
42
+ export interface SourceWriteBackend {
43
+ /** Surgical inline-style/className edit — the `/__ui-source/write` shape.
44
+ * `appended` (D-A1, wave 13) is true only when the write took the writer's
45
+ * APPEND branch (no prior literal for this prop) — see
46
+ * `ReactWorldAuthoringAdapter.writeStyle`'s append-aware undo (D-A4). */
47
+ writeStyle(
48
+ oid: string,
49
+ prop: string,
50
+ value: string,
51
+ ): Promise<{
52
+ changed: boolean;
53
+ dynamic?: boolean;
54
+ route?: string;
55
+ error?: string;
56
+ appended?: boolean;
57
+ }>;
58
+ /**
59
+ * D-A3 (wave 13): surgically REMOVE an appended inline-style property — the
60
+ * correct undo for a write that took the append branch (`appended: true`).
61
+ * Posts the SAME `/__ui-source/write` endpoint `writeStyle` uses, with the
62
+ * `value: null` removal sentinel the middleware routes to `removeInlineStyle`.
63
+ */
64
+ removeStyle(
65
+ oid: string,
66
+ prop: string,
67
+ ): Promise<{ changed: boolean; route?: string; error?: string }>;
68
+ /** Surgical CSS-FILE edit (Cap 2) — the `/__ui-source/css` shape. The client resolves
69
+ * the target rule (source file + selector) from live matched-rule data and sends it;
70
+ * the server edits that file in place. `generated: true` means the selector was not
71
+ * found in source (generated CSS) — the caller re-routes to class/inline.
72
+ * Optional: only a dev-server-backed implementation serves it. */
73
+ writeCss?(
74
+ file: string,
75
+ selector: string,
76
+ prop: string,
77
+ value: string,
78
+ ): Promise<{ changed: boolean; generated?: boolean; error?: string }>;
79
+ /** Text-content edit (Cap 3) — the `/__ui-source/text` shape. Replaces a leaf element's
80
+ * pure-text body; `dynamic: true` means the body has an expression/children (refused).
81
+ * `prevText` is the editable text before the edit (for the undo inverse). Optional:
82
+ * only a dev-server-backed implementation serves it. */
83
+ writeText?(
84
+ oid: string,
85
+ text: string,
86
+ ): Promise<{ changed: boolean; dynamic?: boolean; prevText?: string | null; error?: string }>;
87
+ /** Component prop / attribute edit (Cap 4) — the `/__ui-source/prop` shape. `oid` is the
88
+ * CALL-SITE oid (the `<Component …>` tag). `dynamic: true` ⇒ the prop is a non-literal
89
+ * expression (refused). Optional: only a dev-server-backed implementation serves it. */
90
+ writeProp?(
91
+ oid: string,
92
+ prop: string,
93
+ value: string,
94
+ ): Promise<{ changed: boolean; dynamic?: boolean; error?: string }>;
95
+ /**
96
+ * Structural source edit — the `/__ui-source/struct` shape. Ops: `delete`, `duplicate`,
97
+ * `wrap`/`unwrap`, `create`, `reorder`, `reparent` (Cap 5). Multi-oid ops pass the
98
+ * companion oid(s) + optional `wrapperTag` via `opts`.
99
+ *
100
+ * D-1 (Phase D, spec27 §2): when `changed`, the server ALSO returns the whole
101
+ * touched file's `prevSource`/`newSource` (before/after this op) plus their
102
+ * `prevSha`/`newSha` (sha256, server-computed) — the checksum-guarded
103
+ * whole-file snapshot inverse `ReactWorldAuthoringAdapter.structOp` needs to
104
+ * push ONE undo/redo entry per structural op (a per-OID inverse is unsound
105
+ * here: OIDs are `file:component:tag:nthOccurrence` content signatures,
106
+ * reorder/delete reassign occurrence indices, and delete has no inverse
107
+ * payload of its own). These fields are absent when `changed` is false, and
108
+ * on an implementation that predates D-1 (a bare `{changed}` mock) — either
109
+ * way the caller degrades to "not undoable" rather than crashing.
110
+ */
111
+ writeStruct(
112
+ oid: string,
113
+ op: string,
114
+ opts?: { targetOid?: string; parentOid?: string; wrapperTag?: string },
115
+ ): Promise<{
116
+ changed: boolean;
117
+ error?: string;
118
+ file?: string;
119
+ prevSource?: string;
120
+ newSource?: string;
121
+ prevSha?: string;
122
+ newSha?: string;
123
+ }>;
124
+ /**
125
+ * Batched structural delete — delete-order-residual fix (bug-panel follow-up to
126
+ * the multi-delete corruption fix, 50f90a6d), the `/__ui-source/struct-many`
127
+ * shape. Delete-only today (`op` must be `'delete'`). Unlike N separate
128
+ * `writeStruct(oid, 'delete')` calls, the server resolves every `oids` entry's
129
+ * offset against ONE shared file snapshot and applies them all in a single write
130
+ * — sound and CALLER-ORDER-INDEPENDENT (see `handleStructMany`'s doc comment,
131
+ * `vite-plugin-ui-oid.ts`): the caller does not need to sort `oids` into any
132
+ * particular order, source or otherwise. Same response shape as `writeStruct`
133
+ * (one `prevSource`/`newSource`/`prevSha`/`newSha` snapshot for the whole batch
134
+ * — one undo entry, not N). Optional: only a dev-server-backed implementation
135
+ * serves it; its absence is how `ReactWorldAuthoringAdapter` honestly degrades
136
+ * `structure.removeMany` to absent (the shell then falls back to
137
+ * `deleteSelection`'s per-id loop, unchanged).
138
+ */
139
+ writeStructMany?(
140
+ oids: readonly string[],
141
+ op: string,
142
+ ): Promise<{
143
+ changed: boolean;
144
+ error?: string;
145
+ file?: string;
146
+ prevSource?: string;
147
+ newSource?: string;
148
+ prevSha?: string;
149
+ newSha?: string;
150
+ }>;
151
+ /**
152
+ * D-1 — the checksum-guarded whole-file restore: the undo/redo inverse for a
153
+ * structural edit. Writes `source` to `file` ONLY if the file's CURRENT
154
+ * content hashes (sha256) to `ifMatchSha`; refuses — `restored: false`,
155
+ * NEVER writes — otherwise (the file changed out from under the timeline
156
+ * since, e.g. a hand-edit, or an out-of-order restore). The caller never
157
+ * computes a hash itself; it only echoes back one `writeStruct` already
158
+ * handed it (`prevSha`/`newSha`) — see that method's doc comment. Optional:
159
+ * only a dev-server-backed implementation can serve it — its absence is how
160
+ * a caller without whole-file-restore support (a legacy mock, a future
161
+ * non-dev-server tier) honestly degrades a structural op to "not undoable"
162
+ * instead of assuming a restore path that isn't there.
163
+ */
164
+ restoreSource?(
165
+ file: string,
166
+ source: string,
167
+ ifMatchSha: string,
168
+ ): Promise<{ restored: boolean; sha?: string; error?: string }>;
169
+ /** The whole OID → source-location index — the `/__ui-source/index` shape.
170
+ * Optional: only a dev-server-backed implementation can serve it. */
171
+ index?(): Promise<Record<string, OidEntry>>;
172
+ }
173
+
174
+ async function postJson<T>(url: string, body: unknown): Promise<T> {
175
+ const res = await fetch(url, {
176
+ method: 'POST',
177
+ headers: { 'Content-Type': 'application/json' },
178
+ body: JSON.stringify(body),
179
+ });
180
+ return (await res.json()) as T;
181
+ }
182
+
183
+ /** Wraps the EXISTING `/__ui-source/write` + `/__ui-source/struct` dev-server
184
+ * endpoints (see `vite-plugin-ui-oid.ts`) — the dev-server-backed tier. */
185
+ export function createHttpSourceWriteBackend(): SourceWriteBackend {
186
+ return {
187
+ writeStyle: (oid, prop, value) => postJson('/__ui-source/write', { oid, prop, value }),
188
+ // D-A3: the removal sentinel — same endpoint, `value: null`.
189
+ removeStyle: (oid, prop) => postJson('/__ui-source/write', { oid, prop, value: null }),
190
+ writeCss: (file, selector, prop, value) =>
191
+ postJson('/__ui-source/css', { file, selector, prop, value }),
192
+ writeText: (oid, text) => postJson('/__ui-source/text', { oid, text }),
193
+ writeProp: (oid, prop, value) => postJson('/__ui-source/prop', { oid, prop, value }),
194
+ writeStruct: (oid, op, opts) => postJson('/__ui-source/struct', { oid, op, ...opts }),
195
+ writeStructMany: (oids, op) => postJson('/__ui-source/struct-many', { oids, op }),
196
+ restoreSource: (file, source, ifMatchSha) =>
197
+ postJson('/__ui-source/restore', { file, source, ifMatchSha }),
198
+ index: async () => {
199
+ const res = await fetch('/__ui-source/index');
200
+ return (await res.json()) as Record<string, OidEntry>;
201
+ },
202
+ };
203
+ }
@@ -0,0 +1,174 @@
1
+ /**
2
+ * CSS property → Tailwind class converter (Cap 6 of the React visual-edit parity port,
3
+ * `docs/REACT-VISUAL-EDIT-PARITY.md`). A faithful port of `../../../visual-edit/tailwind-
4
+ * map.ts`: the ~30 grouped-inspector properties, using NAMED utilities where a keyword maps
5
+ * cleanly (`flex`, `justify-center`, `font-bold`, `rounded-full`, `border-2`) and arbitrary
6
+ * values (`w-[200px]`, `bg-[#059669]`) as the universal lossless fallback.
7
+ *
8
+ * This is the NAMED-utility converter — complementary to `writer.ts`'s `cssToUtilityClass`,
9
+ * which stays on the strictly-lossless arbitrary+type-hint scheme the source-write dedupe
10
+ * (`classGroup`) depends on. Use this when a human-readable named utility is preferred over
11
+ * round-trip-exact arbitrary values.
12
+ */
13
+
14
+ /** Sanitize a CSS value for use inside Tailwind's `[...]` arbitrary brackets (no spaces;
15
+ * collapse `, ` → `,` first, e.g. `rgb(0, 0, 0)` → `rgb(0,_0,_0)`). */
16
+ function sanitize(val: string): string {
17
+ return val.replace(/,\s+/g, ',').replace(/\s+/g, '_');
18
+ }
19
+
20
+ /** Empty / non-meaningful values that shouldn't generate a class. */
21
+ function isEmpty(val: string): boolean {
22
+ return !val || val === 'normal' || val === 'auto' || val === 'none' || val === '';
23
+ }
24
+
25
+ const DISPLAY_MAP: Record<string, string> = {
26
+ block: 'block',
27
+ flex: 'flex',
28
+ grid: 'grid',
29
+ inline: 'inline',
30
+ 'inline-block': 'inline-block',
31
+ 'inline-flex': 'inline-flex',
32
+ 'inline-grid': 'inline-grid',
33
+ none: 'hidden',
34
+ contents: 'contents',
35
+ };
36
+ const FLEX_DIRECTION_MAP: Record<string, string> = {
37
+ row: 'flex-row',
38
+ column: 'flex-col',
39
+ 'row-reverse': 'flex-row-reverse',
40
+ 'column-reverse': 'flex-col-reverse',
41
+ };
42
+ const JUSTIFY_CONTENT_MAP: Record<string, string> = {
43
+ 'flex-start': 'justify-start',
44
+ 'flex-end': 'justify-end',
45
+ center: 'justify-center',
46
+ 'space-between': 'justify-between',
47
+ 'space-around': 'justify-around',
48
+ 'space-evenly': 'justify-evenly',
49
+ start: 'justify-start',
50
+ end: 'justify-end',
51
+ stretch: 'justify-stretch',
52
+ };
53
+ const ALIGN_ITEMS_MAP: Record<string, string> = {
54
+ 'flex-start': 'items-start',
55
+ 'flex-end': 'items-end',
56
+ center: 'items-center',
57
+ baseline: 'items-baseline',
58
+ stretch: 'items-stretch',
59
+ start: 'items-start',
60
+ end: 'items-end',
61
+ };
62
+ const TEXT_ALIGN_MAP: Record<string, string> = {
63
+ left: 'text-left',
64
+ center: 'text-center',
65
+ right: 'text-right',
66
+ justify: 'text-justify',
67
+ start: 'text-start',
68
+ end: 'text-end',
69
+ };
70
+ const OVERFLOW_MAP: Record<string, string> = {
71
+ visible: 'overflow-visible',
72
+ hidden: 'overflow-hidden',
73
+ scroll: 'overflow-scroll',
74
+ auto: 'overflow-auto',
75
+ clip: 'overflow-clip',
76
+ };
77
+ const BORDER_STYLE_MAP: Record<string, string> = {
78
+ none: 'border-none',
79
+ solid: 'border-solid',
80
+ dashed: 'border-dashed',
81
+ dotted: 'border-dotted',
82
+ double: 'border-double',
83
+ hidden: 'border-hidden',
84
+ };
85
+ const FONT_WEIGHT_MAP: Record<string, string> = {
86
+ '100': 'font-thin',
87
+ '200': 'font-extralight',
88
+ '300': 'font-light',
89
+ '400': 'font-normal',
90
+ '500': 'font-medium',
91
+ '600': 'font-semibold',
92
+ '700': 'font-bold',
93
+ '800': 'font-extrabold',
94
+ '900': 'font-black',
95
+ normal: 'font-normal',
96
+ bold: 'font-bold',
97
+ };
98
+
99
+ type Converter = (value: string) => string;
100
+ const arb =
101
+ (prefix: string): Converter =>
102
+ (v) =>
103
+ isEmpty(v) ? '' : `${prefix}-[${sanitize(v)}]`;
104
+
105
+ /** camelCase CSS property → a Tailwind-class converter (empty string ⇒ no class). */
106
+ const converters: Record<string, Converter> = {
107
+ display: (v) => DISPLAY_MAP[v] ?? '',
108
+ flexDirection: (v) => FLEX_DIRECTION_MAP[v] ?? '',
109
+ justifyContent: (v) => JUSTIFY_CONTENT_MAP[v] ?? '',
110
+ alignItems: (v) => ALIGN_ITEMS_MAP[v] ?? '',
111
+ gap: arb('gap'),
112
+ width: arb('w'),
113
+ height: arb('h'),
114
+ minWidth: arb('min-w'),
115
+ minHeight: arb('min-h'),
116
+ maxWidth: (v) => (v === 'none' ? 'max-w-none' : arb('max-w')(v)),
117
+ maxHeight: (v) => (v === 'none' ? 'max-h-none' : arb('max-h')(v)),
118
+ marginTop: (v) => `mt-[${sanitize(v)}]`,
119
+ marginRight: (v) => `mr-[${sanitize(v)}]`,
120
+ marginBottom: (v) => `mb-[${sanitize(v)}]`,
121
+ marginLeft: (v) => `ml-[${sanitize(v)}]`,
122
+ paddingTop: (v) => `pt-[${sanitize(v)}]`,
123
+ paddingRight: (v) => `pr-[${sanitize(v)}]`,
124
+ paddingBottom: (v) => `pb-[${sanitize(v)}]`,
125
+ paddingLeft: (v) => `pl-[${sanitize(v)}]`,
126
+ fontSize: arb('text'),
127
+ fontWeight: (v) => FONT_WEIGHT_MAP[v] ?? (isEmpty(v) ? '' : `font-[${sanitize(v)}]`),
128
+ lineHeight: arb('leading'),
129
+ textAlign: (v) => TEXT_ALIGN_MAP[v] ?? '',
130
+ color: (v) => namedColor('text', v) ?? arb('text')(v),
131
+ backgroundColor: (v) => namedColor('bg', v) ?? arb('bg')(v),
132
+ borderColor: (v) => namedColor('border', v) ?? arb('border')(v),
133
+ borderWidth: (v) => BORDER_WIDTH_MAP[v] ?? (isEmpty(v) ? '' : `border-[${sanitize(v)}]`),
134
+ borderStyle: (v) => BORDER_STYLE_MAP[v] ?? '',
135
+ borderRadius: (v) =>
136
+ v === '0' || v === '0px'
137
+ ? 'rounded-none'
138
+ : v === '9999px' || v === '100%'
139
+ ? 'rounded-full'
140
+ : arb('rounded')(v),
141
+ overflow: (v) => OVERFLOW_MAP[v] ?? '',
142
+ opacity: (v) => (v === '1' ? 'opacity-100' : v === '0' ? 'opacity-0' : arb('opacity')(v)),
143
+ boxShadow: (v) => (v === 'none' ? 'shadow-none' : arb('shadow')(v)),
144
+ };
145
+
146
+ const BORDER_WIDTH_MAP: Record<string, string> = {
147
+ '0': 'border-0',
148
+ '0px': 'border-0',
149
+ '1px': 'border',
150
+ '2px': 'border-2',
151
+ '4px': 'border-4',
152
+ '8px': 'border-8',
153
+ };
154
+
155
+ /** The few keyword colors Tailwind names directly (else the caller falls back to arbitrary). */
156
+ function namedColor(prefix: string, v: string): string | null {
157
+ if (v === 'transparent') return `${prefix}-transparent`;
158
+ if (v === 'inherit') return `${prefix}-inherit`;
159
+ if (v === 'currentColor' || v === 'currentcolor') return `${prefix}-current`;
160
+ return null;
161
+ }
162
+
163
+ /** Convert a single CSS `prop:value` to a Tailwind class (`''` if unmapped/empty). */
164
+ export function cssToTailwindClass(prop: string, value: string): string {
165
+ return converters[prop]?.(value.trim()) ?? '';
166
+ }
167
+
168
+ /** Convert a record of CSS changes to a space-separated Tailwind class string. */
169
+ export function cssChangesToTailwindClasses(changes: Record<string, string>): string {
170
+ return Object.entries(changes)
171
+ .map(([prop, value]) => cssToTailwindClass(prop, value))
172
+ .filter(Boolean)
173
+ .join(' ');
174
+ }