@holoscript/core 8.0.8 → 8.0.10

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 (463) hide show
  1. package/LICENSE +21 -0
  2. package/bin/holoscript.cjs +5 -1
  3. package/dist/GLTFPipeline-L76YTXKA.js +15 -0
  4. package/dist/GLTFPipeline-ZWJN7GI6.cjs +36 -0
  5. package/dist/HoloCompositionParser-K76KTEEE.js +12 -0
  6. package/dist/HoloCompositionParser-UZH6Y4HO.cjs +45 -0
  7. package/dist/HoloScriptCodeParser-2D4ERFUZ.js +6 -0
  8. package/dist/HoloScriptCodeParser-UDRLDA7I.cjs +23 -0
  9. package/dist/HoloScriptPlusParser-FTMCGWXD.js +13 -0
  10. package/dist/HoloScriptPlusParser-LQR5GIKF.cjs +30 -0
  11. package/dist/HoloScriptRuntime-UTRLJI5L.js +12 -0
  12. package/dist/HoloScriptRuntime-YBYXJHJN.cjs +21 -0
  13. package/dist/SceneIRCompiler-63KA3M2F.js +17 -0
  14. package/dist/SceneIRCompiler-KWUFHZ4O.cjs +42 -0
  15. package/dist/{USDZExporter-AUHZODOF.cjs → USDZExporter-ML4UMIGC.cjs} +3 -3
  16. package/dist/{USDZExporter-GXB4AUSG.js → USDZExporter-ZFKLQ76G.js} +3 -3
  17. package/dist/agents/index.cjs +1 -1
  18. package/dist/agents/index.js +1 -1
  19. package/dist/audit/AuditLogger.d.ts +106 -0
  20. package/dist/audit/AuditQueryBuilder.d.ts +79 -0
  21. package/dist/audit/ComplianceReporter.d.ts +90 -0
  22. package/dist/audit/index.d.ts +13 -0
  23. package/dist/{chunk-FGMDMJM7.js → chunk-2BYAUHBO.js} +3 -3
  24. package/dist/{chunk-EDPYCLRC.cjs → chunk-2E6PP5UE.cjs} +83 -59
  25. package/dist/{chunk-GASCWJK5.js → chunk-2EGGAZT3.js} +65 -287
  26. package/dist/{chunk-SIEXCILW.js → chunk-2I3XAGMF.js} +3 -3
  27. package/dist/{chunk-OS533P33.cjs → chunk-2KKOYZYI.cjs} +1074 -1655
  28. package/dist/chunk-2MQKNDNH.js +51 -0
  29. package/dist/{chunk-7DMMMBCY.cjs → chunk-2X3IPCBK.cjs} +10 -10
  30. package/dist/chunk-2ZOTBO25.cjs +202 -0
  31. package/dist/chunk-356N4NPM.cjs +152 -0
  32. package/dist/{chunk-X3GJPRVK.cjs → chunk-36NPLOI4.cjs} +92 -81
  33. package/dist/{chunk-SLU6UPJI.cjs → chunk-37HHY7RH.cjs} +5 -5
  34. package/dist/{chunk-YDPEHCCI.cjs → chunk-3CDDRUER.cjs} +3 -118
  35. package/dist/chunk-3F7ZZWIN.js +1651 -0
  36. package/dist/chunk-3ILD7TKP.cjs +187 -0
  37. package/dist/{chunk-X27QRG4C.cjs → chunk-3IUHTGDD.cjs} +153 -41
  38. package/dist/{chunk-PXNZ6TME.js → chunk-4376VHNJ.js} +60 -36
  39. package/dist/{chunk-YKGPCZEE.cjs → chunk-4FIBOJW6.cjs} +10 -10
  40. package/dist/chunk-4Q7RKQQI.cjs +14195 -0
  41. package/dist/{chunk-YCM6QQRT.cjs → chunk-566ZB4LZ.cjs} +180 -8
  42. package/dist/{chunk-2Y3V22SG.js → chunk-5CURIYGT.js} +3 -3
  43. package/dist/{chunk-QBWNJXNR.js → chunk-5FYMCVBY.js} +5 -5
  44. package/dist/{chunk-ZA34AN3R.js → chunk-5LVFTGKH.js} +841 -12
  45. package/dist/{chunk-Z6PMDS4G.js → chunk-5OUOMBKK.js} +1090 -53
  46. package/dist/chunk-63AUAIL6.js +48 -0
  47. package/dist/chunk-655FATJU.js +110 -0
  48. package/dist/{chunk-TQ377OLB.cjs → chunk-65S62L4D.cjs} +28 -22
  49. package/dist/chunk-6NTAZN5Z.cjs +502 -0
  50. package/dist/chunk-6TRX6AR2.js +9376 -0
  51. package/dist/chunk-6ZCCLKS2.cjs +51 -0
  52. package/dist/{chunk-MDR235RZ.js → chunk-734OB5SK.js} +721 -8
  53. package/dist/{chunk-Q2Q34NF3.js → chunk-77QI552M.js} +6 -6
  54. package/dist/{chunk-WKX5AMOY.js → chunk-7I74JWPW.js} +3 -3
  55. package/dist/{chunk-5FSKKMR7.cjs → chunk-7IDJDMPU.cjs} +232 -4
  56. package/dist/chunk-7VBKRGIZ.cjs +847 -0
  57. package/dist/{chunk-KCPHASCO.js → chunk-AATYYLYK.js} +5 -5
  58. package/dist/{chunk-DTGBOSEM.cjs → chunk-AWXFEAQL.cjs} +12 -12
  59. package/dist/{chunk-THCK6ECW.js → chunk-B3CWTCK2.js} +231 -3
  60. package/dist/chunk-BCP4E7MD.cjs +997 -0
  61. package/dist/chunk-BG7RPYJP.js +91 -0
  62. package/dist/{chunk-VF257YKO.js → chunk-BP4KTMLY.js} +117 -5
  63. package/dist/{chunk-YN6IETRZ.js → chunk-BQJ3YBF3.js} +6424 -987
  64. package/dist/{chunk-7X5PNIQJ.js → chunk-BXSOEQI3.js} +27 -25
  65. package/dist/{chunk-WFKVS2K4.cjs → chunk-C65C5VED.cjs} +1101 -55
  66. package/dist/{chunk-KOUAQL2B.cjs → chunk-CANAL6CT.cjs} +1569 -168
  67. package/dist/{chunk-FDSGHPAK.js → chunk-CBSDJF3B.js} +3 -3
  68. package/dist/{chunk-FEWUHQNM.cjs → chunk-CFVK26P4.cjs} +4 -4
  69. package/dist/{chunk-M7K4LE66.cjs → chunk-CI33NINL.cjs} +4 -4
  70. package/dist/{chunk-2OWDB4FB.js → chunk-CKV5NYAE.js} +6 -6
  71. package/dist/{chunk-LPS5AMQD.cjs → chunk-CM4NHXVO.cjs} +24 -24
  72. package/dist/chunk-D3HSRXYB.js +115 -0
  73. package/dist/{chunk-FNHJRI32.js → chunk-DBH3D2MU.js} +1561 -160
  74. package/dist/{chunk-GW3LKD5C.js → chunk-DJNH4HFW.js} +3 -3
  75. package/dist/{chunk-2GCNOI2U.cjs → chunk-DN6F4DHH.cjs} +9 -3
  76. package/dist/chunk-DQRJ5RJG.js +432 -0
  77. package/dist/{chunk-C7ZBSV2Q.js → chunk-DUG3OQAS.js} +106 -43
  78. package/dist/chunk-ECUYJOJ2.cjs +199 -0
  79. package/dist/chunk-EEMTCAK7.js +182 -0
  80. package/dist/{chunk-3SKLAUDD.cjs → chunk-EI3J6LTW.cjs} +359 -581
  81. package/dist/{chunk-SSSN7VAM.js → chunk-EM2OGPYE.js} +4 -4
  82. package/dist/{chunk-56VOWURB.cjs → chunk-ENZCIQZC.cjs} +522 -453
  83. package/dist/chunk-ESBXUYOC.cjs +836 -0
  84. package/dist/{chunk-L5U4W6JC.js → chunk-ESSEF6U2.js} +4 -4
  85. package/dist/chunk-ET3AN4W3.js +824 -0
  86. package/dist/chunk-EXKSFGSX.cjs +14 -14
  87. package/dist/{chunk-I76GIKN7.cjs → chunk-F3YT6GUF.cjs} +17 -17
  88. package/dist/{chunk-WRWEX5ZF.js → chunk-F75DZBBI.js} +3 -3
  89. package/dist/{chunk-4JGRWHZJ.js → chunk-FG6PYGIZ.js} +12 -6
  90. package/dist/chunk-FGGJQRLZ.js +14 -14
  91. package/dist/{chunk-SXOSDZD6.js → chunk-FWCHPCD4.js} +128 -10
  92. package/dist/{chunk-XW6IXEQH.cjs → chunk-G35CAQ7I.cjs} +5 -5
  93. package/dist/{chunk-7VMGP2FW.js → chunk-G6KACNMO.js} +132 -188
  94. package/dist/{chunk-FJPADH77.cjs → chunk-GAWJJAGT.cjs} +6 -6
  95. package/dist/chunk-GGR7QUKK.cjs +4724 -0
  96. package/dist/chunk-GIBF4BHH.js +14098 -0
  97. package/dist/{chunk-AWB7QYY6.cjs → chunk-GILYQ5VH.cjs} +6 -6
  98. package/dist/{chunk-FWVYAMNW.cjs → chunk-GKFCHGCM.cjs} +4 -4
  99. package/dist/{chunk-Y3ZFVUUU.js → chunk-GLEJ2TLS.js} +3 -3
  100. package/dist/{chunk-3GWWJT7L.js → chunk-GP3OKSKT.js} +13 -13
  101. package/dist/chunk-GTIPOEHN.js +196 -0
  102. package/dist/{chunk-LMJ5EBV7.cjs → chunk-GZPUA2TL.cjs} +16 -15
  103. package/dist/{chunk-IELX2OKI.js → chunk-H4LJ4HEA.js} +3 -115
  104. package/dist/{chunk-ACVWFGBI.cjs → chunk-HCX4SMFK.cjs} +3 -3
  105. package/dist/{chunk-HYNTS5CP.js → chunk-HHRZ6STP.js} +24 -2
  106. package/dist/chunk-HIDEIEFG.js +48 -0
  107. package/dist/{chunk-W6RW64BB.cjs → chunk-HSWBIDZM.cjs} +228 -5
  108. package/dist/{chunk-GSOUNWEL.js → chunk-HVX7QQYK.js} +1035 -1616
  109. package/dist/{chunk-TRCGG42I.cjs → chunk-IFW5IW4L.cjs} +6502 -1019
  110. package/dist/{chunk-F5LKJ6F5.cjs → chunk-IJU6AS67.cjs} +32 -28
  111. package/dist/{chunk-TVANHNJ7.js → chunk-IK6Z2HNC.js} +173 -3
  112. package/dist/{chunk-QGMUTVVH.cjs → chunk-IS7NTKY5.cjs} +20 -20
  113. package/dist/{chunk-YFVQWSNJ.js → chunk-IXWGYWH6.js} +3 -3
  114. package/dist/chunk-J2DAS2PL.js +148 -0
  115. package/dist/chunk-JBQXZIWB.js +148 -0
  116. package/dist/chunk-JBYBMBMW.js +261 -0
  117. package/dist/{chunk-LWUOUCYL.js → chunk-JEJ7TT54.js} +5 -5
  118. package/dist/{chunk-DUCB6LGL.js → chunk-JOTCTHN5.js} +211 -5
  119. package/dist/{chunk-47RUFYKR.js → chunk-K56UVEXV.js} +3 -3
  120. package/dist/{chunk-XSFNHP2E.js → chunk-KB26H447.js} +3 -3
  121. package/dist/{chunk-3NGDZZR4.js → chunk-KEYG7HGY.js} +430 -37
  122. package/dist/{chunk-DY72BMU5.js → chunk-KFVNCW4Q.js} +4 -4
  123. package/dist/chunk-KPGC7SK2.js +192 -0
  124. package/dist/chunk-KROXXBK5.cjs +53 -0
  125. package/dist/{chunk-ZPZAVHXL.cjs → chunk-LB3ZZMKT.cjs} +137 -19
  126. package/dist/chunk-LDYZWNJW.cjs +1654 -0
  127. package/dist/{chunk-UGXHYIWT.cjs → chunk-LHNHO27U.cjs} +28 -28
  128. package/dist/{chunk-ULMSSHCJ.cjs → chunk-LMB555QB.cjs} +405 -38
  129. package/dist/{chunk-CBJY23VL.js → chunk-MD4IVS42.js} +3 -3
  130. package/dist/{chunk-VMLKSV26.js → chunk-MDDWEBCX.js} +226 -3
  131. package/dist/chunk-MUWH4QK5.cjs +94 -0
  132. package/dist/{chunk-T6W3Z3SH.cjs → chunk-N2ILK7BF.cjs} +4 -4
  133. package/dist/{chunk-6EX3I5P4.cjs → chunk-N74QRJGP.cjs} +6 -6
  134. package/dist/chunk-NFFHLX37.cjs +132 -0
  135. package/dist/chunk-NJSYWX7H.js +190 -0
  136. package/dist/{chunk-SH5PPVK7.cjs → chunk-NQN5LFN4.cjs} +165 -221
  137. package/dist/chunk-NTQRFOPP.cjs +56 -0
  138. package/dist/{chunk-YFWRJW4B.js → chunk-NWMJPTVL.js} +51 -55
  139. package/dist/{chunk-63ZGNR2T.cjs → chunk-OC6GV5QH.cjs} +42 -42
  140. package/dist/{chunk-7E7GCQCK.js → chunk-OG3MBHM3.js} +9 -3
  141. package/dist/chunk-OMNHTCS4.cjs +264 -0
  142. package/dist/chunk-OQXB6NNC.cjs +117 -0
  143. package/dist/{chunk-Y57W2CT5.cjs → chunk-OV46CCA2.cjs} +51 -55
  144. package/dist/{chunk-6PRZIHGL.cjs → chunk-OV6HHKLE.cjs} +21 -266
  145. package/dist/{chunk-C37BHB7N.cjs → chunk-P26YYPOO.cjs} +5 -5
  146. package/dist/{chunk-2BZHWUSR.cjs → chunk-P7JEKJTZ.cjs} +16 -16
  147. package/dist/{chunk-NGBXXZLL.cjs → chunk-PDXX5UI4.cjs} +45 -45
  148. package/dist/{chunk-FHQFPYV4.js → chunk-PJFTTSKE.js} +8 -7
  149. package/dist/{chunk-GVFJ6T4S.cjs → chunk-PVODJEJ7.cjs} +721 -8
  150. package/dist/{chunk-ZZ7RO2UQ.js → chunk-Q265VZJ4.js} +3 -3
  151. package/dist/{chunk-E7JP3BS3.cjs → chunk-Q47JEE2C.cjs} +4 -4
  152. package/dist/chunk-QBY7LRN2.js +281 -0
  153. package/dist/{chunk-GH6C2QGW.cjs → chunk-QEDMOGF3.cjs} +857 -16
  154. package/dist/{chunk-5WUNN6ZN.cjs → chunk-QHSAVOOL.cjs} +24 -2
  155. package/dist/chunk-QJNCDVYM.cjs +137 -0
  156. package/dist/chunk-QM7FJB5W.js +4717 -0
  157. package/dist/chunk-QQFB4RT2.cjs +448 -0
  158. package/dist/chunk-QSYBHQAD.cjs +196 -0
  159. package/dist/{chunk-2YSNA2F2.cjs → chunk-QVJMDVYZ.cjs} +5 -5
  160. package/dist/chunk-RAJMAX6D.js +131 -0
  161. package/dist/{chunk-VVKVQZI6.js → chunk-RAN3ECAS.js} +3 -3
  162. package/dist/chunk-RKGUZBOC.js +79 -0
  163. package/dist/{chunk-HLFDZ35S.cjs → chunk-RRQIAJLQ.cjs} +8 -8
  164. package/dist/{chunk-PABONWKI.js → chunk-RXQNRZ5F.js} +3 -3
  165. package/dist/{chunk-7ZHYEITD.cjs → chunk-S4O7CFHQ.cjs} +10 -11
  166. package/dist/chunk-SE7ANBGN.js +128 -0
  167. package/dist/{chunk-X2AVEAZM.cjs → chunk-SJIRCTIS.cjs} +176 -2
  168. package/dist/{chunk-WXQ57LAO.js → chunk-SQXAZHDG.js} +4 -4
  169. package/dist/chunk-SR73L5ZP.cjs +84 -0
  170. package/dist/{chunk-OABEO6HH.js → chunk-SZOLTWWD.js} +2349 -214
  171. package/dist/{chunk-5JEU52DW.js → chunk-TPZ3BN2D.js} +3 -3
  172. package/dist/{chunk-QFU53KRH.js → chunk-TSTFYGGX.js} +4 -5
  173. package/dist/{chunk-EBS32AJI.cjs → chunk-TZCFKZ6Z.cjs} +5 -5
  174. package/dist/chunk-UCVZXQ63.js +724 -0
  175. package/dist/chunk-UGQ3QNV6.js +992 -0
  176. package/dist/chunk-UNMVGZVW.js +142 -0
  177. package/dist/{chunk-TEVX443G.js → chunk-UTUD6GJI.js} +178 -6
  178. package/dist/chunk-UXBYA7DV.cjs +155 -0
  179. package/dist/chunk-V6GXFSK7.cjs +443 -0
  180. package/dist/chunk-VHORVGCG.cjs +761 -0
  181. package/dist/{chunk-B4EKV6HQ.js → chunk-VKCTYLDN.js} +500 -442
  182. package/dist/{chunk-O3MGZDCT.cjs → chunk-VTEXJOSD.cjs} +63 -63
  183. package/dist/chunk-VVAAZITA.cjs +287 -0
  184. package/dist/{chunk-V7AKE5VT.cjs → chunk-W2GA3EQB.cjs} +435 -42
  185. package/dist/chunk-W4GABFCR.js +37 -0
  186. package/dist/chunk-WKEK4NCA.js +490 -0
  187. package/dist/{chunk-5A4TCHOC.cjs → chunk-WNYXT4TE.cjs} +4 -4
  188. package/dist/{chunk-S762UEV5.js → chunk-WTH23ISI.js} +6 -6
  189. package/dist/{chunk-BTKZGHAV.js → chunk-WWNCM473.js} +10 -6
  190. package/dist/{chunk-4YNYPHH3.cjs → chunk-X75H75UY.cjs} +2351 -213
  191. package/dist/{chunk-LYJJI7ZK.cjs → chunk-XCUDDFKC.cjs} +216 -9
  192. package/dist/chunk-YAO4HPJL.cjs +120 -0
  193. package/dist/chunk-YKY2JBER.cjs +9443 -0
  194. package/dist/chunk-YP5CHEYU.cjs +154 -0
  195. package/dist/{chunk-WRNEH5MO.js → chunk-YW3I2FQL.js} +18 -7
  196. package/dist/{chunk-BRNUPDHJ.js → chunk-Z557DVYN.js} +386 -19
  197. package/dist/{chunk-VSACG2AR.js → chunk-Z6LZUYZF.js} +21 -264
  198. package/dist/chunk-ZHPMP447.cjs +43 -0
  199. package/dist/{chunk-LODTHGST.cjs → chunk-ZK7KX5CD.cjs} +133 -70
  200. package/dist/{chunk-CCPSNT6B.cjs → chunk-ZRPALBQB.cjs} +5 -5
  201. package/dist/chunk-ZVFHPMC5.js +824 -0
  202. package/dist/chunk-ZVQMKUGJ.js +443 -0
  203. package/dist/cli/holoscript-runner.cjs +86 -48
  204. package/dist/cli/holoscript-runner.js +54 -16
  205. package/dist/codebase/index.cjs +1 -1
  206. package/dist/codebase/index.js +1 -1
  207. package/dist/compiler/agent-inference.cjs +11 -11
  208. package/dist/compiler/agent-inference.js +9 -6
  209. package/dist/compiler/android-xr.cjs +13 -12
  210. package/dist/compiler/android-xr.js +11 -10
  211. package/dist/compiler/android.cjs +12 -12
  212. package/dist/compiler/android.js +10 -10
  213. package/dist/compiler/business-quest.cjs +10 -10
  214. package/dist/compiler/business-quest.js +2 -2
  215. package/dist/compiler/coco.cjs +1 -1
  216. package/dist/compiler/coco.js +1 -1
  217. package/dist/compiler/context.cjs +9 -9
  218. package/dist/compiler/context.js +6 -6
  219. package/dist/compiler/daimon-seed.cjs +36 -0
  220. package/dist/compiler/daimon-seed.js +12 -0
  221. package/dist/compiler/domain-block-utils.cjs +205 -156
  222. package/dist/compiler/domain-block-utils.js +8 -7
  223. package/dist/compiler/dtdl.cjs +10 -10
  224. package/dist/compiler/dtdl.js +7 -7
  225. package/dist/compiler/gltf-pipeline.cjs +9 -9
  226. package/dist/compiler/gltf-pipeline.js +2 -2
  227. package/dist/compiler/godot.cjs +10 -9
  228. package/dist/compiler/godot.js +9 -8
  229. package/dist/compiler/incremental.cjs +11 -11
  230. package/dist/compiler/incremental.js +6 -6
  231. package/dist/compiler/index.cjs +2585 -3273
  232. package/dist/compiler/index.d.ts +149 -35
  233. package/dist/compiler/index.js +2197 -3102
  234. package/dist/compiler/ios.cjs +11 -11
  235. package/dist/compiler/ios.js +9 -9
  236. package/dist/compiler/llm-provider-capabilities.cjs +9 -9
  237. package/dist/compiler/llm-provider-capabilities.js +6 -6
  238. package/dist/compiler/nodetoy.cjs +1 -1
  239. package/dist/compiler/nodetoy.js +1 -1
  240. package/dist/compiler/openxr.cjs +11 -10
  241. package/dist/compiler/openxr.js +10 -9
  242. package/dist/compiler/remotion.cjs +1 -1
  243. package/dist/compiler/remotion.js +1 -1
  244. package/dist/compiler/reproducibility.cjs +1 -1
  245. package/dist/compiler/reproducibility.js +1 -1
  246. package/dist/compiler/sdf.cjs +11 -10
  247. package/dist/compiler/sdf.js +9 -8
  248. package/dist/compiler/semantic-scene.cjs +7 -7
  249. package/dist/compiler/semantic-scene.js +2 -2
  250. package/dist/compiler/state.cjs +9 -9
  251. package/dist/compiler/state.js +7 -7
  252. package/dist/compiler/trait-composition.cjs +11 -11
  253. package/dist/compiler/trait-composition.js +7 -7
  254. package/dist/compiler/traits/ProvenanceSemiring.d.ts +167 -0
  255. package/dist/compiler/traits/Semiring.d.ts +26 -0
  256. package/dist/compiler/unity.cjs +11 -10
  257. package/dist/compiler/unity.js +10 -9
  258. package/dist/compiler/unreal.cjs +12 -11
  259. package/dist/compiler/unreal.js +10 -9
  260. package/dist/compiler/urdf.cjs +18 -17
  261. package/dist/compiler/urdf.js +9 -8
  262. package/dist/compiler/usd-physics.cjs +11 -10
  263. package/dist/compiler/usd-physics.js +8 -7
  264. package/dist/compiler/visionos.cjs +11 -10
  265. package/dist/compiler/visionos.js +10 -9
  266. package/dist/compiler/vrchat.cjs +11 -10
  267. package/dist/compiler/vrchat.js +9 -8
  268. package/dist/compiler/wasm.cjs +14 -13
  269. package/dist/compiler/wasm.js +9 -8
  270. package/dist/compiler/webgpu.cjs +10 -9
  271. package/dist/compiler/webgpu.js +9 -8
  272. package/dist/constants.cjs +398 -0
  273. package/dist/constants.js +9 -0
  274. package/dist/coordinators/index.cjs +4 -1
  275. package/dist/coordinators/index.js +4 -1
  276. package/dist/debugger.cjs +11 -9
  277. package/dist/debugger.js +9 -7
  278. package/dist/{dist-ACXG6IH5.cjs → dist-GRMOP2UL.cjs} +341 -65
  279. package/dist/{dist-VD4L6V7Q.js → dist-ZBUKU2JG.js} +333 -63
  280. package/dist/entries/interop.cjs +23 -23
  281. package/dist/entries/interop.js +3 -3
  282. package/dist/entries/scripting.cjs +12 -11
  283. package/dist/entries/scripting.js +8 -7
  284. package/dist/evolution/index.cjs +66 -0
  285. package/dist/evolution/index.d.ts +162 -0
  286. package/dist/evolution/index.js +17 -0
  287. package/dist/hololand/index.cjs +41 -37
  288. package/dist/hololand/index.js +3 -3
  289. package/dist/index.cjs +12394 -3224
  290. package/dist/index.d.ts +260 -18
  291. package/dist/index.js +10755 -2182
  292. package/dist/keyword-registry/PluginKeywordRegistry.d.ts +138 -0
  293. package/dist/keyword-registry/index.cjs +29 -0
  294. package/dist/keyword-registry/index.d.ts +1 -0
  295. package/dist/keyword-registry/index.js +4 -0
  296. package/dist/math/tropical-spmv.cjs +11 -11
  297. package/dist/math/tropical-spmv.d.ts +1 -0
  298. package/dist/math/tropical-spmv.js +2 -2
  299. package/dist/math/tropicalSpmv.d.ts +43 -0
  300. package/dist/math/vec3.cjs +1 -1
  301. package/dist/math/vec3.js +1 -1
  302. package/dist/matter/StagedMatter.d.ts +82 -0
  303. package/dist/matter/index.cjs +17 -0
  304. package/dist/matter/index.d.ts +1 -0
  305. package/dist/matter/index.js +4 -0
  306. package/dist/{ml-dsa-Z7JDKW4L.js → ml-dsa-J6VKVT4K.js} +3 -3
  307. package/dist/{ml-dsa-FSKBTE3W.cjs → ml-dsa-MTIK4ZUQ.cjs} +23 -23
  308. package/dist/paper-0c-spike/index.cjs +1 -1
  309. package/dist/paper-0c-spike/index.js +1 -1
  310. package/dist/parameter-envelope/ParameterEnvelope.d.ts +128 -0
  311. package/dist/parameter-envelope/index.cjs +17 -0
  312. package/dist/parameter-envelope/index.d.ts +2 -0
  313. package/dist/parameter-envelope/index.js +4 -0
  314. package/dist/parser/HoloCompositionTypes.d.ts +2013 -0
  315. package/dist/parser/PipelineParser.d.ts +162 -0
  316. package/dist/parser.cjs +36 -20
  317. package/dist/parser.js +13 -9
  318. package/dist/plugin-manifest/ManifestAutoLoader.d.ts +35 -0
  319. package/dist/plugin-manifest/PluginManifest.d.ts +156 -0
  320. package/dist/plugin-manifest/index.cjs +25 -0
  321. package/dist/plugin-manifest/index.d.ts +2 -0
  322. package/dist/plugin-manifest/index.js +4 -0
  323. package/dist/plugin-solver-contract/PluginSolverContract.d.ts +288 -0
  324. package/dist/plugin-solver-contract/index.cjs +41 -0
  325. package/dist/plugin-solver-contract/index.d.ts +1 -0
  326. package/dist/plugin-solver-contract/index.js +4 -0
  327. package/dist/policy/ContentPolicyGate.d.ts +51 -0
  328. package/dist/policy/PolicyPack.d.ts +120 -0
  329. package/dist/policy/blocklist.d.ts +32 -0
  330. package/dist/policy/defaults.d.ts +44 -0
  331. package/dist/policy/index.cjs +106 -0
  332. package/dist/policy/index.d.ts +17 -0
  333. package/dist/policy/index.js +5 -0
  334. package/dist/policy/jsonLogic.d.ts +31 -0
  335. package/dist/policy/types.d.ts +174 -0
  336. package/dist/proof-composition/ProofCompositionLaw.d.ts +93 -0
  337. package/dist/proof-composition/index.cjs +25 -0
  338. package/dist/proof-composition/index.d.ts +1 -0
  339. package/dist/proof-composition/index.js +4 -0
  340. package/dist/receipts/hash-policy.d.ts +85 -0
  341. package/dist/reconstruction/index.cjs +75 -47
  342. package/dist/reconstruction/index.js +5 -5
  343. package/dist/runtime/plugin-trait-registrar.d.ts +36 -0
  344. package/dist/runtime.cjs +81 -79
  345. package/dist/runtime.js +9 -7
  346. package/dist/scene-presets-EDKYZ5GW.js +4 -0
  347. package/dist/scene-presets-NCW2CFRS.cjs +17 -0
  348. package/dist/self-improvement/index.cjs +1 -1
  349. package/dist/self-improvement/index.js +1 -1
  350. package/dist/{src-PT2FERDV.cjs → src-G3D7ZATQ.cjs} +545 -21
  351. package/dist/{src-J6CM4AUR.js → src-UXYA7HM6.js} +539 -16
  352. package/dist/storage/index.cjs +10 -10
  353. package/dist/storage/index.js +3 -3
  354. package/dist/testing.cjs +1 -1
  355. package/dist/testing.js +1 -1
  356. package/dist/tools/index.cjs +3631 -0
  357. package/dist/tools/index.js +3581 -0
  358. package/dist/traitDocs/traitDocs.cjs +9 -9
  359. package/dist/traitDocs/traitDocs.d.ts +1 -0
  360. package/dist/traitDocs/traitDocs.js +3 -3
  361. package/dist/traits/ControlLoopTrait.d.ts +154 -0
  362. package/dist/traits/KinematicChainTrait.d.ts +116 -0
  363. package/dist/traits/SensorSamplingTrait.d.ts +154 -0
  364. package/dist/traits/TraitTypes.d.ts +270 -0
  365. package/dist/traits/TransactionTrait.d.ts +121 -0
  366. package/dist/traits/botanical-lotus.cjs +54 -50
  367. package/dist/traits/botanical-lotus.js +2 -5
  368. package/dist/traits/control-loop.cjs +21 -0
  369. package/dist/traits/control-loop.d.ts +1 -0
  370. package/dist/traits/control-loop.js +4 -0
  371. package/dist/traits/engines/cloth-verlet.d.ts +65 -0
  372. package/dist/traits/engines/index.cjs +19 -19
  373. package/dist/traits/engines/index.d.ts +15 -275
  374. package/dist/traits/engines/index.js +3 -3
  375. package/dist/traits/engines/motion-data-schema.d.ts +119 -0
  376. package/dist/traits/engines/motion-matching.d.ts +102 -0
  377. package/dist/traits/engines/noise.d.ts +25 -0
  378. package/dist/traits/engines/onnx-adapter.d.ts +173 -0
  379. package/dist/traits/engines/onnx-motion-matching.d.ts +163 -0
  380. package/dist/traits/engines/pfnn-network.d.ts +92 -0
  381. package/dist/traits/engines/synthetic-walk-cycle.d.ts +38 -0
  382. package/dist/traits/engines/tensor-ops.d.ts +15 -0
  383. package/dist/traits/index.cjs +2789 -1259
  384. package/dist/traits/index.d.ts +35 -0
  385. package/dist/traits/index.js +1692 -411
  386. package/dist/traits/kinematic-chain.cjs +25 -0
  387. package/dist/traits/kinematic-chain.d.ts +1 -0
  388. package/dist/traits/kinematic-chain.js +4 -0
  389. package/dist/traits/sensor-sampling.cjs +33 -0
  390. package/dist/traits/sensor-sampling.d.ts +1 -0
  391. package/dist/traits/sensor-sampling.js +4 -0
  392. package/dist/traits/simulation-solver-factory.cjs +1 -1
  393. package/dist/traits/simulation-solver-factory.js +1 -1
  394. package/dist/traits/transaction.cjs +29 -0
  395. package/dist/traits/transaction.d.ts +1 -0
  396. package/dist/traits/transaction.js +4 -0
  397. package/dist/traits/webcam-gaze.cjs +1 -1
  398. package/dist/traits/webcam-gaze.js +1 -1
  399. package/dist/type-checker.cjs +7 -6
  400. package/dist/type-checker.js +5 -4
  401. package/dist/types/AdvancedTypeSystem.d.ts +489 -0
  402. package/dist/types/HoloScriptPlus.d.ts +810 -0
  403. package/dist/types/base.d.ts +248 -0
  404. package/dist/types.d.ts +799 -0
  405. package/dist/world/index.cjs +3 -4
  406. package/dist/world/index.js +3 -4
  407. package/dist/world-model/index.cjs +1 -1
  408. package/dist/world-model/index.js +1 -1
  409. package/dist/wot/index.cjs +1 -1
  410. package/dist/wot/index.js +1 -1
  411. package/package.json +135 -69
  412. package/dist/GLTFPipeline-3EJLL3TS.cjs +0 -38
  413. package/dist/GLTFPipeline-3OWJV2VK.js +0 -17
  414. package/dist/HoloScriptPlusParser-ADWRY5OS.cjs +0 -698
  415. package/dist/HoloScriptPlusParser-JLJDXHSC.js +0 -693
  416. package/dist/HoloScriptPlusParser-QCGFPWIL.cjs +0 -27
  417. package/dist/HoloScriptPlusParser-WENAFEFV.js +0 -10
  418. package/dist/HoloScriptRuntime-GGRVEU4T.cjs +0 -19
  419. package/dist/HoloScriptRuntime-GJK6VJPH.js +0 -10
  420. package/dist/chunk-22Q5A6RV.js +0 -979
  421. package/dist/chunk-4C57PR42.js +0 -468
  422. package/dist/chunk-4W5G5QVL.cjs +0 -847
  423. package/dist/chunk-5ET6L4NS.js +0 -487
  424. package/dist/chunk-C4GSLCHE.cjs +0 -7153
  425. package/dist/chunk-F2G6CWFT.js +0 -7089
  426. package/dist/chunk-G6PNEASF.cjs +0 -814
  427. package/dist/chunk-GTA4Q3A3.js +0 -1491
  428. package/dist/chunk-HCCQLAA4.cjs +0 -74
  429. package/dist/chunk-HFSVMKM3.cjs +0 -72
  430. package/dist/chunk-KAIVDEIG.cjs +0 -4539
  431. package/dist/chunk-KQU64CIX.cjs +0 -1508
  432. package/dist/chunk-MSYSQ2AJ.js +0 -4533
  433. package/dist/chunk-NHJLP37F.js +0 -70
  434. package/dist/chunk-NHYRRAGI.cjs +0 -490
  435. package/dist/chunk-OIQYIIO3.cjs +0 -473
  436. package/dist/chunk-OL46QLBJ.js +0 -61
  437. package/dist/chunk-SKZPC5AG.cjs +0 -984
  438. package/dist/chunk-SLHLXUMO.js +0 -445
  439. package/dist/chunk-UOIGFQXC.cjs +0 -9750
  440. package/dist/chunk-UOL6Y5TD.cjs +0 -759
  441. package/dist/chunk-UVFI7XIR.cjs +0 -452
  442. package/dist/chunk-VEPGBETF.js +0 -844
  443. package/dist/chunk-XQ5MWMP2.js +0 -756
  444. package/dist/chunk-XT6JY5W5.js +0 -9684
  445. package/dist/chunk-YRRAUB35.js +0 -811
  446. package/dist/compiler/GasOptimizationAnalyzer.d.ts +0 -73
  447. package/dist/compiler/NFTMarketplaceCompiler.d.ts +0 -23
  448. package/dist/compiler/ar.cjs +0 -23
  449. package/dist/compiler/ar.js +0 -11
  450. package/dist/compiler/babylon.cjs +0 -21
  451. package/dist/compiler/babylon.js +0 -15
  452. package/dist/compiler/multi-layer.cjs +0 -24
  453. package/dist/compiler/multi-layer.js +0 -15
  454. package/dist/compiler/playcanvas.cjs +0 -19
  455. package/dist/compiler/playcanvas.js +0 -13
  456. package/dist/compiler/r3f.cjs +0 -39
  457. package/dist/compiler/r3f.d.ts +0 -22
  458. package/dist/compiler/r3f.js +0 -17
  459. package/dist/compiler/vrr.cjs +0 -24
  460. package/dist/compiler/vrr.js +0 -12
  461. package/dist/parser/NFTMarketplaceTypes.d.ts +0 -191
  462. package/dist/playwright-3AT3AKTV.cjs +0 -98673
  463. package/dist/playwright-VZPDVUEQ.js +0 -98660
@@ -0,0 +1,3631 @@
1
+ 'use strict';
2
+
3
+ var chunkZHPMP447_cjs = require('../chunk-ZHPMP447.cjs');
4
+ var readline = require('readline');
5
+ var MaterialLibrary = require('@holoscript/engine/rendering/MaterialLibrary');
6
+
7
+ function _interopNamespace(e) {
8
+ if (e && e.__esModule) return e;
9
+ var n = Object.create(null);
10
+ if (e) {
11
+ Object.keys(e).forEach(function (k) {
12
+ if (k !== 'default') {
13
+ var d = Object.getOwnPropertyDescriptor(e, k);
14
+ Object.defineProperty(n, k, d.get ? d : {
15
+ enumerable: true,
16
+ get: function () { return e[k]; }
17
+ });
18
+ }
19
+ });
20
+ }
21
+ n.default = e;
22
+ return Object.freeze(n);
23
+ }
24
+
25
+ var readline__namespace = /*#__PURE__*/_interopNamespace(readline);
26
+
27
+ var ErrorFormatter = class {
28
+ /**
29
+ * Format error with source context and helpful visual indicators
30
+ *
31
+ * @param error - The error object containing message, location, and suggestion
32
+ * @param sourceCode - Optional source code to show contextual error location
33
+ * @returns Formatted error string with visual cues and source context
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * const formatted = ErrorFormatter.formatError({
38
+ * message: "Unexpected token",
39
+ * location: { line: 5, column: 12 },
40
+ * suggestion: "Try adding a semicolon"
41
+ * }, sourceCode);
42
+ * ```
43
+ */
44
+ static formatError(error, sourceCode) {
45
+ const { message, location, suggestion, token: _token } = error;
46
+ let formatted = `
47
+ \u274C Error: ${message}
48
+ `;
49
+ if (location) {
50
+ formatted += ` at line ${location.line}, column ${location.column}
51
+ `;
52
+ if (sourceCode) {
53
+ const lines = sourceCode.split("\n");
54
+ const errorLine = lines[location.line - 1];
55
+ if (errorLine) {
56
+ formatted += `
57
+ ${location.line} | ${errorLine}
58
+ `;
59
+ formatted += ` | ${" ".repeat(location.column - 1)}^
60
+ `;
61
+ }
62
+ }
63
+ }
64
+ if (suggestion) {
65
+ formatted += `
66
+ \u{1F4A1} Suggestion: ${suggestion}
67
+ `;
68
+ }
69
+ return formatted;
70
+ }
71
+ /**
72
+ * Format multiple errors with summary and limit display to first 5
73
+ *
74
+ * @param errors - Array of error objects to format
75
+ * @returns Formatted string with error count summary and individual error details
76
+ */
77
+ static formatErrors(errors) {
78
+ if (errors.length === 0) return "";
79
+ let formatted = `
80
+ \u274C Found ${errors.length} error${errors.length !== 1 ? "s" : ""}:
81
+ `;
82
+ for (let i = 0; i < Math.min(errors.length, 5); i++) {
83
+ formatted += this.formatError(errors[i]);
84
+ }
85
+ if (errors.length > 5) {
86
+ formatted += `
87
+ ... and ${errors.length - 5} more error${errors.length - 5 !== 1 ? "s" : ""}
88
+ `;
89
+ }
90
+ return formatted;
91
+ }
92
+ /**
93
+ * Format success message
94
+ */
95
+ static formatSuccess(message, details) {
96
+ let formatted = `
97
+ \u2705 ${message}
98
+ `;
99
+ if (details) {
100
+ if (typeof details === "object") {
101
+ formatted += JSON.stringify(details, null, 2) + "\n";
102
+ } else {
103
+ formatted += `${details}
104
+ `;
105
+ }
106
+ }
107
+ return formatted;
108
+ }
109
+ /**
110
+ * Format help text
111
+ */
112
+ static formatHelp() {
113
+ return `
114
+ HoloScript+ REPL v1.0.0
115
+ \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
116
+
117
+ Commands:
118
+ help - Show this help message
119
+ clear - Clear the screen
120
+ vars - List all variables
121
+ types - List all types
122
+ profile - Show performance profile
123
+ exit - Exit REPL
124
+
125
+ Shortcuts:
126
+ .create orb#name - Create orb
127
+ .position x y z - Set position
128
+ .property key val - Set property
129
+
130
+ Examples:
131
+ > orb#myOrb { position: [0, 0, 0] }
132
+ > myOrb.position = [1, 1, 1]
133
+ > match state { "idle" => { ... } }
134
+
135
+ For more info, visit: https://github.com/brianonbased-dev/holoscript
136
+ `.trim();
137
+ }
138
+ };
139
+ var HoloScriptREPL = class {
140
+ constructor(parser, runtime) {
141
+ this.variables = /* @__PURE__ */ new Map();
142
+ this.types = /* @__PURE__ */ new Map();
143
+ this.history = [];
144
+ this.parser = parser;
145
+ this.runtime = runtime;
146
+ this.rl = readline__namespace.createInterface({
147
+ input: process.stdin,
148
+ output: process.stdout,
149
+ history: this.history,
150
+ historySize: 100
151
+ });
152
+ }
153
+ /**
154
+ * Start REPL
155
+ */
156
+ async start() {
157
+ console.debug("\n\u{1F97D} HoloScript+ REPL v1.0.0");
158
+ console.debug('Type "help" for commands, "exit" to quit\n');
159
+ await this.repl();
160
+ }
161
+ /**
162
+ * Main REPL loop
163
+ */
164
+ async repl() {
165
+ const prompt = () => {
166
+ this.rl.question("> ", async (input) => {
167
+ try {
168
+ const trimmed = input.trim();
169
+ if (!trimmed) {
170
+ prompt();
171
+ return;
172
+ }
173
+ if (trimmed.startsWith(".")) {
174
+ this.handleCommand(trimmed);
175
+ prompt();
176
+ return;
177
+ }
178
+ switch (trimmed.toLowerCase()) {
179
+ case "help":
180
+ console.debug(ErrorFormatter.formatHelp());
181
+ prompt();
182
+ return;
183
+ case "clear":
184
+ console.clear();
185
+ prompt();
186
+ return;
187
+ case "vars":
188
+ this.showVariables();
189
+ prompt();
190
+ return;
191
+ case "types":
192
+ this.showTypes();
193
+ prompt();
194
+ return;
195
+ case "profile":
196
+ this.showProfile();
197
+ prompt();
198
+ return;
199
+ case "exit":
200
+ this.rl.close();
201
+ console.debug("\n\u{1F44B} Goodbye!\n");
202
+ return;
203
+ }
204
+ const result = await this.evaluate(trimmed);
205
+ if (result !== void 0 && result !== null) {
206
+ this.displayResult(result);
207
+ }
208
+ } catch (error) {
209
+ console.error(ErrorFormatter.formatError(error));
210
+ }
211
+ prompt();
212
+ });
213
+ };
214
+ prompt();
215
+ }
216
+ /**
217
+ * Handle dot commands
218
+ */
219
+ handleCommand(command) {
220
+ const parts = command.slice(1).split(" ");
221
+ const cmd = parts[0];
222
+ switch (cmd) {
223
+ case "create":
224
+ console.debug(`Creating: ${parts.slice(1).join(" ")}`);
225
+ break;
226
+ case "position":
227
+ console.debug(`Position: x=${parts[1]}, y=${parts[2]}, z=${parts[3]}`);
228
+ break;
229
+ case "property":
230
+ console.debug(`Property: ${parts[1]} = ${parts[2]}`);
231
+ break;
232
+ default:
233
+ console.debug(`Unknown command: .${cmd}`);
234
+ }
235
+ }
236
+ /**
237
+ * Evaluate HoloScript code
238
+ */
239
+ async evaluate(code) {
240
+ try {
241
+ const parseResult = this.parser.parse(code);
242
+ if (parseResult.errors && parseResult.errors.length > 0) {
243
+ throw {
244
+ message: "Parse error",
245
+ errors: parseResult.errors
246
+ };
247
+ }
248
+ const result = await this.runtime.execute(parseResult.ast);
249
+ return result;
250
+ } catch (error) {
251
+ throw error;
252
+ }
253
+ }
254
+ /**
255
+ * Show variables
256
+ */
257
+ showVariables() {
258
+ if (this.variables.size === 0) {
259
+ console.debug("No variables defined");
260
+ return;
261
+ }
262
+ console.debug("\n\u{1F4E6} Variables:");
263
+ for (const [name, value] of this.variables) {
264
+ console.debug(` ${name}: ${this.formatValue(value)}`);
265
+ }
266
+ console.debug();
267
+ }
268
+ /**
269
+ * Show types
270
+ */
271
+ showTypes() {
272
+ if (this.types.size === 0) {
273
+ console.debug("No custom types defined");
274
+ return;
275
+ }
276
+ console.debug("\n\u{1F3F7}\uFE0F Types:");
277
+ for (const [name, type] of this.types) {
278
+ console.debug(` ${name}: ${JSON.stringify(type)}`);
279
+ }
280
+ console.debug();
281
+ }
282
+ /**
283
+ * Show performance profile
284
+ */
285
+ showProfile() {
286
+ console.debug("\n\u23F1\uFE0F Performance Profile:");
287
+ console.debug(" (Profiling data would be displayed here)");
288
+ console.debug();
289
+ }
290
+ /**
291
+ * Display result
292
+ */
293
+ displayResult(result) {
294
+ const formatted = this.formatValue(result);
295
+ console.debug(`=> ${formatted}`);
296
+ }
297
+ /**
298
+ * Format value for display
299
+ */
300
+ formatValue(value) {
301
+ if (value === null) return "null";
302
+ if (value === void 0) return "undefined";
303
+ if (typeof value === "string") return `"${value}"`;
304
+ if (typeof value === "number") return `${value}`;
305
+ if (typeof value === "boolean") return `${value}`;
306
+ if (Array.isArray(value)) {
307
+ return `[ ${value.map((v) => this.formatValue(v)).join(", ")} ]`;
308
+ }
309
+ if (typeof value === "object" && value !== null) {
310
+ const pairs = Object.entries(value).map(([k, v]) => `${k}: ${this.formatValue(v)}`).join(", ");
311
+ return `{ ${pairs} }`;
312
+ }
313
+ return String(value);
314
+ }
315
+ };
316
+ async function startREPL(parser, runtime) {
317
+ const repl = new HoloScriptREPL(parser, runtime);
318
+ await repl.start();
319
+ }
320
+ var HotReloadWatcher = class {
321
+ constructor() {
322
+ this.watchers = /* @__PURE__ */ new Map();
323
+ }
324
+ /**
325
+ * Watch file for changes
326
+ */
327
+ watch(filePath, callback) {
328
+ const fs = chunkZHPMP447_cjs.__require("fs");
329
+ fs.watchFile(filePath, { interval: 1e3 }, async () => {
330
+ try {
331
+ console.debug(`
332
+ \u{1F504} Reloading: ${filePath}`);
333
+ await callback();
334
+ console.debug("\u2705 Reload complete\n");
335
+ } catch (error) {
336
+ console.error(ErrorFormatter.formatError(error));
337
+ }
338
+ });
339
+ this.watchers.set(filePath, true);
340
+ }
341
+ /**
342
+ * Stop watching
343
+ */
344
+ unwatch(filePath) {
345
+ const fs = chunkZHPMP447_cjs.__require("fs");
346
+ fs.unwatchFile(filePath);
347
+ this.watchers.delete(filePath);
348
+ }
349
+ /**
350
+ * Stop all watchers
351
+ */
352
+ clear() {
353
+ const fs = chunkZHPMP447_cjs.__require("fs");
354
+ for (const filePath of this.watchers.keys()) {
355
+ fs.unwatchFile(filePath);
356
+ }
357
+ this.watchers.clear();
358
+ }
359
+ };
360
+ var SourceMapGenerator = class {
361
+ constructor() {
362
+ this.mappings = [];
363
+ }
364
+ /**
365
+ * Add mapping
366
+ */
367
+ addMapping(generatedLine, generatedColumn, sourceLine, sourceColumn, name) {
368
+ this.mappings.push({
369
+ generatedLine,
370
+ generatedColumn,
371
+ sourceLine,
372
+ sourceColumn,
373
+ name
374
+ });
375
+ }
376
+ /**
377
+ * Generate source map
378
+ */
379
+ generate(sourceFile, generatedFile) {
380
+ return {
381
+ version: 3,
382
+ file: generatedFile,
383
+ sourceRoot: "",
384
+ sources: [sourceFile],
385
+ sourcesContent: [],
386
+ mappings: this.encodeMappings(),
387
+ names: []
388
+ };
389
+ }
390
+ /**
391
+ * Encode mappings in VLQ format
392
+ */
393
+ encodeMappings() {
394
+ return this.mappings.map((m) => `${m.generatedLine}:${m.generatedColumn}->${m.sourceLine}:${m.sourceColumn}`).join(";");
395
+ }
396
+ };
397
+ var _MaterialEditor = class _MaterialEditor {
398
+ constructor(config = {}) {
399
+ this.materials = /* @__PURE__ */ new Map();
400
+ this.currentMaterialId = null;
401
+ this.container = null;
402
+ this.previewCanvas = null;
403
+ this.previewRenderer = null;
404
+ // THREE.WebGLRenderer
405
+ this.previewScene = null;
406
+ // THREE.Scene
407
+ this.previewCamera = null;
408
+ // THREE.Camera
409
+ this.previewSphere = null;
410
+ this.config = {
411
+ container: config.container,
412
+ livePreview: config.livePreview ?? true,
413
+ previewResolution: config.previewResolution ?? 512,
414
+ autoSave: config.autoSave ?? true,
415
+ debug: config.debug ?? false,
416
+ theme: config.theme ?? "dark"
417
+ };
418
+ if (this.config.container) {
419
+ this.initializeUI();
420
+ }
421
+ if (this.config.livePreview) {
422
+ this.initializePreview();
423
+ }
424
+ if (this.config.debug) {
425
+ console.debug("[MaterialEditor] Initialized", this.config);
426
+ }
427
+ }
428
+ /**
429
+ * Initialize editor UI
430
+ */
431
+ initializeUI() {
432
+ if (!this.config.container) return;
433
+ this.container = this.config.container;
434
+ this.container.innerHTML = this.generateEditorHTML();
435
+ this.previewCanvas = this.container.querySelector(
436
+ "#material-preview-canvas"
437
+ );
438
+ this.attachEventListeners();
439
+ this.updatePresetsList();
440
+ if (this.config.debug) {
441
+ console.debug("[MaterialEditor] UI initialized");
442
+ }
443
+ }
444
+ /**
445
+ * Generate editor HTML
446
+ */
447
+ generateEditorHTML() {
448
+ const theme = this.config.theme === "dark" ? "material-editor-dark" : "material-editor-light";
449
+ return `
450
+ <div class="material-editor ${theme}">
451
+ <!-- Preview Panel -->
452
+ <div class="preview-panel">
453
+ <div class="panel-header">
454
+ <h3>Preview</h3>
455
+ <div class="preview-controls">
456
+ <button class="preview-btn" data-action="rotate" title="Rotate">\u{1F504}</button>
457
+ <button class="preview-btn" data-action="reset" title="Reset View">\u21BB</button>
458
+ </div>
459
+ </div>
460
+ <div class="preview-content">
461
+ <canvas id="material-preview-canvas"></canvas>
462
+ </div>
463
+ </div>
464
+
465
+ <!-- Properties Panel -->
466
+ <div class="properties-panel">
467
+ <div class="panel-header">
468
+ <h3>Material Properties</h3>
469
+ </div>
470
+ <div class="properties-content">
471
+ <div class="property-group">
472
+ <label>Material Type</label>
473
+ <select id="material-type" class="property-select">
474
+ <option value="standard">Standard</option>
475
+ <option value="physical">Physical</option>
476
+ <option value="metal">Metal</option>
477
+ <option value="glass">Glass</option>
478
+ <option value="emissive">Emissive</option>
479
+ <option value="toon">Toon</option>
480
+ </select>
481
+ </div>
482
+
483
+ <div class="property-group">
484
+ <label>
485
+ Base Color
486
+ <span class="value-display" id="color-value">#888888</span>
487
+ </label>
488
+ <input type="color" id="base-color" class="color-input" value="#888888">
489
+ </div>
490
+
491
+ <div class="property-group">
492
+ <label>
493
+ Metalness
494
+ <span class="value-display" id="metalness-value">0.5</span>
495
+ </label>
496
+ <input type="range" id="metalness" min="0" max="1" step="0.01" value="0.5" class="property-slider">
497
+ </div>
498
+
499
+ <div class="property-group">
500
+ <label>
501
+ Roughness
502
+ <span class="value-display" id="roughness-value">0.5</span>
503
+ </label>
504
+ <input type="range" id="roughness" min="0" max="1" step="0.01" value="0.5" class="property-slider">
505
+ </div>
506
+
507
+ <div class="property-group">
508
+ <label>
509
+ Opacity
510
+ <span class="value-display" id="opacity-value">1.0</span>
511
+ </label>
512
+ <input type="range" id="opacity" min="0" max="1" step="0.01" value="1.0" class="property-slider">
513
+ </div>
514
+
515
+ <div class="property-group" id="emissive-group" style="display: none;">
516
+ <label>
517
+ Emissive Color
518
+ <span class="value-display" id="emissive-color-value">#00ff00</span>
519
+ </label>
520
+ <input type="color" id="emissive-color" class="color-input" value="#00ff00">
521
+ </div>
522
+
523
+ <div class="property-group" id="emissive-intensity-group" style="display: none;">
524
+ <label>
525
+ Emissive Intensity
526
+ <span class="value-display" id="emissive-intensity-value">1.0</span>
527
+ </label>
528
+ <input type="range" id="emissive-intensity" min="0" max="5" step="0.1" value="1.0" class="property-slider">
529
+ </div>
530
+
531
+ <div class="property-group">
532
+ <button id="save-material-btn" class="action-btn">Save Material</button>
533
+ </div>
534
+ </div>
535
+ </div>
536
+
537
+ <!-- Presets Panel -->
538
+ <div class="presets-panel">
539
+ <div class="panel-header">
540
+ <h3>Material Presets</h3>
541
+ </div>
542
+ <div class="presets-content" id="presets-list">
543
+ <!-- Populated dynamically -->
544
+ </div>
545
+ </div>
546
+ </div>
547
+
548
+ <style>
549
+ .material-editor {
550
+ display: grid;
551
+ grid-template-columns: 1fr 350px;
552
+ grid-template-rows: 1fr 200px;
553
+ gap: 1px;
554
+ width: 100%;
555
+ height: 100%;
556
+ background: rgba(255, 255, 255, 0.05);
557
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
558
+ }
559
+
560
+ .material-editor-dark {
561
+ background: #1a1a1a;
562
+ color: #e0e0e0;
563
+ }
564
+
565
+ .material-editor-light {
566
+ background: #f5f5f5;
567
+ color: #333;
568
+ }
569
+
570
+ .preview-panel {
571
+ grid-column: 1;
572
+ grid-row: 1 / 3;
573
+ display: flex;
574
+ flex-direction: column;
575
+ background: rgba(30, 30, 30, 0.95);
576
+ border: 1px solid rgba(255, 255, 255, 0.1);
577
+ }
578
+
579
+ .properties-panel {
580
+ grid-column: 2;
581
+ grid-row: 1;
582
+ display: flex;
583
+ flex-direction: column;
584
+ background: rgba(30, 30, 30, 0.95);
585
+ border: 1px solid rgba(255, 255, 255, 0.1);
586
+ }
587
+
588
+ .presets-panel {
589
+ grid-column: 2;
590
+ grid-row: 2;
591
+ display: flex;
592
+ flex-direction: column;
593
+ background: rgba(30, 30, 30, 0.95);
594
+ border: 1px solid rgba(255, 255, 255, 0.1);
595
+ }
596
+
597
+ .panel-header {
598
+ display: flex;
599
+ justify-content: space-between;
600
+ align-items: center;
601
+ padding: 10px 15px;
602
+ background: rgba(0, 0, 0, 0.2);
603
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
604
+ }
605
+
606
+ .panel-header h3 {
607
+ margin: 0;
608
+ font-size: 14px;
609
+ font-weight: 600;
610
+ color: #60a5fa;
611
+ }
612
+
613
+ .preview-controls {
614
+ display: flex;
615
+ gap: 5px;
616
+ }
617
+
618
+ .preview-btn {
619
+ padding: 4px 8px;
620
+ background: rgba(96, 165, 250, 0.1);
621
+ border: 1px solid rgba(96, 165, 250, 0.3);
622
+ border-radius: 4px;
623
+ color: #60a5fa;
624
+ cursor: pointer;
625
+ font-size: 14px;
626
+ }
627
+
628
+ .preview-btn:hover {
629
+ background: rgba(96, 165, 250, 0.2);
630
+ }
631
+
632
+ .preview-content {
633
+ flex: 1;
634
+ display: flex;
635
+ align-items: center;
636
+ justify-content: center;
637
+ background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
638
+ padding: 20px;
639
+ }
640
+
641
+ #material-preview-canvas {
642
+ max-width: 100%;
643
+ max-height: 100%;
644
+ border-radius: 8px;
645
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
646
+ }
647
+
648
+ .properties-content {
649
+ flex: 1;
650
+ overflow-y: auto;
651
+ padding: 15px;
652
+ }
653
+
654
+ .property-group {
655
+ margin-bottom: 20px;
656
+ }
657
+
658
+ .property-group label {
659
+ display: block;
660
+ font-size: 13px;
661
+ margin-bottom: 8px;
662
+ color: #cbd5e0;
663
+ font-weight: 500;
664
+ }
665
+
666
+ .value-display {
667
+ float: right;
668
+ color: #60a5fa;
669
+ font-size: 12px;
670
+ font-weight: 600;
671
+ }
672
+
673
+ .property-slider {
674
+ width: 100%;
675
+ height: 6px;
676
+ border-radius: 3px;
677
+ background: rgba(96, 165, 250, 0.2);
678
+ outline: none;
679
+ -webkit-appearance: none;
680
+ }
681
+
682
+ .property-slider::-webkit-slider-thumb {
683
+ -webkit-appearance: none;
684
+ width: 16px;
685
+ height: 16px;
686
+ border-radius: 50%;
687
+ background: #60a5fa;
688
+ cursor: pointer;
689
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
690
+ }
691
+
692
+ .property-select {
693
+ width: 100%;
694
+ padding: 8px;
695
+ background: rgba(0, 0, 0, 0.3);
696
+ border: 1px solid rgba(255, 255, 255, 0.2);
697
+ border-radius: 4px;
698
+ color: #fff;
699
+ font-size: 13px;
700
+ }
701
+
702
+ .color-input {
703
+ width: 100%;
704
+ height: 40px;
705
+ padding: 0;
706
+ border: 1px solid rgba(255, 255, 255, 0.2);
707
+ border-radius: 4px;
708
+ background: transparent;
709
+ cursor: pointer;
710
+ }
711
+
712
+ .action-btn {
713
+ width: 100%;
714
+ padding: 10px;
715
+ background: rgba(34, 197, 94, 0.2);
716
+ border: 1px solid rgba(34, 197, 94, 0.4);
717
+ border-radius: 6px;
718
+ color: #4ade80;
719
+ cursor: pointer;
720
+ font-size: 13px;
721
+ font-weight: 500;
722
+ transition: all 0.2s;
723
+ }
724
+
725
+ .action-btn:hover {
726
+ background: rgba(34, 197, 94, 0.3);
727
+ border-color: rgba(34, 197, 94, 0.6);
728
+ }
729
+
730
+ .presets-content {
731
+ padding: 10px;
732
+ overflow-y: auto;
733
+ }
734
+
735
+ .presets-grid {
736
+ display: grid;
737
+ grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
738
+ gap: 10px;
739
+ }
740
+
741
+ .preset-card {
742
+ padding: 10px;
743
+ background: rgba(0, 0, 0, 0.3);
744
+ border: 1px solid rgba(255, 255, 255, 0.1);
745
+ border-radius: 6px;
746
+ cursor: pointer;
747
+ transition: all 0.2s;
748
+ text-align: center;
749
+ }
750
+
751
+ .preset-card:hover {
752
+ background: rgba(96, 165, 250, 0.1);
753
+ border-color: rgba(96, 165, 250, 0.3);
754
+ }
755
+
756
+ .preset-color {
757
+ width: 100%;
758
+ height: 50px;
759
+ border-radius: 4px;
760
+ margin-bottom: 8px;
761
+ border: 1px solid rgba(255, 255, 255, 0.2);
762
+ }
763
+
764
+ .preset-name {
765
+ font-size: 11px;
766
+ color: #94a3b8;
767
+ }
768
+ </style>
769
+ `;
770
+ }
771
+ /**
772
+ * Initialize 3D preview
773
+ */
774
+ initializePreview() {
775
+ const THREE = globalThis.THREE;
776
+ if (!this.previewCanvas || !THREE) return;
777
+ this.previewScene = new THREE.Scene();
778
+ this.previewScene.background = null;
779
+ this.previewCamera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
780
+ this.previewCamera.position.set(0, 0, 5);
781
+ this.previewRenderer = new THREE.WebGLRenderer({
782
+ canvas: this.previewCanvas,
783
+ alpha: true,
784
+ antialias: true
785
+ });
786
+ this.previewRenderer.setSize(this.config.previewResolution, this.config.previewResolution);
787
+ this.previewRenderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
788
+ const geometry = new THREE.SphereGeometry(1.5, 64, 64);
789
+ const material = new THREE.MeshStandardMaterial({
790
+ color: 8947848,
791
+ metalness: 0.5,
792
+ roughness: 0.5
793
+ });
794
+ this.previewSphere = new THREE.Mesh(geometry, material);
795
+ this.previewScene.add(this.previewSphere);
796
+ const ambientLight = new THREE.AmbientLight(16777215, 0.4);
797
+ this.previewScene.add(ambientLight);
798
+ const directionalLight = new THREE.DirectionalLight(16777215, 1);
799
+ directionalLight.position.set(5, 5, 5);
800
+ this.previewScene.add(directionalLight);
801
+ const rimLight = new THREE.DirectionalLight(6333946, 0.5);
802
+ rimLight.position.set(-5, 0, -5);
803
+ this.previewScene.add(rimLight);
804
+ this.animatePreview();
805
+ if (this.config.debug) {
806
+ console.debug("[MaterialEditor] Preview initialized");
807
+ }
808
+ }
809
+ /**
810
+ * Animate preview sphere
811
+ */
812
+ animatePreview() {
813
+ if (!this.previewRenderer || !this.previewScene || !this.previewCamera) return;
814
+ const animate = () => {
815
+ requestAnimationFrame(animate);
816
+ if (this.previewSphere) {
817
+ this.previewSphere.rotation[1] += 5e-3;
818
+ }
819
+ this.previewRenderer.render(this.previewScene, this.previewCamera);
820
+ };
821
+ animate();
822
+ }
823
+ /**
824
+ * Attach event listeners
825
+ */
826
+ attachEventListeners() {
827
+ if (!this.container) return;
828
+ const typeSelect = this.container.querySelector("#material-type");
829
+ typeSelect?.addEventListener("change", (e) => {
830
+ const type = e.target.value;
831
+ this.updateMaterialType(type);
832
+ });
833
+ const colorInput = this.container.querySelector("#base-color");
834
+ colorInput?.addEventListener("input", (e) => {
835
+ const color = e.target.value;
836
+ document.getElementById("color-value").textContent = color;
837
+ this.updatePreviewMaterial({ color });
838
+ });
839
+ const metalnessInput = this.container.querySelector("#metalness");
840
+ metalnessInput?.addEventListener("input", (e) => {
841
+ const value = parseFloat(e.target.value);
842
+ document.getElementById("metalness-value").textContent = value.toFixed(2);
843
+ this.updatePreviewMaterial({ metalness: value });
844
+ });
845
+ const roughnessInput = this.container.querySelector("#roughness");
846
+ roughnessInput?.addEventListener("input", (e) => {
847
+ const value = parseFloat(e.target.value);
848
+ document.getElementById("roughness-value").textContent = value.toFixed(2);
849
+ this.updatePreviewMaterial({ roughness: value });
850
+ });
851
+ const opacityInput = this.container.querySelector("#opacity");
852
+ opacityInput?.addEventListener("input", (e) => {
853
+ const value = parseFloat(e.target.value);
854
+ document.getElementById("opacity-value").textContent = value.toFixed(2);
855
+ this.updatePreviewMaterial({ opacity: value, transparent: value < 1 });
856
+ });
857
+ const emissiveColorInput = this.container.querySelector("#emissive-color");
858
+ emissiveColorInput?.addEventListener("input", (e) => {
859
+ const color = e.target.value;
860
+ document.getElementById("emissive-color-value").textContent = color;
861
+ this.updatePreviewMaterial({ emissive: color });
862
+ });
863
+ const emissiveIntensityInput = this.container.querySelector(
864
+ "#emissive-intensity"
865
+ );
866
+ emissiveIntensityInput?.addEventListener("input", (e) => {
867
+ const value = parseFloat(e.target.value);
868
+ document.getElementById("emissive-intensity-value").textContent = value.toFixed(1);
869
+ this.updatePreviewMaterial({ emissiveIntensity: value });
870
+ });
871
+ const saveBtn = this.container.querySelector("#save-material-btn");
872
+ saveBtn?.addEventListener("click", () => {
873
+ this.saveMaterial();
874
+ });
875
+ }
876
+ /**
877
+ * Update material type
878
+ */
879
+ updateMaterialType(type) {
880
+ const emissiveGroup = document.getElementById("emissive-group");
881
+ const emissiveIntensityGroup = document.getElementById("emissive-intensity-group");
882
+ if (type === "emissive") {
883
+ if (emissiveGroup) emissiveGroup.style.display = "block";
884
+ if (emissiveIntensityGroup) emissiveIntensityGroup.style.display = "block";
885
+ } else {
886
+ if (emissiveGroup) emissiveGroup.style.display = "none";
887
+ if (emissiveIntensityGroup) emissiveIntensityGroup.style.display = "none";
888
+ }
889
+ if (this.config.debug) {
890
+ console.debug("[MaterialEditor] Material type changed:", type);
891
+ }
892
+ }
893
+ /**
894
+ * Update preview material (THREE.js sphere).
895
+ * Accepts hex color strings for THREE.js Color.set() compatibility.
896
+ */
897
+ updatePreviewMaterial(params) {
898
+ if (!this.previewSphere) return;
899
+ const material = this.previewSphere.material;
900
+ if (params.color !== void 0) {
901
+ material.color.set(params.color);
902
+ }
903
+ if (params.metalness !== void 0) {
904
+ material.metalness = params.metalness;
905
+ }
906
+ if (params.roughness !== void 0) {
907
+ material.roughness = params.roughness;
908
+ }
909
+ if (params.opacity !== void 0) {
910
+ material.opacity = params.opacity;
911
+ }
912
+ if (params.transparent !== void 0) {
913
+ material.transparent = params.transparent;
914
+ }
915
+ if (params.emissive !== void 0) {
916
+ material.emissive.set(params.emissive);
917
+ }
918
+ if (params.emissiveIntensity !== void 0) {
919
+ material.emissiveIntensity = params.emissiveIntensity;
920
+ }
921
+ material.needsUpdate = true;
922
+ }
923
+ /**
924
+ * Save current material.
925
+ * Returns a fully-formed MaterialDef (the unified PBR type).
926
+ */
927
+ saveMaterial() {
928
+ const id = `material_${Date.now()}`;
929
+ const material = this.getCurrentMaterial();
930
+ material.id = id;
931
+ this.materials.set(id, material);
932
+ if (this.config.debug) {
933
+ console.debug("[MaterialEditor] Material saved:", material);
934
+ }
935
+ return material;
936
+ }
937
+ /**
938
+ * Get current material configuration from the editor UI.
939
+ * Reads hex color values from DOM inputs and converts to RGBA objects
940
+ * for the unified MaterialDef type.
941
+ */
942
+ getCurrentMaterial() {
943
+ const typeSelect = document.getElementById("material-type");
944
+ const colorInput = document.getElementById("base-color");
945
+ const metalnessInput = document.getElementById("metalness");
946
+ const roughnessInput = document.getElementById("roughness");
947
+ const opacityInput = document.getElementById("opacity");
948
+ const emissiveColorInput = document.getElementById("emissive-color");
949
+ const emissiveIntensityInput = document.getElementById(
950
+ "emissive-intensity"
951
+ );
952
+ const materialType = typeSelect?.value || "standard";
953
+ const colorHex = colorInput?.value || "#888888";
954
+ const albedo = MaterialLibrary.hexToRGBA(colorHex);
955
+ const opacity = parseFloat(opacityInput?.value || "1.0");
956
+ albedo.a = opacity;
957
+ const material = MaterialLibrary.createDefaultMaterialDef("", "Custom Material", materialType);
958
+ material.albedo = albedo;
959
+ material.metallic = parseFloat(metalnessInput?.value || "0.5");
960
+ material.roughness = parseFloat(roughnessInput?.value || "0.5");
961
+ if (opacity < 1) {
962
+ material.blendMode = "transparent";
963
+ }
964
+ if (materialType === "emissive") {
965
+ const emissiveHex = emissiveColorInput?.value || "#00ff00";
966
+ const emissiveRgba = MaterialLibrary.hexToRGBA(emissiveHex);
967
+ material.emission = { r: emissiveRgba.r, g: emissiveRgba.g, b: emissiveRgba.b };
968
+ material.emissionStrength = parseFloat(emissiveIntensityInput?.value || "1.0");
969
+ }
970
+ return material;
971
+ }
972
+ /**
973
+ * Load a MaterialDef into the editor UI.
974
+ * Converts RGBA objects back to hex strings for DOM color inputs.
975
+ */
976
+ loadMaterial(material) {
977
+ this.currentMaterialId = material.id;
978
+ const colorHex = MaterialLibrary.rgbaToHex(material.albedo);
979
+ const emissiveHex = MaterialLibrary.rgbaToHex(material.emission);
980
+ const typeSelect = document.getElementById("material-type");
981
+ const colorInput = document.getElementById("base-color");
982
+ const metalnessInput = document.getElementById("metalness");
983
+ const roughnessInput = document.getElementById("roughness");
984
+ const opacityInput = document.getElementById("opacity");
985
+ const emissiveColorInput = document.getElementById("emissive-color");
986
+ const emissiveIntensityInput = document.getElementById(
987
+ "emissive-intensity"
988
+ );
989
+ if (typeSelect) typeSelect.value = material.materialType || "standard";
990
+ if (colorInput) colorInput.value = colorHex;
991
+ if (metalnessInput) metalnessInput.value = material.metallic.toString();
992
+ if (roughnessInput) roughnessInput.value = material.roughness.toString();
993
+ if (opacityInput) opacityInput.value = (material.albedo.a ?? 1).toString();
994
+ if (emissiveColorInput) {
995
+ emissiveColorInput.value = emissiveHex;
996
+ }
997
+ if (emissiveIntensityInput) {
998
+ emissiveIntensityInput.value = material.emissionStrength.toString();
999
+ }
1000
+ document.getElementById("color-value").textContent = colorHex;
1001
+ document.getElementById("metalness-value").textContent = material.metallic.toFixed(2);
1002
+ document.getElementById("roughness-value").textContent = material.roughness.toFixed(2);
1003
+ document.getElementById("opacity-value").textContent = (material.albedo.a ?? 1).toFixed(2);
1004
+ if (material.emissionStrength > 0) {
1005
+ document.getElementById("emissive-color-value").textContent = emissiveHex;
1006
+ document.getElementById("emissive-intensity-value").textContent = material.emissionStrength.toFixed(1);
1007
+ }
1008
+ this.updateMaterialType(material.materialType || "standard");
1009
+ this.updatePreviewMaterial({
1010
+ color: colorHex,
1011
+ metalness: material.metallic,
1012
+ roughness: material.roughness,
1013
+ opacity: material.albedo.a ?? 1,
1014
+ transparent: material.blendMode === "transparent",
1015
+ emissive: emissiveHex,
1016
+ emissiveIntensity: material.emissionStrength
1017
+ });
1018
+ if (this.config.debug) {
1019
+ console.debug("[MaterialEditor] Material loaded:", material.name);
1020
+ }
1021
+ }
1022
+ /**
1023
+ * Load a preset by name.
1024
+ * Merges preset overrides with default MaterialDef values.
1025
+ */
1026
+ loadPreset(presetName) {
1027
+ const preset = _MaterialEditor.PRESETS.find((p) => p.name === presetName);
1028
+ if (!preset) return;
1029
+ const material = {
1030
+ ...MaterialLibrary.createDefaultMaterialDef(
1031
+ `preset_${presetName.toLowerCase()}`,
1032
+ presetName,
1033
+ preset.material.materialType
1034
+ ),
1035
+ ...preset.material,
1036
+ id: `preset_${presetName.toLowerCase()}`,
1037
+ name: presetName
1038
+ };
1039
+ this.loadMaterial(material);
1040
+ if (this.config.debug) {
1041
+ console.debug("[MaterialEditor] Preset loaded:", presetName);
1042
+ }
1043
+ }
1044
+ /**
1045
+ * Update presets list
1046
+ */
1047
+ updatePresetsList() {
1048
+ const presetsList = document.getElementById("presets-list");
1049
+ if (!presetsList) return;
1050
+ let html = '<div class="presets-grid">';
1051
+ for (const preset of _MaterialEditor.PRESETS) {
1052
+ html += `
1053
+ <div class="preset-card" data-preset="${preset.name}">
1054
+ <div class="preset-color" style="background: ${preset.previewColor};"></div>
1055
+ <div class="preset-name">${preset.name}</div>
1056
+ </div>
1057
+ `;
1058
+ }
1059
+ html += "</div>";
1060
+ presetsList.innerHTML = html;
1061
+ presetsList.querySelectorAll(".preset-card").forEach((card) => {
1062
+ card.addEventListener("click", (e) => {
1063
+ const presetName = e.currentTarget.getAttribute("data-preset");
1064
+ if (presetName) {
1065
+ this.loadPreset(presetName);
1066
+ }
1067
+ });
1068
+ });
1069
+ }
1070
+ /**
1071
+ * Get all materials (returns unified MaterialDef objects)
1072
+ */
1073
+ getMaterials() {
1074
+ return Array.from(this.materials.values());
1075
+ }
1076
+ /**
1077
+ * Get presets
1078
+ */
1079
+ static getPresets() {
1080
+ return _MaterialEditor.PRESETS;
1081
+ }
1082
+ /**
1083
+ * Dispose editor
1084
+ */
1085
+ dispose() {
1086
+ if (this.previewRenderer) {
1087
+ this.previewRenderer.dispose();
1088
+ this.previewRenderer = null;
1089
+ }
1090
+ this.materials.clear();
1091
+ if (this.config.debug) {
1092
+ console.debug("[MaterialEditor] Disposed");
1093
+ }
1094
+ }
1095
+ };
1096
+ // THREE.Mesh
1097
+ // Material presets (using unified MaterialDef schema)
1098
+ _MaterialEditor.PRESETS = [
1099
+ {
1100
+ name: "Concrete",
1101
+ category: "Construction",
1102
+ material: {
1103
+ materialType: "standard",
1104
+ albedo: { r: 0.502, g: 0.502, b: 0.502, a: 1 },
1105
+ metallic: 0,
1106
+ roughness: 0.9
1107
+ },
1108
+ previewColor: "#808080"
1109
+ },
1110
+ {
1111
+ name: "Metal",
1112
+ category: "Metal",
1113
+ material: {
1114
+ materialType: "metal",
1115
+ albedo: { r: 0.533, g: 0.533, b: 0.533, a: 1 },
1116
+ metallic: 1,
1117
+ roughness: 0.2
1118
+ },
1119
+ previewColor: "#888888"
1120
+ },
1121
+ {
1122
+ name: "Gold",
1123
+ category: "Metal",
1124
+ material: {
1125
+ materialType: "metal",
1126
+ albedo: { r: 1, g: 0.843, b: 0, a: 1 },
1127
+ metallic: 1,
1128
+ roughness: 0.15
1129
+ },
1130
+ previewColor: "#ffd700"
1131
+ },
1132
+ {
1133
+ name: "Copper",
1134
+ category: "Metal",
1135
+ material: {
1136
+ materialType: "metal",
1137
+ albedo: { r: 0.722, g: 0.451, b: 0.2, a: 1 },
1138
+ metallic: 1,
1139
+ roughness: 0.25
1140
+ },
1141
+ previewColor: "#b87333"
1142
+ },
1143
+ {
1144
+ name: "Chrome",
1145
+ category: "Metal",
1146
+ material: {
1147
+ materialType: "metal",
1148
+ albedo: { r: 0.8, g: 0.8, b: 0.8, a: 1 },
1149
+ metallic: 1,
1150
+ roughness: 0.05
1151
+ },
1152
+ previewColor: "#cccccc"
1153
+ },
1154
+ {
1155
+ name: "Wood",
1156
+ category: "Natural",
1157
+ material: {
1158
+ materialType: "standard",
1159
+ albedo: { r: 0.545, g: 0.435, b: 0.278, a: 1 },
1160
+ metallic: 0,
1161
+ roughness: 0.7
1162
+ },
1163
+ previewColor: "#8b6f47"
1164
+ },
1165
+ {
1166
+ name: "Plastic",
1167
+ category: "Synthetic",
1168
+ material: {
1169
+ materialType: "physical",
1170
+ albedo: { r: 1, g: 0.42, b: 0.42, a: 1 },
1171
+ metallic: 0,
1172
+ roughness: 0.4
1173
+ },
1174
+ previewColor: "#ff6b6b"
1175
+ },
1176
+ {
1177
+ name: "Glass",
1178
+ category: "Transparent",
1179
+ material: {
1180
+ materialType: "glass",
1181
+ albedo: { r: 1, g: 1, b: 1, a: 0.3 },
1182
+ metallic: 0,
1183
+ roughness: 0,
1184
+ blendMode: "transparent",
1185
+ doubleSided: true
1186
+ },
1187
+ previewColor: "rgba(255, 255, 255, 0.3)"
1188
+ },
1189
+ {
1190
+ name: "Rubber",
1191
+ category: "Synthetic",
1192
+ material: {
1193
+ materialType: "standard",
1194
+ albedo: { r: 0.2, g: 0.2, b: 0.2, a: 1 },
1195
+ metallic: 0,
1196
+ roughness: 0.8
1197
+ },
1198
+ previewColor: "#333333"
1199
+ },
1200
+ {
1201
+ name: "Ceramic",
1202
+ category: "Natural",
1203
+ material: {
1204
+ materialType: "physical",
1205
+ albedo: { r: 0.941, g: 0.941, b: 0.941, a: 1 },
1206
+ metallic: 0,
1207
+ roughness: 0.3
1208
+ },
1209
+ previewColor: "#f0f0f0"
1210
+ },
1211
+ {
1212
+ name: "Neon",
1213
+ category: "Emissive",
1214
+ material: {
1215
+ materialType: "emissive",
1216
+ albedo: { r: 0, g: 1, b: 0, a: 1 },
1217
+ emission: { r: 0, g: 1, b: 0 },
1218
+ emissionStrength: 2
1219
+ },
1220
+ previewColor: "#00ff00"
1221
+ },
1222
+ {
1223
+ name: "Glow",
1224
+ category: "Emissive",
1225
+ material: {
1226
+ materialType: "emissive",
1227
+ albedo: { r: 0.376, g: 0.647, b: 0.98, a: 1 },
1228
+ emission: { r: 0.376, g: 0.647, b: 0.98 },
1229
+ emissionStrength: 1.5
1230
+ },
1231
+ previewColor: "#60a5fa"
1232
+ }
1233
+ ];
1234
+ var MaterialEditor = _MaterialEditor;
1235
+
1236
+ // src/tools/materialPresetQuickPick.ts
1237
+ function getMaterialEditorBuiltinPresets() {
1238
+ return MaterialEditor.getPresets();
1239
+ }
1240
+ function listMaterialEditorQuickPickPresetNames() {
1241
+ return MaterialEditor.getPresets().map((p) => p.name);
1242
+ }
1243
+ function listMaterialEditorQuickPickPresetsByCategory() {
1244
+ const m = /* @__PURE__ */ new Map();
1245
+ for (const p of MaterialEditor.getPresets()) {
1246
+ const list = m.get(p.category) ?? [];
1247
+ list.push(p.name);
1248
+ m.set(p.category, list);
1249
+ }
1250
+ return m;
1251
+ }
1252
+
1253
+ // src/tools/SceneInspector.ts
1254
+ var SceneInspector = class {
1255
+ constructor(config = {}) {
1256
+ this.composition = null;
1257
+ this.renderer = null;
1258
+ this.entities = /* @__PURE__ */ new Map();
1259
+ this.selectedEntityId = null;
1260
+ this.performanceHistory = [];
1261
+ this.currentFrame = 0;
1262
+ this.lastUpdateTime = 0;
1263
+ this.physicsVisualization = {
1264
+ showBounds: false,
1265
+ showVelocity: false,
1266
+ showForces: false,
1267
+ showContacts: false,
1268
+ showCenterOfMass: false
1269
+ };
1270
+ this.config = {
1271
+ enabled: config.enabled ?? true,
1272
+ showPerformance: config.showPerformance ?? true,
1273
+ showHierarchy: config.showHierarchy ?? true,
1274
+ showProperties: config.showProperties ?? true,
1275
+ showStatistics: config.showStatistics ?? true,
1276
+ performanceHistorySize: config.performanceHistorySize ?? 300,
1277
+ updateFrequency: config.updateFrequency ?? 10
1278
+ // 10 Hz
1279
+ };
1280
+ }
1281
+ /**
1282
+ * Initialize inspector with composition
1283
+ */
1284
+ initialize(composition, renderer) {
1285
+ this.composition = composition;
1286
+ this.renderer = renderer || null;
1287
+ this.buildEntityHierarchy();
1288
+ console.debug("[SceneInspector] Initialized with", this.entities.size, "entities");
1289
+ }
1290
+ /**
1291
+ * Build entity hierarchy from composition
1292
+ */
1293
+ buildEntityHierarchy() {
1294
+ if (!this.composition) return;
1295
+ this.entities.clear();
1296
+ for (const obj of this.composition.objects || []) {
1297
+ const getProp = (key) => obj.properties?.find((p) => p.key === key)?.value;
1298
+ const pos = getProp("position");
1299
+ const rot = getProp("rotation");
1300
+ const scl = getProp("scale");
1301
+ const inspectorEntity = {
1302
+ id: obj.name,
1303
+ name: obj.name,
1304
+ type: obj.type || "entity",
1305
+ parentId: null,
1306
+ // HoloScript doesn't have hierarchy yet, all root-level
1307
+ childrenIds: [],
1308
+ transform: {
1309
+ // @ts-expect-error
1310
+ position: pos ? [pos[0], pos[1], pos[2]] : [0, 0, 0],
1311
+ // @ts-expect-error
1312
+ rotation: rot ? [rot[0], rot[1], rot[2]] : [0, 0, 0],
1313
+ // @ts-expect-error
1314
+ scale: scl ? [scl[0], scl[1], scl[2]] : [1, 1, 1]
1315
+ },
1316
+ properties: this.extractProperties(obj),
1317
+ // @ts-expect-error
1318
+ traits: obj.traits?.map((t) => t.name) || [],
1319
+ visible: true,
1320
+ active: true
1321
+ };
1322
+ this.entities.set(inspectorEntity.id, inspectorEntity);
1323
+ }
1324
+ }
1325
+ /**
1326
+ * Extract properties from entity
1327
+ */
1328
+ extractProperties(entity) {
1329
+ const props = {};
1330
+ for (const prop of entity.properties || []) {
1331
+ if (["geometry", "material"].includes(prop.key)) {
1332
+ props[prop.key] = prop.value;
1333
+ }
1334
+ }
1335
+ for (const trait of entity.traits || []) {
1336
+ if (trait.config) {
1337
+ props[trait.name] = trait.config;
1338
+ }
1339
+ }
1340
+ return props;
1341
+ }
1342
+ /**
1343
+ * Update inspector (call per frame)
1344
+ */
1345
+ update(deltaTime) {
1346
+ if (!this.config.enabled) return;
1347
+ this.currentFrame++;
1348
+ const now = performance.now();
1349
+ if (now - this.lastUpdateTime >= 1e3 / this.config.updateFrequency) {
1350
+ this.recordPerformanceFrame(deltaTime);
1351
+ this.lastUpdateTime = now;
1352
+ }
1353
+ }
1354
+ /**
1355
+ * Record performance frame
1356
+ */
1357
+ recordPerformanceFrame(deltaTime) {
1358
+ const stats = this.renderer?.getStatistics();
1359
+ const frame = {
1360
+ frameNumber: this.currentFrame,
1361
+ frameTime: deltaTime * 1e3,
1362
+ // Convert to ms
1363
+ fps: stats?.fps || 0,
1364
+ physicsTime: 0,
1365
+ // Would come from physics engine
1366
+ renderTime: deltaTime * 1e3 * 0.6,
1367
+ // Estimate
1368
+ syncTime: deltaTime * 1e3 * 0.1,
1369
+ // Estimate
1370
+ timestamp: performance.now()
1371
+ };
1372
+ this.performanceHistory.push(frame);
1373
+ if (this.performanceHistory.length > this.config.performanceHistorySize) {
1374
+ this.performanceHistory.shift();
1375
+ }
1376
+ }
1377
+ /**
1378
+ * Get entity hierarchy (for tree view)
1379
+ */
1380
+ getEntityHierarchy() {
1381
+ return Array.from(this.entities.values());
1382
+ }
1383
+ /**
1384
+ * Get entity by ID
1385
+ */
1386
+ getEntity(id) {
1387
+ return this.entities.get(id);
1388
+ }
1389
+ /**
1390
+ * Select entity
1391
+ */
1392
+ selectEntity(id) {
1393
+ if (this.entities.has(id)) {
1394
+ this.selectedEntityId = id;
1395
+ console.debug("[SceneInspector] Selected entity:", id);
1396
+ }
1397
+ }
1398
+ /**
1399
+ * Get selected entity
1400
+ */
1401
+ getSelectedEntity() {
1402
+ return this.selectedEntityId ? this.entities.get(this.selectedEntityId) || null : null;
1403
+ }
1404
+ /**
1405
+ * Update entity property (live editing)
1406
+ */
1407
+ updateEntityProperty(id, path, value) {
1408
+ const entity = this.entities.get(id);
1409
+ if (!entity) return false;
1410
+ const parts = path.split(".");
1411
+ let target = entity;
1412
+ for (let i = 0; i < parts.length - 1; i++) {
1413
+ target = target[parts[i]];
1414
+ if (!target) return false;
1415
+ }
1416
+ const lastKey = parts[parts.length - 1];
1417
+ target[lastKey] = value;
1418
+ console.debug("[SceneInspector] Updated", id, path, "=", value);
1419
+ return true;
1420
+ }
1421
+ /**
1422
+ * Get scene statistics
1423
+ */
1424
+ getStatistics() {
1425
+ const rendererStats = this.renderer?.getStatistics();
1426
+ const recentFrames = this.performanceHistory.slice(-60);
1427
+ const fpsList = recentFrames.map((f) => f.fps).filter((fps) => fps > 0);
1428
+ const averageFPS = fpsList.length > 0 ? fpsList.reduce((a, b) => a + b, 0) / fpsList.length : 0;
1429
+ const minFPS = fpsList.length > 0 ? Math.min(...fpsList) : 0;
1430
+ const maxFPS = fpsList.length > 0 ? Math.max(...fpsList) : 0;
1431
+ return {
1432
+ totalEntities: this.entities.size,
1433
+ activeEntities: Array.from(this.entities.values()).filter((e) => e.active).length,
1434
+ visibleEntities: Array.from(this.entities.values()).filter((e) => e.visible).length,
1435
+ totalVertices: 0,
1436
+ // Would come from renderer
1437
+ totalTriangles: rendererStats?.triangles || 0,
1438
+ drawCalls: rendererStats?.drawCalls || 0,
1439
+ textureMemory: (rendererStats?.memoryUsage?.textures || 0) / (1024 * 1024),
1440
+ geometryMemory: (rendererStats?.memoryUsage?.geometries || 0) / (1024 * 1024),
1441
+ currentFPS: rendererStats?.fps || 0,
1442
+ averageFPS: Math.round(averageFPS),
1443
+ minFPS: Math.round(minFPS),
1444
+ maxFPS: Math.round(maxFPS)
1445
+ };
1446
+ }
1447
+ /**
1448
+ * Get performance history
1449
+ */
1450
+ getPerformanceHistory() {
1451
+ return this.performanceHistory;
1452
+ }
1453
+ /**
1454
+ * Toggle physics visualization
1455
+ */
1456
+ togglePhysicsVisualization(type) {
1457
+ this.physicsVisualization[type] = !this.physicsVisualization[type];
1458
+ console.debug(
1459
+ "[SceneInspector] Physics visualization:",
1460
+ type,
1461
+ "=",
1462
+ this.physicsVisualization[type]
1463
+ );
1464
+ }
1465
+ /**
1466
+ * Get physics visualization settings
1467
+ */
1468
+ getPhysicsVisualization() {
1469
+ return { ...this.physicsVisualization };
1470
+ }
1471
+ /**
1472
+ * Generate HTML for inspector UI
1473
+ */
1474
+ generateInspectorHTML() {
1475
+ const stats = this.getStatistics();
1476
+ const selectedEntity = this.getSelectedEntity();
1477
+ return `
1478
+ <div class="scene-inspector" style="position: absolute; top: 0; right: 0; width: 400px; height: 100vh; background: rgba(0, 0, 0, 0.9); color: #fff; font-family: monospace; font-size: 12px; overflow-y: auto; z-index: 1000;">
1479
+
1480
+ <!-- Header -->
1481
+ <div style="padding: 15px; border-bottom: 2px solid #4ecdc4; background: #1a1a2e;">
1482
+ <h2 style="margin: 0; color: #4ecdc4;">\u{1F50D} Scene Inspector</h2>
1483
+ <div style="color: #aaa; margin-top: 5px;">Frame: ${this.currentFrame}</div>
1484
+ </div>
1485
+
1486
+ <!-- Statistics Panel -->
1487
+ ${this.config.showStatistics ? `
1488
+ <div style="padding: 15px; border-bottom: 1px solid #333;">
1489
+ <h3 style="margin: 0 0 10px 0; color: #ffd93d;">\u{1F4CA} Statistics</h3>
1490
+ <div style="line-height: 1.6;">
1491
+ <div>Entities: ${stats.totalEntities} (${stats.activeEntities} active)</div>
1492
+ <div>FPS: ${stats.currentFPS} (avg: ${stats.averageFPS}, min: ${stats.minFPS})</div>
1493
+ <div>Draw Calls: ${stats.drawCalls}</div>
1494
+ <div>Triangles: ${stats.totalTriangles.toLocaleString()}</div>
1495
+ <div>Memory: ${(stats.textureMemory + stats.geometryMemory).toFixed(1)} MB</div>
1496
+ </div>
1497
+ </div>
1498
+ ` : ""}
1499
+
1500
+ <!-- Hierarchy Panel -->
1501
+ ${this.config.showHierarchy ? `
1502
+ <div style="padding: 15px; border-bottom: 1px solid #333;">
1503
+ <h3 style="margin: 0 0 10px 0; color: #95e1d3;">\u{1F333} Hierarchy</h3>
1504
+ <div style="max-height: 300px; overflow-y: auto;">
1505
+ ${this.getEntityHierarchy().map(
1506
+ (entity) => `
1507
+ <div
1508
+ data-entity-id="${entity.id}"
1509
+ style="padding: 5px; margin: 2px 0; background: ${this.selectedEntityId === entity.id ? "#4ecdc4" : "#2a2a3e"}; cursor: pointer; border-radius: 4px;"
1510
+ onclick="window.inspectorSelectEntity('${entity.id}')"
1511
+ >
1512
+ <div style="font-weight: bold;">${entity.name}</div>
1513
+ <div style="color: #aaa; font-size: 10px;">${entity.type} \xB7 ${entity.traits.join(", ")}</div>
1514
+ </div>
1515
+ `
1516
+ ).join("")}
1517
+ </div>
1518
+ </div>
1519
+ ` : ""}
1520
+
1521
+ <!-- Properties Panel -->
1522
+ ${this.config.showProperties && selectedEntity ? `
1523
+ <div style="padding: 15px; border-bottom: 1px solid #333;">
1524
+ <h3 style="margin: 0 0 10px 0; color: #ff6b6b;">\u2699\uFE0F Properties</h3>
1525
+ <div style="line-height: 1.8;">
1526
+ <div style="color: #4ecdc4; margin-bottom: 10px; font-size: 14px; font-weight: bold;">${selectedEntity.name}</div>
1527
+
1528
+ <div style="margin: 10px 0;">
1529
+ <div style="color: #ffd93d;">Transform:</div>
1530
+ <div style="margin-left: 10px;">
1531
+ <div>Position: ${selectedEntity.transform.position.map((v) => v.toFixed(2)).join(", ")}</div>
1532
+ <div>Rotation: ${selectedEntity.transform.rotation.map((v) => v.toFixed(2)).join(", ")}</div>
1533
+ <div>Scale: ${selectedEntity.transform.scale.map((v) => v.toFixed(2)).join(", ")}</div>
1534
+ </div>
1535
+ </div>
1536
+
1537
+ <div style="margin: 10px 0;">
1538
+ <div style="color: #ffd93d;">Traits:</div>
1539
+ <div style="margin-left: 10px;">${selectedEntity.traits.join(", ") || "None"}</div>
1540
+ </div>
1541
+
1542
+ <div style="margin: 10px 0;">
1543
+ <div style="color: #ffd93d;">Properties:</div>
1544
+ <div style="margin-left: 10px;">
1545
+ ${Object.entries(selectedEntity.properties).map(
1546
+ ([key, value]) => `
1547
+ <div>${key}: ${typeof value === "object" ? JSON.stringify(value) : value}</div>
1548
+ `
1549
+ ).join("")}
1550
+ </div>
1551
+ </div>
1552
+ </div>
1553
+ </div>
1554
+ ` : ""}
1555
+
1556
+ <!-- Performance Graph -->
1557
+ ${this.config.showPerformance ? `
1558
+ <div style="padding: 15px;">
1559
+ <h3 style="margin: 0 0 10px 0; color: #95e1d3;">\u26A1 Performance</h3>
1560
+ <canvas id="performance-graph" width="370" height="100" style="background: #1a1a2e; border-radius: 4px;"></canvas>
1561
+ </div>
1562
+ ` : ""}
1563
+
1564
+ </div>
1565
+
1566
+ <script>
1567
+ // Entity selection handler
1568
+ window.inspectorSelectEntity = function(entityId) {
1569
+ console.debug('Selected entity:', entityId);
1570
+ // Would call inspector.selectEntity(entityId) and refresh UI
1571
+ };
1572
+
1573
+ // Render performance graph
1574
+ ${this.config.showPerformance ? `
1575
+ (function() {
1576
+ const canvas = document.getElementById('performance-graph');
1577
+ if (!canvas) return;
1578
+
1579
+ const ctx = canvas.getContext('2d');
1580
+ const history = ${JSON.stringify(this.performanceHistory.slice(-60))};
1581
+
1582
+ // Clear canvas
1583
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
1584
+
1585
+ // Draw graph
1586
+ const maxFPS = 60;
1587
+ const width = canvas.width;
1588
+ const height = canvas.height;
1589
+
1590
+ ctx.strokeStyle = '#4ecdc4';
1591
+ ctx.lineWidth = 2;
1592
+ ctx.beginPath();
1593
+
1594
+ history.forEach((frame, i) => {
1595
+ const x = (i / history.length) * width;
1596
+ const y = height - (frame.fps / maxFPS) * height;
1597
+
1598
+ if (i === 0) ctx.moveTo(x, y);
1599
+ else ctx.lineTo(x, y);
1600
+ });
1601
+
1602
+ ctx.stroke();
1603
+
1604
+ // Draw 60 FPS line
1605
+ ctx.strokeStyle = '#ffd93d';
1606
+ ctx.lineWidth = 1;
1607
+ ctx.setLineDash([5, 5]);
1608
+ ctx.beginPath();
1609
+ ctx.moveTo(0, 0);
1610
+ ctx.lineTo(width, 0);
1611
+ ctx.stroke();
1612
+ ctx.setLineDash([]);
1613
+
1614
+ // Draw labels
1615
+ ctx.fillStyle = '#fff';
1616
+ ctx.font = '10px monospace';
1617
+ ctx.fillText('60 FPS', 5, 10);
1618
+ ctx.fillText('0 FPS', 5, height - 5);
1619
+ })();
1620
+ ` : ""}
1621
+ </script>
1622
+ `;
1623
+ }
1624
+ /**
1625
+ * Enable/disable inspector
1626
+ */
1627
+ setEnabled(enabled) {
1628
+ this.config.enabled = enabled;
1629
+ }
1630
+ /**
1631
+ * Get configuration
1632
+ */
1633
+ getConfig() {
1634
+ return { ...this.config };
1635
+ }
1636
+ };
1637
+
1638
+ // src/tools/VisualEditor.ts
1639
+ var VisualEditor = class {
1640
+ constructor(config = {}) {
1641
+ this.entities = /* @__PURE__ */ new Map();
1642
+ this.selectedEntityIds = /* @__PURE__ */ new Set();
1643
+ this.composition = null;
1644
+ this.history = [];
1645
+ this.historyIndex = -1;
1646
+ this.maxHistorySize = 100;
1647
+ this.container = null;
1648
+ this.hierarchyPanel = null;
1649
+ this.propertiesPanel = null;
1650
+ this.viewportPanel = null;
1651
+ this.toolbarPanel = null;
1652
+ this.config = {
1653
+ container: config.container ?? null,
1654
+ autoSave: config.autoSave ?? true,
1655
+ autoSaveInterval: config.autoSaveInterval ?? 3e4,
1656
+ // 30 seconds
1657
+ debug: config.debug ?? false,
1658
+ gridSnap: config.gridSnap ?? true,
1659
+ gridSize: config.gridSize ?? 1,
1660
+ enable3DPreview: config.enable3DPreview ?? true,
1661
+ theme: config.theme ?? "dark"
1662
+ };
1663
+ if (this.config.container) {
1664
+ this.initializeUI();
1665
+ }
1666
+ if (this.config.autoSave) {
1667
+ this.startAutoSave();
1668
+ }
1669
+ if (this.config.debug) {
1670
+ console.debug("[VisualEditor] Initialized", this.config);
1671
+ }
1672
+ }
1673
+ /**
1674
+ * Initialize editor UI
1675
+ */
1676
+ initializeUI() {
1677
+ if (!this.config.container) return;
1678
+ this.container = this.config.container;
1679
+ this.container.innerHTML = this.generateEditorHTML();
1680
+ this.hierarchyPanel = this.container.querySelector(".editor-hierarchy");
1681
+ this.propertiesPanel = this.container.querySelector(".editor-properties");
1682
+ this.viewportPanel = this.container.querySelector(".editor-viewport");
1683
+ this.toolbarPanel = this.container.querySelector(".editor-toolbar");
1684
+ this.attachEventListeners();
1685
+ if (this.config.debug) {
1686
+ console.debug("[VisualEditor] UI initialized");
1687
+ }
1688
+ }
1689
+ /**
1690
+ * Generate editor HTML
1691
+ */
1692
+ generateEditorHTML() {
1693
+ const theme = this.config.theme === "dark" ? "editor-dark" : "editor-light";
1694
+ return `
1695
+ <div class="holostudio-editor ${theme}">
1696
+ <!-- Toolbar -->
1697
+ <div class="editor-toolbar">
1698
+ <div class="toolbar-section">
1699
+ <button class="toolbar-btn" data-action="new" title="New Composition">
1700
+ <span class="icon">\u{1F4C4}</span> New
1701
+ </button>
1702
+ <button class="toolbar-btn" data-action="open" title="Open Composition">
1703
+ <span class="icon">\u{1F4C1}</span> Open
1704
+ </button>
1705
+ <button class="toolbar-btn" data-action="save" title="Save Composition">
1706
+ <span class="icon">\u{1F4BE}</span> Save
1707
+ </button>
1708
+ </div>
1709
+ <div class="toolbar-section">
1710
+ <button class="toolbar-btn" data-action="undo" title="Undo">
1711
+ <span class="icon">\u21B6</span>
1712
+ </button>
1713
+ <button class="toolbar-btn" data-action="redo" title="Redo">
1714
+ <span class="icon">\u21B7</span>
1715
+ </button>
1716
+ </div>
1717
+ <div class="toolbar-section">
1718
+ <button class="toolbar-btn" data-action="play" title="Preview">
1719
+ <span class="icon">\u25B6</span> Preview
1720
+ </button>
1721
+ </div>
1722
+ </div>
1723
+
1724
+ <!-- Main Content -->
1725
+ <div class="editor-content">
1726
+ <!-- Left Sidebar: Hierarchy -->
1727
+ <div class="editor-panel editor-hierarchy">
1728
+ <div class="panel-header">
1729
+ <h3>Hierarchy</h3>
1730
+ <button class="panel-btn" data-action="add-entity" title="Add Entity">+</button>
1731
+ </div>
1732
+ <div class="panel-content" id="hierarchy-content">
1733
+ <!-- Populated dynamically -->
1734
+ </div>
1735
+ </div>
1736
+
1737
+ <!-- Center: Viewport -->
1738
+ <div class="editor-panel editor-viewport">
1739
+ <div class="panel-header">
1740
+ <h3>Viewport</h3>
1741
+ <div class="viewport-controls">
1742
+ <button class="panel-btn" data-action="grid-toggle" title="Toggle Grid">
1743
+ <span class="icon">\u229E</span>
1744
+ </button>
1745
+ <button class="panel-btn" data-action="gizmo-toggle" title="Toggle Gizmo">
1746
+ <span class="icon">\u22B9</span>
1747
+ </button>
1748
+ </div>
1749
+ </div>
1750
+ <div class="panel-content viewport-content" id="viewport-content">
1751
+ <canvas id="editor-canvas"></canvas>
1752
+ <div class="viewport-overlay">
1753
+ <div class="viewport-stats">
1754
+ <div>Entities: <span id="entity-count">0</span></div>
1755
+ <div>Selected: <span id="selected-count">0</span></div>
1756
+ </div>
1757
+ </div>
1758
+ </div>
1759
+ </div>
1760
+
1761
+ <!-- Right Sidebar: Properties -->
1762
+ <div class="editor-panel editor-properties">
1763
+ <div class="panel-header">
1764
+ <h3>Properties</h3>
1765
+ </div>
1766
+ <div class="panel-content" id="properties-content">
1767
+ <div class="no-selection">
1768
+ <p>No entity selected</p>
1769
+ <p class="hint">Select an entity in the Hierarchy or Viewport</p>
1770
+ </div>
1771
+ </div>
1772
+ </div>
1773
+ </div>
1774
+
1775
+ <!-- Bottom Panel: Asset Library & Traits -->
1776
+ <div class="editor-bottom">
1777
+ <div class="panel-header">
1778
+ <h3>Traits & Assets</h3>
1779
+ </div>
1780
+ <div class="panel-content traits-library">
1781
+ <!-- Populated dynamically -->
1782
+ </div>
1783
+ </div>
1784
+ </div>
1785
+
1786
+ <style>
1787
+ .holostudio-editor {
1788
+ width: 100%;
1789
+ height: 100%;
1790
+ display: flex;
1791
+ flex-direction: column;
1792
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Arial, sans-serif;
1793
+ overflow: hidden;
1794
+ }
1795
+
1796
+ .editor-dark {
1797
+ background: #1a1a1a;
1798
+ color: #e0e0e0;
1799
+ }
1800
+
1801
+ .editor-light {
1802
+ background: #f5f5f5;
1803
+ color: #333;
1804
+ }
1805
+
1806
+ /* Toolbar */
1807
+ .editor-toolbar {
1808
+ display: flex;
1809
+ gap: 20px;
1810
+ padding: 10px 15px;
1811
+ background: rgba(0, 0, 0, 0.3);
1812
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
1813
+ }
1814
+
1815
+ .toolbar-section {
1816
+ display: flex;
1817
+ gap: 5px;
1818
+ }
1819
+
1820
+ .toolbar-btn {
1821
+ display: flex;
1822
+ align-items: center;
1823
+ gap: 5px;
1824
+ padding: 6px 12px;
1825
+ background: rgba(96, 165, 250, 0.1);
1826
+ border: 1px solid rgba(96, 165, 250, 0.3);
1827
+ border-radius: 6px;
1828
+ color: #60a5fa;
1829
+ cursor: pointer;
1830
+ font-size: 13px;
1831
+ transition: all 0.2s;
1832
+ }
1833
+
1834
+ .toolbar-btn:hover {
1835
+ background: rgba(96, 165, 250, 0.2);
1836
+ border-color: rgba(96, 165, 250, 0.5);
1837
+ }
1838
+
1839
+ .icon {
1840
+ font-size: 16px;
1841
+ }
1842
+
1843
+ /* Main Content */
1844
+ .editor-content {
1845
+ display: flex;
1846
+ flex: 1;
1847
+ gap: 1px;
1848
+ overflow: hidden;
1849
+ background: rgba(255, 255, 255, 0.05);
1850
+ }
1851
+
1852
+ .editor-panel {
1853
+ display: flex;
1854
+ flex-direction: column;
1855
+ background: rgba(30, 30, 30, 0.95);
1856
+ border: 1px solid rgba(255, 255, 255, 0.1);
1857
+ }
1858
+
1859
+ .editor-hierarchy {
1860
+ width: 250px;
1861
+ flex-shrink: 0;
1862
+ }
1863
+
1864
+ .editor-viewport {
1865
+ flex: 1;
1866
+ min-width: 0;
1867
+ }
1868
+
1869
+ .editor-properties {
1870
+ width: 300px;
1871
+ flex-shrink: 0;
1872
+ }
1873
+
1874
+ .panel-header {
1875
+ display: flex;
1876
+ justify-content: space-between;
1877
+ align-items: center;
1878
+ padding: 10px 15px;
1879
+ background: rgba(0, 0, 0, 0.2);
1880
+ border-bottom: 1px solid rgba(255, 255, 255, 0.1);
1881
+ }
1882
+
1883
+ .panel-header h3 {
1884
+ margin: 0;
1885
+ font-size: 14px;
1886
+ font-weight: 600;
1887
+ color: #60a5fa;
1888
+ }
1889
+
1890
+ .panel-btn {
1891
+ padding: 4px 8px;
1892
+ background: rgba(96, 165, 250, 0.1);
1893
+ border: 1px solid rgba(96, 165, 250, 0.3);
1894
+ border-radius: 4px;
1895
+ color: #60a5fa;
1896
+ cursor: pointer;
1897
+ font-size: 14px;
1898
+ }
1899
+
1900
+ .panel-btn:hover {
1901
+ background: rgba(96, 165, 250, 0.2);
1902
+ }
1903
+
1904
+ .panel-content {
1905
+ flex: 1;
1906
+ overflow-y: auto;
1907
+ padding: 10px;
1908
+ }
1909
+
1910
+ /* Viewport */
1911
+ .viewport-content {
1912
+ position: relative;
1913
+ padding: 0;
1914
+ }
1915
+
1916
+ #editor-canvas {
1917
+ width: 100%;
1918
+ height: 100%;
1919
+ display: block;
1920
+ }
1921
+
1922
+ .viewport-overlay {
1923
+ position: absolute;
1924
+ top: 10px;
1925
+ left: 10px;
1926
+ pointer-events: none;
1927
+ }
1928
+
1929
+ .viewport-stats {
1930
+ background: rgba(0, 0, 0, 0.7);
1931
+ padding: 8px 12px;
1932
+ border-radius: 6px;
1933
+ font-size: 12px;
1934
+ font-family: 'Courier New', monospace;
1935
+ }
1936
+
1937
+ .viewport-stats div {
1938
+ margin-bottom: 3px;
1939
+ }
1940
+
1941
+ .viewport-controls {
1942
+ display: flex;
1943
+ gap: 5px;
1944
+ }
1945
+
1946
+ /* Properties Panel */
1947
+ .no-selection {
1948
+ text-align: center;
1949
+ padding: 40px 20px;
1950
+ color: #888;
1951
+ }
1952
+
1953
+ .no-selection .hint {
1954
+ font-size: 12px;
1955
+ margin-top: 10px;
1956
+ }
1957
+
1958
+ /* Bottom Panel */
1959
+ .editor-bottom {
1960
+ height: 200px;
1961
+ background: rgba(30, 30, 30, 0.95);
1962
+ border-top: 1px solid rgba(255, 255, 255, 0.1);
1963
+ }
1964
+
1965
+ .traits-library {
1966
+ display: grid;
1967
+ grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
1968
+ gap: 10px;
1969
+ }
1970
+ </style>
1971
+ `;
1972
+ }
1973
+ /**
1974
+ * Attach event listeners
1975
+ */
1976
+ attachEventListeners() {
1977
+ if (!this.container) return;
1978
+ this.container.querySelectorAll("[data-action]").forEach((btn) => {
1979
+ btn.addEventListener("click", (e) => {
1980
+ const action = e.currentTarget.getAttribute("data-action");
1981
+ this.handleAction(action);
1982
+ });
1983
+ });
1984
+ document.addEventListener("keydown", (e) => {
1985
+ this.handleKeyboardShortcut(e);
1986
+ });
1987
+ }
1988
+ /**
1989
+ * Handle toolbar action
1990
+ */
1991
+ handleAction(action) {
1992
+ if (!action) return;
1993
+ switch (action) {
1994
+ case "new":
1995
+ this.createNewComposition();
1996
+ break;
1997
+ case "save":
1998
+ this.saveComposition();
1999
+ break;
2000
+ case "undo":
2001
+ this.undo();
2002
+ break;
2003
+ case "redo":
2004
+ this.redo();
2005
+ break;
2006
+ case "add-entity":
2007
+ this.addEntity();
2008
+ break;
2009
+ case "play":
2010
+ this.previewComposition();
2011
+ break;
2012
+ default:
2013
+ if (this.config.debug) {
2014
+ console.debug("[VisualEditor] Unhandled action:", action);
2015
+ }
2016
+ }
2017
+ }
2018
+ /**
2019
+ * Handle keyboard shortcut
2020
+ */
2021
+ handleKeyboardShortcut(e) {
2022
+ const isCtrl = e.ctrlKey || e.metaKey;
2023
+ if (isCtrl && e.key === "s") {
2024
+ e.preventDefault();
2025
+ this.saveComposition();
2026
+ } else if (isCtrl && e.key === "z") {
2027
+ e.preventDefault();
2028
+ if (e.shiftKey) {
2029
+ this.redo();
2030
+ } else {
2031
+ this.undo();
2032
+ }
2033
+ } else if (e.key === "Delete") {
2034
+ e.preventDefault();
2035
+ this.deleteSelectedEntities();
2036
+ }
2037
+ }
2038
+ /**
2039
+ * Create new composition
2040
+ */
2041
+ createNewComposition() {
2042
+ this.composition = {
2043
+ type: "Composition",
2044
+ name: "Untitled",
2045
+ objects: [],
2046
+ templates: [],
2047
+ spatialGroups: [],
2048
+ lights: [],
2049
+ imports: [],
2050
+ timelines: [],
2051
+ audio: [],
2052
+ zones: [],
2053
+ transitions: [],
2054
+ conditionals: [],
2055
+ iterators: [],
2056
+ npcs: [],
2057
+ quests: [],
2058
+ abilities: [],
2059
+ dialogues: [],
2060
+ stateMachines: [],
2061
+ achievements: [],
2062
+ talentTrees: [],
2063
+ shapes: []
2064
+ };
2065
+ this.entities.clear();
2066
+ this.selectedEntityIds.clear();
2067
+ this.history = [];
2068
+ this.historyIndex = -1;
2069
+ this.updateHierarchy();
2070
+ this.updateProperties();
2071
+ if (this.config.debug) {
2072
+ console.debug("[VisualEditor] New composition created");
2073
+ }
2074
+ }
2075
+ /**
2076
+ * Load composition
2077
+ */
2078
+ loadComposition(composition) {
2079
+ this.composition = composition;
2080
+ this.entities.clear();
2081
+ this.selectedEntityIds.clear();
2082
+ let entityIndex = 0;
2083
+ for (const obj of composition.objects || []) {
2084
+ const entity = this.holoObjectToEntity(obj, entityIndex++);
2085
+ this.entities.set(entity.id, entity);
2086
+ }
2087
+ this.updateHierarchy();
2088
+ this.updateProperties();
2089
+ if (this.config.debug) {
2090
+ console.debug("[VisualEditor] Composition loaded:", composition.name);
2091
+ }
2092
+ }
2093
+ /**
2094
+ * Convert HoloObject to EditorEntity
2095
+ */
2096
+ holoObjectToEntity(obj, index) {
2097
+ const getProp = (key) => obj.properties?.find((p) => p.key === key)?.value;
2098
+ const pos = getProp("position");
2099
+ const rot = getProp("rotation");
2100
+ const scl = getProp("scale");
2101
+ return {
2102
+ id: `entity_${index}`,
2103
+ name: obj.name || `Object ${index}`,
2104
+ type: "object",
2105
+ parentId: null,
2106
+ children: [],
2107
+ transform: {
2108
+ position: pos ? [pos[0], pos[1], pos[2]] : [0, 0, 0],
2109
+ rotation: rot ? [rot[0], rot[1], rot[2]] : [0, 0, 0],
2110
+ scale: scl ? [scl[0], scl[1], scl[2]] : [1, 1, 1]
2111
+ },
2112
+ geometry: getProp("geometry"),
2113
+ material: getProp("material"),
2114
+ traits: (obj.traits || []).map((t) => ({
2115
+ name: t.name,
2116
+ properties: t.config,
2117
+ category: this.getTraitCategory(t.name)
2118
+ })),
2119
+ properties: obj.properties?.filter(
2120
+ (p) => !["position", "rotation", "scale", "geometry", "material"].includes(p.key)
2121
+ ).reduce((acc, p) => ({ ...acc, [p.key]: p.value }), {}) || {},
2122
+ visible: true,
2123
+ locked: false,
2124
+ selected: false
2125
+ };
2126
+ }
2127
+ /**
2128
+ * Get trait category
2129
+ */
2130
+ getTraitCategory(traitName) {
2131
+ if (traitName.startsWith("physics")) return "Physics";
2132
+ if (traitName.startsWith("render")) return "Rendering";
2133
+ if (["grabbable", "climbable", "teleport"].includes(traitName)) return "VR";
2134
+ return "General";
2135
+ }
2136
+ /**
2137
+ * Save composition
2138
+ */
2139
+ saveComposition() {
2140
+ if (!this.composition) return null;
2141
+ const objects = [];
2142
+ for (const entity of this.entities.values()) {
2143
+ const properties = [];
2144
+ properties.push({
2145
+ type: "ObjectProperty",
2146
+ key: "position",
2147
+ value: entity.transform.position
2148
+ });
2149
+ properties.push({
2150
+ type: "ObjectProperty",
2151
+ key: "rotation",
2152
+ value: entity.transform.rotation
2153
+ });
2154
+ properties.push({ type: "ObjectProperty", key: "scale", value: entity.transform.scale });
2155
+ if (entity.geometry)
2156
+ properties.push({ type: "ObjectProperty", key: "geometry", value: entity.geometry });
2157
+ if (entity.material)
2158
+ properties.push({
2159
+ type: "ObjectProperty",
2160
+ key: "material",
2161
+ value: entity.material
2162
+ });
2163
+ for (const [k, v] of Object.entries(entity.properties)) {
2164
+ properties.push({ type: "ObjectProperty", key: k, value: v });
2165
+ }
2166
+ const obj = {
2167
+ type: "Object",
2168
+ name: entity.name,
2169
+ properties,
2170
+ traits: entity.traits.map((t) => ({
2171
+ type: "ObjectTrait",
2172
+ name: t.name,
2173
+ config: t.properties || {}
2174
+ })),
2175
+ children: []
2176
+ };
2177
+ objects.push(obj);
2178
+ }
2179
+ this.composition.objects = objects;
2180
+ if (this.config.debug) {
2181
+ console.debug("[VisualEditor] Composition saved:", this.composition);
2182
+ }
2183
+ return this.composition;
2184
+ }
2185
+ /**
2186
+ * Export composition as .holo code
2187
+ */
2188
+ exportToCode() {
2189
+ if (!this.composition) return "";
2190
+ let code = `composition "${this.composition.name}" {
2191
+ `;
2192
+ for (const entity of this.entities.values()) {
2193
+ code += ` object "${entity.name}" {
2194
+ `;
2195
+ for (const trait of entity.traits) {
2196
+ if (trait.properties && Object.keys(trait.properties).length > 0) {
2197
+ const propsStr = Object.entries(trait.properties).map(([k, v]) => `${k}: ${JSON.stringify(v)}`).join(", ");
2198
+ code += ` @${trait.name}(${propsStr})
2199
+ `;
2200
+ } else {
2201
+ code += ` @${trait.name}
2202
+ `;
2203
+ }
2204
+ }
2205
+ if (entity.geometry) {
2206
+ code += ` geometry: "${entity.geometry}"
2207
+ `;
2208
+ }
2209
+ code += ` position: [${entity.transform.position.join(", ")}]
2210
+ `;
2211
+ if (entity.transform.rotation.some((r) => r !== 0)) {
2212
+ code += ` rotation: [${entity.transform.rotation.join(", ")}]
2213
+ `;
2214
+ }
2215
+ if (entity.transform.scale.some((s) => s !== 1)) {
2216
+ code += ` scale: [${entity.transform.scale.join(", ")}]
2217
+ `;
2218
+ }
2219
+ if (entity.material) {
2220
+ code += ` material: ${JSON.stringify(entity.material)}
2221
+ `;
2222
+ }
2223
+ code += ` }
2224
+ `;
2225
+ }
2226
+ code += `}
2227
+ `;
2228
+ return code;
2229
+ }
2230
+ /**
2231
+ * Add new entity
2232
+ */
2233
+ addEntity(type = "box") {
2234
+ const id = `entity_${Date.now()}_${Math.random().toString(36).substring(7)}`;
2235
+ const entity = {
2236
+ id,
2237
+ name: `${type.charAt(0).toUpperCase() + type.slice(1)} ${this.entities.size + 1}`,
2238
+ type: "object",
2239
+ parentId: null,
2240
+ children: [],
2241
+ transform: {
2242
+ position: [0, 0, 0],
2243
+ rotation: [0, 0, 0],
2244
+ scale: [1, 1, 1]
2245
+ },
2246
+ geometry: type,
2247
+ material: {
2248
+ type: "standard",
2249
+ color: "#888888"
2250
+ },
2251
+ traits: [],
2252
+ properties: {},
2253
+ visible: true,
2254
+ locked: false,
2255
+ selected: false
2256
+ };
2257
+ this.entities.set(id, entity);
2258
+ this.addToHistory("create", id, void 0, entity);
2259
+ this.updateHierarchy();
2260
+ if (this.config.debug) {
2261
+ console.debug("[VisualEditor] Entity added:", entity.name);
2262
+ }
2263
+ return entity;
2264
+ }
2265
+ /**
2266
+ * Delete selected entities
2267
+ */
2268
+ deleteSelectedEntities() {
2269
+ for (const id of this.selectedEntityIds) {
2270
+ const entity = this.entities.get(id);
2271
+ if (entity && !entity.locked) {
2272
+ this.addToHistory("delete", id, entity, void 0);
2273
+ this.entities.delete(id);
2274
+ }
2275
+ }
2276
+ this.selectedEntityIds.clear();
2277
+ this.updateHierarchy();
2278
+ this.updateProperties();
2279
+ if (this.config.debug) {
2280
+ console.debug("[VisualEditor] Deleted entities");
2281
+ }
2282
+ }
2283
+ /**
2284
+ * Select entity
2285
+ */
2286
+ selectEntity(id, multiSelect = false) {
2287
+ if (!multiSelect) {
2288
+ for (const entity2 of this.entities.values()) {
2289
+ entity2.selected = false;
2290
+ }
2291
+ this.selectedEntityIds.clear();
2292
+ }
2293
+ const entity = this.entities.get(id);
2294
+ if (entity) {
2295
+ entity.selected = true;
2296
+ this.selectedEntityIds.add(id);
2297
+ this.updateProperties();
2298
+ }
2299
+ }
2300
+ /**
2301
+ * Update entity property
2302
+ */
2303
+ updateEntityProperty(id, path, value) {
2304
+ const entity = this.entities.get(id);
2305
+ if (!entity || entity.locked) return;
2306
+ const previousState = { ...entity };
2307
+ const keys = path.split(".");
2308
+ let target = entity;
2309
+ for (let i = 0; i < keys.length - 1; i++) {
2310
+ target = target[keys[i]];
2311
+ }
2312
+ target[keys[keys.length - 1]] = value;
2313
+ this.addToHistory("modify", id, previousState, entity);
2314
+ this.updateProperties();
2315
+ if (this.config.debug) {
2316
+ console.debug("[VisualEditor] Property updated:", path, "=", value);
2317
+ }
2318
+ }
2319
+ /**
2320
+ * Add history entry
2321
+ */
2322
+ addToHistory(action, entityId, previousState, newState) {
2323
+ this.history = this.history.slice(0, this.historyIndex + 1);
2324
+ this.history.push({
2325
+ action,
2326
+ entityId,
2327
+ previousState,
2328
+ newState,
2329
+ timestamp: Date.now()
2330
+ });
2331
+ this.historyIndex++;
2332
+ if (this.history.length > this.maxHistorySize) {
2333
+ this.history.shift();
2334
+ this.historyIndex--;
2335
+ }
2336
+ }
2337
+ /**
2338
+ * Undo last action
2339
+ */
2340
+ undo() {
2341
+ if (this.historyIndex < 0) return;
2342
+ const entry = this.history[this.historyIndex];
2343
+ if (entry.action === "create") {
2344
+ this.entities.delete(entry.entityId);
2345
+ } else if (entry.action === "delete") {
2346
+ this.entities.set(entry.entityId, entry.previousState);
2347
+ } else if (entry.action === "modify") {
2348
+ this.entities.set(entry.entityId, entry.previousState);
2349
+ }
2350
+ this.historyIndex--;
2351
+ this.updateHierarchy();
2352
+ this.updateProperties();
2353
+ if (this.config.debug) {
2354
+ console.debug("[VisualEditor] Undo:", entry.action);
2355
+ }
2356
+ }
2357
+ /**
2358
+ * Redo last undone action
2359
+ */
2360
+ redo() {
2361
+ if (this.historyIndex >= this.history.length - 1) return;
2362
+ this.historyIndex++;
2363
+ const entry = this.history[this.historyIndex];
2364
+ if (entry.action === "create") {
2365
+ this.entities.set(entry.entityId, entry.newState);
2366
+ } else if (entry.action === "delete") {
2367
+ this.entities.delete(entry.entityId);
2368
+ } else if (entry.action === "modify") {
2369
+ this.entities.set(entry.entityId, entry.newState);
2370
+ }
2371
+ this.updateHierarchy();
2372
+ this.updateProperties();
2373
+ if (this.config.debug) {
2374
+ console.debug("[VisualEditor] Redo:", entry.action);
2375
+ }
2376
+ }
2377
+ /**
2378
+ * Update hierarchy panel
2379
+ */
2380
+ updateHierarchy() {
2381
+ if (!this.hierarchyPanel) return;
2382
+ const content = this.hierarchyPanel.querySelector("#hierarchy-content");
2383
+ if (!content) return;
2384
+ let html = '<div class="hierarchy-list">';
2385
+ for (const entity of this.entities.values()) {
2386
+ const selectedClass = entity.selected ? "selected" : "";
2387
+ const visibleIcon = entity.visible ? "\u{1F441}" : "\u{1F6AB}";
2388
+ const lockedIcon = entity.locked ? "\u{1F512}" : "";
2389
+ html += `
2390
+ <div class="hierarchy-item ${selectedClass}" data-entity-id="${entity.id}">
2391
+ <span class="entity-icon">${this.getEntityIcon(entity.type)}</span>
2392
+ <span class="entity-name">${entity.name}</span>
2393
+ <span class="entity-status">${visibleIcon} ${lockedIcon}</span>
2394
+ </div>
2395
+ `;
2396
+ }
2397
+ html += "</div>";
2398
+ content.innerHTML = html;
2399
+ const entityCount = document.getElementById("entity-count");
2400
+ const selectedCount = document.getElementById("selected-count");
2401
+ if (entityCount) entityCount.textContent = this.entities.size.toString();
2402
+ if (selectedCount) selectedCount.textContent = this.selectedEntityIds.size.toString();
2403
+ }
2404
+ /**
2405
+ * Get entity icon
2406
+ */
2407
+ getEntityIcon(type) {
2408
+ const icons = {
2409
+ object: "\u{1F4E6}",
2410
+ light: "\u{1F4A1}",
2411
+ camera: "\u{1F4F7}",
2412
+ group: "\u{1F4C1}"
2413
+ };
2414
+ return icons[type] || "\u{1F4E6}";
2415
+ }
2416
+ /**
2417
+ * Update properties panel
2418
+ */
2419
+ updateProperties() {
2420
+ if (!this.propertiesPanel) return;
2421
+ const content = this.propertiesPanel.querySelector("#properties-content");
2422
+ if (!content) return;
2423
+ if (this.selectedEntityIds.size === 0) {
2424
+ content.innerHTML = `
2425
+ <div class="no-selection">
2426
+ <p>No entity selected</p>
2427
+ <p class="hint">Select an entity in the Hierarchy or Viewport</p>
2428
+ </div>
2429
+ `;
2430
+ return;
2431
+ }
2432
+ const entityId = Array.from(this.selectedEntityIds)[0];
2433
+ const entity = this.entities.get(entityId);
2434
+ if (!entity) return;
2435
+ const html = `
2436
+ <div class="properties-inspector">
2437
+ <div class="property-group">
2438
+ <h4>Transform</h4>
2439
+ <div class="property-row">
2440
+ <label>Position</label>
2441
+ <div class="vector3-input">
2442
+ <input type="number" value="${entity.transform.position[0]}" step="0.1" data-property="transform.position.0">
2443
+ <input type="number" value="${entity.transform.position[1]}" step="0.1" data-property="transform.position.1">
2444
+ <input type="number" value="${entity.transform.position[2]}" step="0.1" data-property="transform.position.2">
2445
+ </div>
2446
+ </div>
2447
+ <div class="property-row">
2448
+ <label>Rotation</label>
2449
+ <div class="vector3-input">
2450
+ <input type="number" value="${entity.transform.rotation[0]}" step="1" data-property="transform.rotation.0">
2451
+ <input type="number" value="${entity.transform.rotation[1]}" step="1" data-property="transform.rotation.1">
2452
+ <input type="number" value="${entity.transform.rotation[2]}" step="1" data-property="transform.rotation.2">
2453
+ </div>
2454
+ </div>
2455
+ <div class="property-row">
2456
+ <label>Scale</label>
2457
+ <div class="vector3-input">
2458
+ <input type="number" value="${entity.transform.scale[0]}" step="0.1" data-property="transform.scale.0">
2459
+ <input type="number" value="${entity.transform.scale[1]}" step="0.1" data-property="transform.scale.1">
2460
+ <input type="number" value="${entity.transform.scale[2]}" step="0.1" data-property="transform.scale.2">
2461
+ </div>
2462
+ </div>
2463
+ </div>
2464
+
2465
+ <div class="property-group">
2466
+ <h4>Traits</h4>
2467
+ <div class="traits-list">
2468
+ ${entity.traits.map((t) => `<div class="trait-badge">${t.name}</div>`).join("")}
2469
+ </div>
2470
+ <button class="add-trait-btn">+ Add Trait</button>
2471
+ </div>
2472
+ </div>
2473
+
2474
+ <style>
2475
+ .properties-inspector {
2476
+ padding: 10px;
2477
+ }
2478
+
2479
+ .property-group {
2480
+ margin-bottom: 20px;
2481
+ padding: 10px;
2482
+ background: rgba(0, 0, 0, 0.2);
2483
+ border-radius: 6px;
2484
+ }
2485
+
2486
+ .property-group h4 {
2487
+ margin: 0 0 10px 0;
2488
+ font-size: 13px;
2489
+ color: #60a5fa;
2490
+ }
2491
+
2492
+ .property-row {
2493
+ margin-bottom: 10px;
2494
+ }
2495
+
2496
+ .property-row label {
2497
+ display: block;
2498
+ font-size: 12px;
2499
+ margin-bottom: 5px;
2500
+ color: #94a3b8;
2501
+ }
2502
+
2503
+ .vector3-input {
2504
+ display: grid;
2505
+ grid-template-columns: 1fr 1fr 1fr;
2506
+ gap: 5px;
2507
+ }
2508
+
2509
+ .vector3-input input {
2510
+ padding: 5px;
2511
+ background: rgba(0, 0, 0, 0.3);
2512
+ border: 1px solid rgba(255, 255, 255, 0.2);
2513
+ border-radius: 4px;
2514
+ color: #fff;
2515
+ font-size: 12px;
2516
+ }
2517
+
2518
+ .traits-list {
2519
+ display: flex;
2520
+ flex-wrap: wrap;
2521
+ gap: 5px;
2522
+ margin-bottom: 10px;
2523
+ }
2524
+
2525
+ .trait-badge {
2526
+ padding: 4px 8px;
2527
+ background: rgba(96, 165, 250, 0.2);
2528
+ border: 1px solid rgba(96, 165, 250, 0.4);
2529
+ border-radius: 12px;
2530
+ font-size: 11px;
2531
+ color: #60a5fa;
2532
+ }
2533
+
2534
+ .add-trait-btn {
2535
+ width: 100%;
2536
+ padding: 6px;
2537
+ background: rgba(34, 197, 94, 0.1);
2538
+ border: 1px solid rgba(34, 197, 94, 0.3);
2539
+ border-radius: 4px;
2540
+ color: #4ade80;
2541
+ cursor: pointer;
2542
+ font-size: 12px;
2543
+ }
2544
+
2545
+ .add-trait-btn:hover {
2546
+ background: rgba(34, 197, 94, 0.2);
2547
+ }
2548
+
2549
+ .hierarchy-list {
2550
+ padding: 5px;
2551
+ }
2552
+
2553
+ .hierarchy-item {
2554
+ display: flex;
2555
+ align-items: center;
2556
+ gap: 8px;
2557
+ padding: 6px 10px;
2558
+ margin-bottom: 2px;
2559
+ border-radius: 4px;
2560
+ cursor: pointer;
2561
+ font-size: 13px;
2562
+ }
2563
+
2564
+ .hierarchy-item:hover {
2565
+ background: rgba(96, 165, 250, 0.1);
2566
+ }
2567
+
2568
+ .hierarchy-item.selected {
2569
+ background: rgba(96, 165, 250, 0.3);
2570
+ }
2571
+
2572
+ .entity-icon {
2573
+ font-size: 14px;
2574
+ }
2575
+
2576
+ .entity-name {
2577
+ flex: 1;
2578
+ }
2579
+
2580
+ .entity-status {
2581
+ font-size: 11px;
2582
+ }
2583
+ </style>
2584
+ `;
2585
+ content.innerHTML = html;
2586
+ }
2587
+ /**
2588
+ * Preview composition
2589
+ */
2590
+ previewComposition() {
2591
+ const composition = this.saveComposition();
2592
+ if (!composition) return;
2593
+ if (this.config.debug) {
2594
+ console.debug("[VisualEditor] Preview:", composition);
2595
+ }
2596
+ this.container?.dispatchEvent(
2597
+ new CustomEvent("holostudio:preview", {
2598
+ detail: { composition }
2599
+ })
2600
+ );
2601
+ }
2602
+ /**
2603
+ * Start auto-save
2604
+ */
2605
+ startAutoSave() {
2606
+ if (typeof window === "undefined") return;
2607
+ this.autoSaveTimer = window.setInterval(() => {
2608
+ this.saveComposition();
2609
+ if (this.config.debug) {
2610
+ console.debug("[VisualEditor] Auto-saved");
2611
+ }
2612
+ }, this.config.autoSaveInterval);
2613
+ }
2614
+ /**
2615
+ * Stop auto-save
2616
+ */
2617
+ stopAutoSave() {
2618
+ if (this.autoSaveTimer) {
2619
+ clearInterval(this.autoSaveTimer);
2620
+ this.autoSaveTimer = void 0;
2621
+ }
2622
+ }
2623
+ /**
2624
+ * Dispose editor
2625
+ */
2626
+ dispose() {
2627
+ this.stopAutoSave();
2628
+ this.entities.clear();
2629
+ this.selectedEntityIds.clear();
2630
+ this.history = [];
2631
+ if (this.config.debug) {
2632
+ console.debug("[VisualEditor] Disposed");
2633
+ }
2634
+ }
2635
+ /**
2636
+ * Get all entities
2637
+ */
2638
+ getEntities() {
2639
+ return Array.from(this.entities.values());
2640
+ }
2641
+ /**
2642
+ * Get selected entities
2643
+ */
2644
+ getSelectedEntities() {
2645
+ return Array.from(this.selectedEntityIds).map((id) => this.entities.get(id)).filter(Boolean);
2646
+ }
2647
+ /**
2648
+ * Get composition
2649
+ */
2650
+ getComposition() {
2651
+ return this.saveComposition();
2652
+ }
2653
+ };
2654
+
2655
+ // src/tools/MixamoIntegration.ts
2656
+ var MIXAMO_API_BASE = "https://www.mixamo.com/api/v1";
2657
+ var MAX_RETRIES = 5;
2658
+ var INITIAL_BACKOFF_MS = 1e3;
2659
+ var MAX_BACKOFF_MS = 3e4;
2660
+ var SUPPORTED_FORMATS = ["fbx", "glb"];
2661
+ var MixamoError = class extends Error {
2662
+ constructor(message, code) {
2663
+ super(message);
2664
+ this.name = "MixamoError";
2665
+ this.code = code;
2666
+ }
2667
+ };
2668
+ var MixamoRateLimitError = class extends MixamoError {
2669
+ constructor(retryAfterSeconds, attemptNumber) {
2670
+ super(
2671
+ `Mixamo API rate limit exceeded. Retry after ${retryAfterSeconds}s (attempt ${attemptNumber}).`,
2672
+ "RATE_LIMIT_EXCEEDED"
2673
+ );
2674
+ this.name = "MixamoRateLimitError";
2675
+ this.retryAfterSeconds = retryAfterSeconds;
2676
+ this.attemptNumber = attemptNumber;
2677
+ }
2678
+ };
2679
+ var MixamoTopologyError = class extends MixamoError {
2680
+ constructor(issues) {
2681
+ const errorIssues = issues.filter((i) => i.severity === "error");
2682
+ const summary = errorIssues.map((i) => i.message).join("; ");
2683
+ super(`Mesh topology unsupported for auto-rigging: ${summary}`, "UNSUPPORTED_TOPOLOGY");
2684
+ this.name = "MixamoTopologyError";
2685
+ this.issues = issues;
2686
+ }
2687
+ };
2688
+ var MixamoFormatError = class extends MixamoError {
2689
+ constructor(providedFormat) {
2690
+ super(
2691
+ `Unsupported mesh format "${providedFormat}". Supported formats: ${SUPPORTED_FORMATS.join(", ")}.`,
2692
+ "UNSUPPORTED_FORMAT"
2693
+ );
2694
+ this.name = "MixamoFormatError";
2695
+ this.providedFormat = providedFormat;
2696
+ this.supportedFormats = SUPPORTED_FORMATS;
2697
+ }
2698
+ };
2699
+ var MixamoAPI = class {
2700
+ constructor(config) {
2701
+ this.config = {
2702
+ credentials: config.credentials,
2703
+ maxRetries: config.maxRetries ?? MAX_RETRIES,
2704
+ initialBackoffMs: config.initialBackoffMs ?? INITIAL_BACKOFF_MS,
2705
+ debug: config.debug ?? false,
2706
+ timeoutMs: config.timeoutMs ?? 6e4
2707
+ };
2708
+ }
2709
+ // -----------------------------------------------------------------------
2710
+ // Public API
2711
+ // -----------------------------------------------------------------------
2712
+ /**
2713
+ * Upload a character mesh to Mixamo for rigging.
2714
+ *
2715
+ * Supports FBX and GLB formats. Validates mesh topology before upload
2716
+ * and throws `MixamoTopologyError` for unsupported geometries.
2717
+ *
2718
+ * @param data - Raw mesh file data as Buffer or ArrayBuffer.
2719
+ * @param filename - Original filename (used to detect format from extension).
2720
+ * @returns Upload result with character ID and mesh metadata.
2721
+ *
2722
+ * @throws {MixamoFormatError} If the file format is not FBX or GLB.
2723
+ * @throws {MixamoTopologyError} If the mesh has unsupported topology.
2724
+ * @throws {MixamoRateLimitError} If the API rate limit is exceeded after max retries.
2725
+ * @throws {MixamoError} For other API errors.
2726
+ */
2727
+ async uploadCharacter(data, filename) {
2728
+ const format = this.detectFormat(filename);
2729
+ const topologyIssues = this.validateTopology(data, format);
2730
+ const errors = topologyIssues.filter((i) => i.severity === "error");
2731
+ if (errors.length > 0) {
2732
+ throw new MixamoTopologyError(topologyIssues);
2733
+ }
2734
+ if (topologyIssues.length > 0 && this.config.debug) {
2735
+ console.warn(
2736
+ "[MixamoAPI] Topology warnings:",
2737
+ topologyIssues.map((i) => i.message)
2738
+ );
2739
+ }
2740
+ const formData = this.createFormData(data, filename, format);
2741
+ const response = await this.requestWithRetry("POST", "/characters/upload", formData, {
2742
+ isFormData: true
2743
+ });
2744
+ const result = {
2745
+ characterId: response.character_id,
2746
+ filename,
2747
+ format,
2748
+ vertexCount: response.vertex_count,
2749
+ polygonCount: response.polygon_count,
2750
+ topologyValid: response.topology_valid,
2751
+ uploadedAt: (/* @__PURE__ */ new Date()).toISOString()
2752
+ };
2753
+ if (this.config.debug) {
2754
+ console.debug("[MixamoAPI] Character uploaded:", result.characterId);
2755
+ }
2756
+ return result;
2757
+ }
2758
+ /**
2759
+ * Auto-rig an uploaded character.
2760
+ *
2761
+ * Triggers Mixamo's server-side auto-rigging pipeline which detects
2762
+ * joint positions and generates a humanoid skeleton.
2763
+ *
2764
+ * @param characterId - The character ID from a previous upload.
2765
+ * @param rigType - Rig type to generate. Default: 'humanoid'.
2766
+ * @returns Rig result with bone count and quality score.
2767
+ *
2768
+ * @throws {MixamoRateLimitError} If the API rate limit is exceeded after max retries.
2769
+ * @throws {MixamoError} For other API errors (e.g. character not found).
2770
+ */
2771
+ async autoRig(characterId, rigType = "humanoid") {
2772
+ const response = await this.requestWithRetry("POST", `/characters/${encodeURIComponent(characterId)}/rig`, {
2773
+ rig_type: rigType
2774
+ });
2775
+ const result = {
2776
+ characterId: response.character_id,
2777
+ rigType: response.rig_type,
2778
+ boneCount: response.bone_count,
2779
+ qualityScore: response.quality_score,
2780
+ success: response.success,
2781
+ warnings: response.warnings ?? [],
2782
+ riggedAt: (/* @__PURE__ */ new Date()).toISOString()
2783
+ };
2784
+ if (this.config.debug) {
2785
+ console.debug(
2786
+ `[MixamoAPI] Character rigged: ${result.characterId} (${result.boneCount} bones, quality: ${result.qualityScore})`
2787
+ );
2788
+ }
2789
+ return result;
2790
+ }
2791
+ /**
2792
+ * List available animations from the Mixamo library.
2793
+ *
2794
+ * Supports pagination, category filtering, and text search.
2795
+ *
2796
+ * @param options - Listing options (page, limit, category, query, motionType).
2797
+ * @returns Paginated list of animations.
2798
+ *
2799
+ * @throws {MixamoRateLimitError} If the API rate limit is exceeded after max retries.
2800
+ * @throws {MixamoError} For other API errors.
2801
+ */
2802
+ async listAnimations(options = {}) {
2803
+ const params = new URLSearchParams();
2804
+ if (options.page) params.set("page", String(options.page));
2805
+ if (options.limit) params.set("limit", String(options.limit));
2806
+ if (options.category) params.set("category", options.category);
2807
+ if (options.query) params.set("query", options.query);
2808
+ if (options.motionType) params.set("motion_type", options.motionType);
2809
+ const queryString = params.toString();
2810
+ const endpoint = `/animations${queryString ? `?${queryString}` : ""}`;
2811
+ const response = await this.requestWithRetry("GET", endpoint);
2812
+ const result = {
2813
+ animations: response.animations.map((a) => ({
2814
+ id: a.id,
2815
+ name: a.name,
2816
+ category: a.category,
2817
+ duration: a.duration,
2818
+ loopable: a.loopable,
2819
+ motionType: a.motion_type,
2820
+ thumbnailUrl: a.thumbnail_url
2821
+ })),
2822
+ total: response.total,
2823
+ page: response.page,
2824
+ limit: response.limit,
2825
+ hasMore: response.has_more
2826
+ };
2827
+ if (this.config.debug) {
2828
+ console.debug(
2829
+ `[MixamoAPI] Listed ${result.animations.length} animations (${result.total} total)`
2830
+ );
2831
+ }
2832
+ return result;
2833
+ }
2834
+ /**
2835
+ * Download an animation applied to a specific character.
2836
+ *
2837
+ * The animation is retargeted to the character's skeleton and returned
2838
+ * as raw file data in the requested format (FBX or GLB).
2839
+ *
2840
+ * @param options - Download options (characterId, animationId, format, etc.)
2841
+ * @returns Download result with raw file data and metadata.
2842
+ *
2843
+ * @throws {MixamoFormatError} If the requested format is not supported.
2844
+ * @throws {MixamoRateLimitError} If the API rate limit is exceeded after max retries.
2845
+ * @throws {MixamoError} For other API errors.
2846
+ */
2847
+ async downloadAnimation(options) {
2848
+ const format = options.format ?? "fbx";
2849
+ if (!SUPPORTED_FORMATS.includes(format)) {
2850
+ throw new MixamoFormatError(format);
2851
+ }
2852
+ const response = await this.requestWithRetry(
2853
+ "POST",
2854
+ `/characters/${encodeURIComponent(options.characterId)}/animations/${encodeURIComponent(options.animationId)}/download`,
2855
+ {
2856
+ format,
2857
+ include_skin: options.includeSkin ?? false,
2858
+ fps: options.fps ?? 30,
2859
+ keyframe_reduction: options.keyframeReduction ?? 0
2860
+ },
2861
+ { binary: true }
2862
+ );
2863
+ const suggestedFilename = `${options.characterId}_${options.animationId}.${format}`;
2864
+ const result = {
2865
+ data: response,
2866
+ contentType: format === "fbx" ? "application/octet-stream" : "model/gltf-binary",
2867
+ filename: suggestedFilename,
2868
+ sizeBytes: response.byteLength,
2869
+ format
2870
+ };
2871
+ if (this.config.debug) {
2872
+ console.debug(
2873
+ `[MixamoAPI] Animation downloaded: ${result.filename} (${result.sizeBytes} bytes)`
2874
+ );
2875
+ }
2876
+ return result;
2877
+ }
2878
+ // -----------------------------------------------------------------------
2879
+ // Format Detection & Validation
2880
+ // -----------------------------------------------------------------------
2881
+ /**
2882
+ * Detect mesh format from filename extension.
2883
+ *
2884
+ * @param filename - The mesh filename.
2885
+ * @returns The detected format.
2886
+ * @throws {MixamoFormatError} If the extension is not recognized.
2887
+ */
2888
+ detectFormat(filename) {
2889
+ const ext = filename.split(".").pop()?.toLowerCase();
2890
+ if (ext === "fbx") return "fbx";
2891
+ if (ext === "glb" || ext === "gltf") return "glb";
2892
+ throw new MixamoFormatError(ext ?? "unknown");
2893
+ }
2894
+ /**
2895
+ * Validate mesh topology for auto-rigging compatibility.
2896
+ *
2897
+ * Performs pre-flight checks on the mesh data to detect issues that
2898
+ * would cause the auto-rigging pipeline to fail or produce poor results.
2899
+ *
2900
+ * @param data - Raw mesh file data.
2901
+ * @param format - The mesh format.
2902
+ * @returns Array of topology issues found (may be empty if mesh is clean).
2903
+ */
2904
+ validateTopology(data, format) {
2905
+ const issues = [];
2906
+ const byteLength = data instanceof ArrayBuffer ? data.byteLength : data.length;
2907
+ if (byteLength < 100) {
2908
+ issues.push({
2909
+ code: "DEGENERATE_FACES",
2910
+ message: "File is too small to contain valid mesh data.",
2911
+ severity: "error"
2912
+ });
2913
+ return issues;
2914
+ }
2915
+ if (format === "fbx") {
2916
+ const header = new Uint8Array(
2917
+ data instanceof ArrayBuffer ? data : data.buffer,
2918
+ data instanceof ArrayBuffer ? 0 : data.byteOffset,
2919
+ Math.min(23, byteLength)
2920
+ );
2921
+ const magic = String.fromCharCode(...header.slice(0, 20));
2922
+ if (!magic.startsWith("Kaydara FBX Binary")) {
2923
+ if (byteLength < 1e3) {
2924
+ issues.push({
2925
+ code: "DEGENERATE_FACES",
2926
+ message: "FBX file does not contain a valid binary header. ASCII FBX may have limited support.",
2927
+ severity: "warning"
2928
+ });
2929
+ }
2930
+ }
2931
+ }
2932
+ if (format === "glb") {
2933
+ const header = new Uint8Array(
2934
+ data instanceof ArrayBuffer ? data : data.buffer,
2935
+ data instanceof ArrayBuffer ? 0 : data.byteOffset,
2936
+ Math.min(4, byteLength)
2937
+ );
2938
+ const glbMagic = header[0] === 103 && header[1] === 108 && header[2] === 84 && header[3] === 70;
2939
+ if (!glbMagic) {
2940
+ issues.push({
2941
+ code: "DEGENERATE_FACES",
2942
+ message: "GLB file does not contain a valid glTF binary header.",
2943
+ severity: "error"
2944
+ });
2945
+ }
2946
+ }
2947
+ const estimatedPolygons = byteLength / 50;
2948
+ if (estimatedPolygons > 5e5) {
2949
+ issues.push({
2950
+ code: "EXCESSIVE_POLYGONS",
2951
+ message: `Estimated polygon count (~${Math.round(estimatedPolygons).toLocaleString()}) exceeds recommended limit of 500,000 for auto-rigging. Consider decimating the mesh.`,
2952
+ severity: "warning"
2953
+ });
2954
+ }
2955
+ if (estimatedPolygons > 2e6) {
2956
+ issues.push({
2957
+ code: "EXCESSIVE_POLYGONS",
2958
+ message: `Estimated polygon count (~${Math.round(estimatedPolygons).toLocaleString()}) far exceeds the maximum of 2,000,000 for auto-rigging.`,
2959
+ severity: "error"
2960
+ });
2961
+ }
2962
+ return issues;
2963
+ }
2964
+ // -----------------------------------------------------------------------
2965
+ // HTTP Utilities
2966
+ // -----------------------------------------------------------------------
2967
+ /**
2968
+ * Create form data for file upload.
2969
+ */
2970
+ createFormData(data, filename, format) {
2971
+ return {
2972
+ file: data,
2973
+ filename,
2974
+ format,
2975
+ type: "character"
2976
+ };
2977
+ }
2978
+ /**
2979
+ * Execute an HTTP request with exponential backoff retry for rate limits.
2980
+ *
2981
+ * @param method - HTTP method.
2982
+ * @param endpoint - API endpoint path (appended to base URL).
2983
+ * @param body - Request body (JSON or form data).
2984
+ * @param options - Additional request options.
2985
+ * @returns Parsed response data.
2986
+ */
2987
+ async requestWithRetry(method, endpoint, body, options) {
2988
+ const baseUrl = this.config.credentials.apiBase ?? MIXAMO_API_BASE;
2989
+ const url = `${baseUrl}${endpoint}`;
2990
+ let lastError = null;
2991
+ for (let attempt = 0; attempt < this.config.maxRetries; attempt++) {
2992
+ try {
2993
+ const headers = {
2994
+ Authorization: `Bearer ${this.config.credentials.accessToken}`,
2995
+ Accept: options?.binary ? "application/octet-stream" : "application/json"
2996
+ };
2997
+ const fetchOptions = {
2998
+ method,
2999
+ headers,
3000
+ signal: AbortSignal.timeout(this.config.timeoutMs)
3001
+ };
3002
+ if (body && method !== "GET") {
3003
+ if (options?.isFormData) {
3004
+ headers["Content-Type"] = "multipart/form-data";
3005
+ fetchOptions.body = JSON.stringify(body);
3006
+ } else {
3007
+ headers["Content-Type"] = "application/json";
3008
+ fetchOptions.body = JSON.stringify(body);
3009
+ }
3010
+ }
3011
+ const response = await fetch(url, fetchOptions);
3012
+ if (response.status === 429) {
3013
+ const retryAfter = parseInt(response.headers.get("Retry-After") ?? "0", 10);
3014
+ const backoffMs = Math.min(
3015
+ retryAfter > 0 ? retryAfter * 1e3 : this.config.initialBackoffMs * Math.pow(2, attempt),
3016
+ MAX_BACKOFF_MS
3017
+ );
3018
+ if (this.config.debug) {
3019
+ console.warn(
3020
+ `[MixamoAPI] Rate limited (attempt ${attempt + 1}/${this.config.maxRetries}). Retrying in ${backoffMs}ms...`
3021
+ );
3022
+ }
3023
+ if (attempt === this.config.maxRetries - 1) {
3024
+ throw new MixamoRateLimitError(Math.ceil(backoffMs / 1e3), attempt + 1);
3025
+ }
3026
+ await this.sleep(backoffMs);
3027
+ continue;
3028
+ }
3029
+ if (!response.ok) {
3030
+ let errorMessage = `Mixamo API error: ${response.status} ${response.statusText}`;
3031
+ try {
3032
+ const errorBody = await response.json();
3033
+ if (errorBody.message) {
3034
+ errorMessage = errorBody.message;
3035
+ }
3036
+ if (errorBody.error) {
3037
+ errorMessage = errorBody.error;
3038
+ }
3039
+ } catch {
3040
+ }
3041
+ throw new MixamoError(errorMessage, `HTTP_${response.status}`);
3042
+ }
3043
+ if (options?.binary) {
3044
+ return await response.arrayBuffer();
3045
+ }
3046
+ return await response.json();
3047
+ } catch (error) {
3048
+ if (error instanceof MixamoRateLimitError || error instanceof MixamoTopologyError || error instanceof MixamoFormatError) {
3049
+ throw error;
3050
+ }
3051
+ lastError = error instanceof Error ? error : new Error(String(error));
3052
+ if (attempt < this.config.maxRetries - 1) {
3053
+ const backoffMs = Math.min(
3054
+ this.config.initialBackoffMs * Math.pow(2, attempt),
3055
+ MAX_BACKOFF_MS
3056
+ );
3057
+ if (this.config.debug) {
3058
+ console.warn(
3059
+ `[MixamoAPI] Request failed (attempt ${attempt + 1}/${this.config.maxRetries}): ${lastError.message}. Retrying in ${backoffMs}ms...`
3060
+ );
3061
+ }
3062
+ await this.sleep(backoffMs);
3063
+ continue;
3064
+ }
3065
+ }
3066
+ }
3067
+ throw new MixamoError(
3068
+ `Request failed after ${this.config.maxRetries} attempts: ${lastError?.message ?? "Unknown error"}`,
3069
+ "MAX_RETRIES_EXCEEDED"
3070
+ );
3071
+ }
3072
+ /**
3073
+ * Sleep for a given number of milliseconds.
3074
+ */
3075
+ sleep(ms) {
3076
+ return new Promise((resolve) => setTimeout(resolve, ms));
3077
+ }
3078
+ };
3079
+ var MixamoPresetMapper = class {
3080
+ constructor() {
3081
+ this.mappings = /* @__PURE__ */ new Map();
3082
+ this.reverseIndex = /* @__PURE__ */ new Map();
3083
+ this.loadDefaultMappings();
3084
+ }
3085
+ /**
3086
+ * Get the Mixamo animation mapping for a preset name.
3087
+ *
3088
+ * @param presetName - HoloScript animation preset name (e.g. 'walk', 'idle').
3089
+ * @returns The mapping, or undefined if no mapping exists.
3090
+ */
3091
+ getMixamoId(presetName) {
3092
+ return this.mappings.get(presetName.toLowerCase());
3093
+ }
3094
+ /**
3095
+ * Get the HoloScript preset name for a Mixamo animation ID.
3096
+ *
3097
+ * @param mixamoId - Mixamo animation ID.
3098
+ * @returns The preset name, or undefined if no reverse mapping exists.
3099
+ */
3100
+ getPresetName(mixamoId) {
3101
+ return this.reverseIndex.get(mixamoId);
3102
+ }
3103
+ /**
3104
+ * Get all preset-to-Mixamo mappings.
3105
+ *
3106
+ * @returns Array of all mappings.
3107
+ */
3108
+ getAllMappings() {
3109
+ return Array.from(this.mappings.values());
3110
+ }
3111
+ /**
3112
+ * Check if a mapping exists for a given preset name.
3113
+ *
3114
+ * @param presetName - HoloScript animation preset name.
3115
+ */
3116
+ hasMapping(presetName) {
3117
+ return this.mappings.has(presetName.toLowerCase());
3118
+ }
3119
+ /**
3120
+ * Register a custom preset-to-Mixamo mapping.
3121
+ *
3122
+ * @param mapping - The mapping to register.
3123
+ */
3124
+ registerMapping(mapping) {
3125
+ const key = mapping.presetName.toLowerCase();
3126
+ this.mappings.set(key, mapping);
3127
+ this.reverseIndex.set(mapping.mixamoAnimationId, key);
3128
+ for (const altId of mapping.alternativeIds) {
3129
+ this.reverseIndex.set(altId, key);
3130
+ }
3131
+ }
3132
+ /**
3133
+ * Search for mappings by Mixamo clip name (partial, case-insensitive).
3134
+ *
3135
+ * @param query - Search query string.
3136
+ * @returns Array of matching mappings.
3137
+ */
3138
+ searchByClipName(query) {
3139
+ const q = query.toLowerCase();
3140
+ const results = [];
3141
+ for (const mapping of this.mappings.values()) {
3142
+ if (mapping.mixamoClipName.toLowerCase().includes(q)) {
3143
+ results.push(mapping);
3144
+ }
3145
+ }
3146
+ return results;
3147
+ }
3148
+ /**
3149
+ * Load the default preset-to-Mixamo mappings for all 15 canonical presets.
3150
+ *
3151
+ * Mixamo animation IDs are based on the Mixamo REST API's internal
3152
+ * identifiers. These map to the canonical clip names defined in
3153
+ * @holoscript/animation-presets.
3154
+ */
3155
+ loadDefaultMappings() {
3156
+ const defaults = [
3157
+ {
3158
+ presetName: "walk",
3159
+ mixamoAnimationId: "14667",
3160
+ mixamoClipName: "Walking",
3161
+ alternativeIds: ["14668", "14670", "14671", "14672"],
3162
+ speedAdjustment: 1
3163
+ },
3164
+ {
3165
+ presetName: "run",
3166
+ mixamoAnimationId: "14524",
3167
+ mixamoClipName: "Running",
3168
+ alternativeIds: ["14525", "14526", "14527", "14528"],
3169
+ speedAdjustment: 1
3170
+ },
3171
+ {
3172
+ presetName: "idle",
3173
+ mixamoAnimationId: "14330",
3174
+ mixamoClipName: "Idle",
3175
+ alternativeIds: ["14331", "14332", "14333"],
3176
+ speedAdjustment: 1
3177
+ },
3178
+ {
3179
+ presetName: "jump",
3180
+ mixamoAnimationId: "14340",
3181
+ mixamoClipName: "Jump",
3182
+ alternativeIds: ["14341", "14342", "14343", "14344"],
3183
+ speedAdjustment: 1
3184
+ },
3185
+ {
3186
+ presetName: "attack",
3187
+ mixamoAnimationId: "14084",
3188
+ mixamoClipName: "Sword And Shield Attack",
3189
+ alternativeIds: ["14085", "14086", "14088", "14090"],
3190
+ speedAdjustment: 1
3191
+ },
3192
+ {
3193
+ presetName: "dance",
3194
+ mixamoAnimationId: "14189",
3195
+ mixamoClipName: "Hip Hop Dancing",
3196
+ alternativeIds: ["14190", "14191", "14192", "14193"],
3197
+ speedAdjustment: 1
3198
+ },
3199
+ {
3200
+ presetName: "wave",
3201
+ mixamoAnimationId: "14660",
3202
+ mixamoClipName: "Waving",
3203
+ alternativeIds: ["14661"],
3204
+ speedAdjustment: 1
3205
+ },
3206
+ {
3207
+ presetName: "speak",
3208
+ mixamoAnimationId: "14591",
3209
+ mixamoClipName: "Talking",
3210
+ alternativeIds: ["14592", "14593"],
3211
+ speedAdjustment: 1
3212
+ },
3213
+ {
3214
+ presetName: "sit",
3215
+ mixamoAnimationId: "14564",
3216
+ mixamoClipName: "Sitting",
3217
+ alternativeIds: ["14565", "14566"],
3218
+ speedAdjustment: 1
3219
+ },
3220
+ {
3221
+ presetName: "sleep",
3222
+ mixamoAnimationId: "14572",
3223
+ mixamoClipName: "Sleeping Idle",
3224
+ alternativeIds: ["14573"],
3225
+ speedAdjustment: 1
3226
+ },
3227
+ {
3228
+ presetName: "crouch",
3229
+ mixamoAnimationId: "14180",
3230
+ mixamoClipName: "Crouching Idle",
3231
+ alternativeIds: ["14181", "14182", "14183"],
3232
+ speedAdjustment: 1
3233
+ },
3234
+ {
3235
+ presetName: "swim",
3236
+ mixamoAnimationId: "14610",
3237
+ mixamoClipName: "Swimming",
3238
+ alternativeIds: ["14611", "14612", "14613"],
3239
+ speedAdjustment: 0.9
3240
+ },
3241
+ {
3242
+ presetName: "fly",
3243
+ mixamoAnimationId: "14250",
3244
+ mixamoClipName: "Flying",
3245
+ alternativeIds: ["14251", "14252", "14253"],
3246
+ speedAdjustment: 0.8
3247
+ },
3248
+ {
3249
+ presetName: "climb",
3250
+ mixamoAnimationId: "14160",
3251
+ mixamoClipName: "Climbing",
3252
+ alternativeIds: ["14161", "14162", "14163"],
3253
+ speedAdjustment: 0.8
3254
+ },
3255
+ {
3256
+ presetName: "emote",
3257
+ mixamoAnimationId: "14220",
3258
+ mixamoClipName: "Cheering",
3259
+ alternativeIds: ["14221", "14222", "14223"],
3260
+ speedAdjustment: 1
3261
+ }
3262
+ ];
3263
+ for (const mapping of defaults) {
3264
+ this.registerMapping(mapping);
3265
+ }
3266
+ }
3267
+ };
3268
+ var CharacterTemplateRegistry = class {
3269
+ /**
3270
+ * Creates a new CharacterTemplateRegistry pre-loaded with default templates.
3271
+ * Pass `false` to create an empty registry.
3272
+ */
3273
+ constructor(loadDefaults = true) {
3274
+ this.templates = /* @__PURE__ */ new Map();
3275
+ if (loadDefaults) {
3276
+ this.loadDefaultTemplates();
3277
+ }
3278
+ }
3279
+ // -----------------------------------------------------------------------
3280
+ // Lookup
3281
+ // -----------------------------------------------------------------------
3282
+ /**
3283
+ * Get a template by its unique identifier.
3284
+ *
3285
+ * @param id - Template ID.
3286
+ * @returns The template, or undefined if not found.
3287
+ */
3288
+ get(id) {
3289
+ return this.templates.get(id);
3290
+ }
3291
+ /**
3292
+ * Check if a template exists.
3293
+ *
3294
+ * @param id - Template ID.
3295
+ */
3296
+ has(id) {
3297
+ return this.templates.has(id);
3298
+ }
3299
+ // -----------------------------------------------------------------------
3300
+ // Filtering
3301
+ // -----------------------------------------------------------------------
3302
+ /**
3303
+ * Get all templates matching a specific archetype.
3304
+ *
3305
+ * @param archetype - The character archetype to filter by.
3306
+ * @returns Array of matching templates.
3307
+ */
3308
+ getByArchetype(archetype) {
3309
+ const results = [];
3310
+ for (const template of this.templates.values()) {
3311
+ if (template.archetype === archetype) {
3312
+ results.push(template);
3313
+ }
3314
+ }
3315
+ return results;
3316
+ }
3317
+ /**
3318
+ * Get all templates matching a specific rig type.
3319
+ *
3320
+ * @param rigType - Rig type (e.g. "humanoid", "quadruped").
3321
+ * @returns Array of matching templates.
3322
+ */
3323
+ getByRigType(rigType) {
3324
+ const results = [];
3325
+ for (const template of this.templates.values()) {
3326
+ if (template.rigType === rigType) {
3327
+ results.push(template);
3328
+ }
3329
+ }
3330
+ return results;
3331
+ }
3332
+ /**
3333
+ * Search templates by name, description, or tags.
3334
+ *
3335
+ * @param query - Search query (case-insensitive substring match).
3336
+ * @returns Array of matching templates.
3337
+ */
3338
+ search(query) {
3339
+ const q = query.toLowerCase();
3340
+ const results = [];
3341
+ for (const template of this.templates.values()) {
3342
+ const matchesName = template.name.toLowerCase().includes(q);
3343
+ const matchesDescription = template.description.toLowerCase().includes(q);
3344
+ const matchesTags = template.tags.some((t) => t.toLowerCase().includes(q));
3345
+ if (matchesName || matchesDescription || matchesTags) {
3346
+ results.push(template);
3347
+ }
3348
+ }
3349
+ return results;
3350
+ }
3351
+ // -----------------------------------------------------------------------
3352
+ // Enumeration
3353
+ // -----------------------------------------------------------------------
3354
+ /**
3355
+ * Get all registered templates.
3356
+ */
3357
+ getAll() {
3358
+ return Array.from(this.templates.values());
3359
+ }
3360
+ /**
3361
+ * Get all available archetypes that have at least one template.
3362
+ */
3363
+ getAvailableArchetypes() {
3364
+ const archetypes = /* @__PURE__ */ new Set();
3365
+ for (const template of this.templates.values()) {
3366
+ archetypes.add(template.archetype);
3367
+ }
3368
+ return Array.from(archetypes);
3369
+ }
3370
+ /**
3371
+ * Get the total number of registered templates.
3372
+ */
3373
+ get size() {
3374
+ return this.templates.size;
3375
+ }
3376
+ // -----------------------------------------------------------------------
3377
+ // Registration
3378
+ // -----------------------------------------------------------------------
3379
+ /**
3380
+ * Register a custom character template.
3381
+ *
3382
+ * @param template - The template to register.
3383
+ */
3384
+ register(template) {
3385
+ this.templates.set(template.id, template);
3386
+ }
3387
+ /**
3388
+ * Remove a template from the registry.
3389
+ *
3390
+ * @param id - Template ID to remove.
3391
+ * @returns `true` if the template was found and removed.
3392
+ */
3393
+ unregister(id) {
3394
+ return this.templates.delete(id);
3395
+ }
3396
+ // -----------------------------------------------------------------------
3397
+ // Default Templates
3398
+ // -----------------------------------------------------------------------
3399
+ /**
3400
+ * Load the 12 default pre-rigged character templates.
3401
+ *
3402
+ * These cover common humanoid archetypes used in spatial computing,
3403
+ * VR, game development, and animation workflows.
3404
+ */
3405
+ loadDefaultTemplates() {
3406
+ const defaults = [
3407
+ // ---- Male Adult Archetypes ----
3408
+ {
3409
+ id: "xbot",
3410
+ name: "X Bot",
3411
+ archetype: "male-adult",
3412
+ description: "Mixamo default male humanoid. Clean topology, production-ready skeleton with 65 bones. Ideal for prototyping and general-purpose character animation.",
3413
+ mixamoCharacterId: "mixamo-xbot-v1",
3414
+ rigType: "humanoid",
3415
+ boneCount: 65,
3416
+ polygonCount: 12456,
3417
+ availableFormats: ["fbx", "glb"],
3418
+ defaultAnimations: ["idle", "walk", "run", "jump"],
3419
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/xbot.png",
3420
+ tags: ["male", "humanoid", "default", "robot", "prototype"],
3421
+ isDefault: true
3422
+ },
3423
+ {
3424
+ id: "brute",
3425
+ name: "Brute",
3426
+ archetype: "male-adult",
3427
+ description: "Muscular male character with heavy build. Suitable for warrior, fighter, and action archetypes. Enhanced upper body deformation.",
3428
+ mixamoCharacterId: "mixamo-brute-v1",
3429
+ rigType: "humanoid",
3430
+ boneCount: 65,
3431
+ polygonCount: 18234,
3432
+ availableFormats: ["fbx", "glb"],
3433
+ defaultAnimations: ["idle", "walk", "attack"],
3434
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/brute.png",
3435
+ tags: ["male", "humanoid", "muscular", "warrior", "heavy"],
3436
+ isDefault: true
3437
+ },
3438
+ // ---- Female Adult Archetypes ----
3439
+ {
3440
+ id: "ybot",
3441
+ name: "Y Bot",
3442
+ archetype: "female-adult",
3443
+ description: "Mixamo default female humanoid. Clean topology, production-ready skeleton with 65 bones. Ideal for prototyping and general-purpose character animation.",
3444
+ mixamoCharacterId: "mixamo-ybot-v1",
3445
+ rigType: "humanoid",
3446
+ boneCount: 65,
3447
+ polygonCount: 11892,
3448
+ availableFormats: ["fbx", "glb"],
3449
+ defaultAnimations: ["idle", "walk", "run", "dance"],
3450
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/ybot.png",
3451
+ tags: ["female", "humanoid", "default", "robot", "prototype"],
3452
+ isDefault: true
3453
+ },
3454
+ {
3455
+ id: "maria",
3456
+ name: "Maria",
3457
+ archetype: "female-adult",
3458
+ description: "Realistic female character with detailed facial blend shapes. Suitable for cinematic sequences and social VR.",
3459
+ mixamoCharacterId: "mixamo-maria-v1",
3460
+ rigType: "humanoid",
3461
+ boneCount: 67,
3462
+ polygonCount: 22340,
3463
+ availableFormats: ["fbx", "glb"],
3464
+ defaultAnimations: ["idle", "walk", "speak", "wave"],
3465
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/maria.png",
3466
+ tags: ["female", "humanoid", "realistic", "facial", "cinematic"],
3467
+ isDefault: true
3468
+ },
3469
+ // ---- Child Archetypes ----
3470
+ {
3471
+ id: "ty",
3472
+ name: "Ty",
3473
+ archetype: "male-child",
3474
+ description: "Young male character with child proportions. Adjusted bone hierarchy for accurate child movement and shorter stride length.",
3475
+ mixamoCharacterId: "mixamo-ty-v1",
3476
+ rigType: "humanoid",
3477
+ boneCount: 63,
3478
+ polygonCount: 9876,
3479
+ availableFormats: ["fbx", "glb"],
3480
+ defaultAnimations: ["idle", "walk", "run", "jump"],
3481
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/ty.png",
3482
+ tags: ["child", "male", "humanoid", "young", "small"],
3483
+ isDefault: true
3484
+ },
3485
+ {
3486
+ id: "lily",
3487
+ name: "Lily",
3488
+ archetype: "female-child",
3489
+ description: "Young female character with child proportions. Suitable for educational, family-friendly, and social VR applications.",
3490
+ mixamoCharacterId: "mixamo-lily-v1",
3491
+ rigType: "humanoid",
3492
+ boneCount: 63,
3493
+ polygonCount: 10234,
3494
+ availableFormats: ["fbx", "glb"],
3495
+ defaultAnimations: ["idle", "walk", "dance", "wave"],
3496
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/lily.png",
3497
+ tags: ["child", "female", "humanoid", "young", "small"],
3498
+ isDefault: true
3499
+ },
3500
+ // ---- Robot Archetypes ----
3501
+ {
3502
+ id: "android",
3503
+ name: "Android Unit",
3504
+ archetype: "robot-humanoid",
3505
+ description: "Humanoid robot with mechanical joint articulation. Rigid body segments connected by revolute joints. Suitable for sci-fi and robotics simulations.",
3506
+ mixamoCharacterId: "mixamo-android-v1",
3507
+ rigType: "humanoid",
3508
+ boneCount: 60,
3509
+ polygonCount: 15678,
3510
+ availableFormats: ["fbx", "glb"],
3511
+ defaultAnimations: ["idle", "walk", "run"],
3512
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/android.png",
3513
+ tags: ["robot", "humanoid", "mechanical", "sci-fi", "android"],
3514
+ isDefault: true
3515
+ },
3516
+ {
3517
+ id: "mech-worker",
3518
+ name: "Mech Worker",
3519
+ archetype: "robot-mechanical",
3520
+ description: "Industrial mechanical robot with hydraulic limbs. Heavy-duty construction archetype with simplified joint hierarchy for mechanical animation.",
3521
+ mixamoCharacterId: "mixamo-mechworker-v1",
3522
+ rigType: "humanoid",
3523
+ boneCount: 48,
3524
+ polygonCount: 20456,
3525
+ availableFormats: ["fbx", "glb"],
3526
+ defaultAnimations: ["idle", "walk", "attack"],
3527
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/mechworker.png",
3528
+ tags: ["robot", "mechanical", "industrial", "heavy", "hydraulic"],
3529
+ isDefault: true
3530
+ },
3531
+ // ---- Creature Archetypes ----
3532
+ {
3533
+ id: "drake",
3534
+ name: "Drake",
3535
+ archetype: "creature-bipedal",
3536
+ description: "Bipedal dragon-like creature with wing attachments. Modified humanoid rig with additional wing and tail bone chains.",
3537
+ mixamoCharacterId: "mixamo-drake-v1",
3538
+ rigType: "humanoid",
3539
+ boneCount: 78,
3540
+ polygonCount: 24890,
3541
+ availableFormats: ["fbx", "glb"],
3542
+ defaultAnimations: ["idle", "walk", "attack", "fly"],
3543
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/drake.png",
3544
+ tags: ["creature", "bipedal", "dragon", "fantasy", "wings", "tail"],
3545
+ isDefault: true
3546
+ },
3547
+ {
3548
+ id: "wolf",
3549
+ name: "Wolf",
3550
+ archetype: "creature-quadruped",
3551
+ description: "Quadruped wolf with full locomotion rig. Four-legged skeleton hierarchy with spine flexibility and tail chain.",
3552
+ mixamoCharacterId: "mixamo-wolf-v1",
3553
+ rigType: "quadruped",
3554
+ boneCount: 52,
3555
+ polygonCount: 14567,
3556
+ availableFormats: ["fbx", "glb"],
3557
+ defaultAnimations: ["idle", "walk", "run"],
3558
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/wolf.png",
3559
+ tags: ["creature", "quadruped", "wolf", "animal", "four-legged"],
3560
+ isDefault: true
3561
+ },
3562
+ // ---- Stylized Archetypes ----
3563
+ {
3564
+ id: "paladin",
3565
+ name: "Paladin",
3566
+ archetype: "armored-knight",
3567
+ description: "Armored knight with plate armor and cape physics. Heavy character suitable for medieval and fantasy combat scenarios.",
3568
+ mixamoCharacterId: "mixamo-paladin-v1",
3569
+ rigType: "humanoid",
3570
+ boneCount: 68,
3571
+ polygonCount: 28456,
3572
+ availableFormats: ["fbx", "glb"],
3573
+ defaultAnimations: ["idle", "walk", "attack", "crouch"],
3574
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/paladin.png",
3575
+ tags: ["humanoid", "armored", "knight", "medieval", "fantasy", "heavy"],
3576
+ isDefault: true
3577
+ },
3578
+ {
3579
+ id: "trooper",
3580
+ name: "Trooper",
3581
+ archetype: "sci-fi-soldier",
3582
+ description: "Sci-fi soldier with tactical gear and visor. Modern military archetype suitable for FPS, action, and tactical simulations.",
3583
+ mixamoCharacterId: "mixamo-trooper-v1",
3584
+ rigType: "humanoid",
3585
+ boneCount: 65,
3586
+ polygonCount: 19234,
3587
+ availableFormats: ["fbx", "glb"],
3588
+ defaultAnimations: ["idle", "walk", "run", "crouch", "attack"],
3589
+ thumbnailUrl: "https://www.mixamo.com/thumbnails/trooper.png",
3590
+ tags: ["humanoid", "soldier", "sci-fi", "military", "tactical", "fps"],
3591
+ isDefault: true
3592
+ }
3593
+ ];
3594
+ for (const template of defaults) {
3595
+ this.templates.set(template.id, template);
3596
+ }
3597
+ }
3598
+ };
3599
+
3600
+ Object.defineProperty(exports, "createDefaultMaterialDef", {
3601
+ enumerable: true,
3602
+ get: function () { return MaterialLibrary.createDefaultMaterialDef; }
3603
+ });
3604
+ Object.defineProperty(exports, "hexToRGBA", {
3605
+ enumerable: true,
3606
+ get: function () { return MaterialLibrary.hexToRGBA; }
3607
+ });
3608
+ Object.defineProperty(exports, "rgbaToHex", {
3609
+ enumerable: true,
3610
+ get: function () { return MaterialLibrary.rgbaToHex; }
3611
+ });
3612
+ exports.CharacterTemplateRegistry = CharacterTemplateRegistry;
3613
+ exports.ErrorFormatter = ErrorFormatter;
3614
+ exports.HoloScriptREPL = HoloScriptREPL;
3615
+ exports.HotReloadWatcher = HotReloadWatcher;
3616
+ exports.MaterialEditor = MaterialEditor;
3617
+ exports.MixamoAPI = MixamoAPI;
3618
+ exports.MixamoError = MixamoError;
3619
+ exports.MixamoFormatError = MixamoFormatError;
3620
+ exports.MixamoPresetMapper = MixamoPresetMapper;
3621
+ exports.MixamoRateLimitError = MixamoRateLimitError;
3622
+ exports.MixamoTopologyError = MixamoTopologyError;
3623
+ exports.SceneInspector = SceneInspector;
3624
+ exports.SourceMapGenerator = SourceMapGenerator;
3625
+ exports.VisualEditor = VisualEditor;
3626
+ exports.getMaterialEditorBuiltinPresets = getMaterialEditorBuiltinPresets;
3627
+ exports.listMaterialEditorQuickPickPresetNames = listMaterialEditorQuickPickPresetNames;
3628
+ exports.listMaterialEditorQuickPickPresetsByCategory = listMaterialEditorQuickPickPresetsByCategory;
3629
+ exports.startREPL = startREPL;
3630
+ //# sourceMappingURL=index.cjs.map
3631
+ //# sourceMappingURL=index.cjs.map