@chartts/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (575) hide show
  1. package/dist/area-type-B3CtuIIP.d.cts +9 -0
  2. package/dist/area-type-DmB6fTVf.d.ts +9 -0
  3. package/dist/area.cjs +34 -0
  4. package/dist/area.cjs.map +1 -0
  5. package/dist/area.d.cts +8 -0
  6. package/dist/area.d.ts +8 -0
  7. package/dist/area.js +13 -0
  8. package/dist/area.js.map +1 -0
  9. package/dist/bar-type-Bne_khsO.d.ts +5 -0
  10. package/dist/bar-type-CkI-kkrC.d.cts +5 -0
  11. package/dist/bar.cjs +34 -0
  12. package/dist/bar.cjs.map +1 -0
  13. package/dist/bar.d.cts +8 -0
  14. package/dist/bar.d.ts +8 -0
  15. package/dist/bar.js +13 -0
  16. package/dist/bar.js.map +1 -0
  17. package/dist/baseline-type-B1O4XHT3.d.ts +25 -0
  18. package/dist/baseline-type-BAPrZJ5W.d.cts +25 -0
  19. package/dist/baseline.cjs +32 -0
  20. package/dist/baseline.cjs.map +1 -0
  21. package/dist/baseline.d.cts +8 -0
  22. package/dist/baseline.d.ts +8 -0
  23. package/dist/baseline.js +11 -0
  24. package/dist/baseline.js.map +1 -0
  25. package/dist/boxplot-type-CEWf7p5X.d.ts +5 -0
  26. package/dist/boxplot-type-Da8_smoe.d.cts +5 -0
  27. package/dist/boxplot.cjs +33 -0
  28. package/dist/boxplot.cjs.map +1 -0
  29. package/dist/boxplot.d.cts +8 -0
  30. package/dist/boxplot.d.ts +8 -0
  31. package/dist/boxplot.js +12 -0
  32. package/dist/boxplot.js.map +1 -0
  33. package/dist/bubble-type-Dxb2JCKv.d.cts +9 -0
  34. package/dist/bubble-type-pru5MgB7.d.ts +9 -0
  35. package/dist/bubble.cjs +32 -0
  36. package/dist/bubble.cjs.map +1 -0
  37. package/dist/bubble.d.cts +8 -0
  38. package/dist/bubble.d.ts +8 -0
  39. package/dist/bubble.js +11 -0
  40. package/dist/bubble.js.map +1 -0
  41. package/dist/bullet-type-CPEYkEvt.d.ts +15 -0
  42. package/dist/bullet-type-khCmwFqH.d.cts +15 -0
  43. package/dist/bullet.cjs +33 -0
  44. package/dist/bullet.cjs.map +1 -0
  45. package/dist/bullet.d.cts +8 -0
  46. package/dist/bullet.d.ts +8 -0
  47. package/dist/bullet.js +12 -0
  48. package/dist/bullet.js.map +1 -0
  49. package/dist/calendar-type-Br5mpdea.d.cts +15 -0
  50. package/dist/calendar-type-yJWPY6qT.d.ts +15 -0
  51. package/dist/calendar.cjs +33 -0
  52. package/dist/calendar.cjs.map +1 -0
  53. package/dist/calendar.d.cts +8 -0
  54. package/dist/calendar.d.ts +8 -0
  55. package/dist/calendar.js +12 -0
  56. package/dist/calendar.js.map +1 -0
  57. package/dist/candlestick-type-CazXUb77.d.cts +22 -0
  58. package/dist/candlestick-type-CtLZy_uC.d.ts +22 -0
  59. package/dist/candlestick.cjs +33 -0
  60. package/dist/candlestick.cjs.map +1 -0
  61. package/dist/candlestick.d.cts +8 -0
  62. package/dist/candlestick.d.ts +8 -0
  63. package/dist/candlestick.js +12 -0
  64. package/dist/candlestick.js.map +1 -0
  65. package/dist/chord-type-ChIZaOZd.d.cts +5 -0
  66. package/dist/chord-type-ruvHboZl.d.ts +5 -0
  67. package/dist/chord.cjs +33 -0
  68. package/dist/chord.cjs.map +1 -0
  69. package/dist/chord.d.cts +8 -0
  70. package/dist/chord.d.ts +8 -0
  71. package/dist/chord.js +12 -0
  72. package/dist/chord.js.map +1 -0
  73. package/dist/chunk-22LR6CFM.js +176 -0
  74. package/dist/chunk-22LR6CFM.js.map +1 -0
  75. package/dist/chunk-2TWYS3BT.cjs +199 -0
  76. package/dist/chunk-2TWYS3BT.cjs.map +1 -0
  77. package/dist/chunk-3BRQGYDX.js +4859 -0
  78. package/dist/chunk-3BRQGYDX.js.map +1 -0
  79. package/dist/chunk-3TTTELGB.cjs +146 -0
  80. package/dist/chunk-3TTTELGB.cjs.map +1 -0
  81. package/dist/chunk-3XVGEBR4.js +17 -0
  82. package/dist/chunk-3XVGEBR4.js.map +1 -0
  83. package/dist/chunk-4HK4RM3X.cjs +128 -0
  84. package/dist/chunk-4HK4RM3X.cjs.map +1 -0
  85. package/dist/chunk-4VG47RLS.js +130 -0
  86. package/dist/chunk-4VG47RLS.js.map +1 -0
  87. package/dist/chunk-4YJPBUX2.cjs +68 -0
  88. package/dist/chunk-4YJPBUX2.cjs.map +1 -0
  89. package/dist/chunk-5QOQR5FE.cjs +19 -0
  90. package/dist/chunk-5QOQR5FE.cjs.map +1 -0
  91. package/dist/chunk-66HSJRRH.js +112 -0
  92. package/dist/chunk-66HSJRRH.js.map +1 -0
  93. package/dist/chunk-6KNBJ4CE.js +144 -0
  94. package/dist/chunk-6KNBJ4CE.js.map +1 -0
  95. package/dist/chunk-6LKN3HRX.js +79 -0
  96. package/dist/chunk-6LKN3HRX.js.map +1 -0
  97. package/dist/chunk-6N66T7VQ.js +103 -0
  98. package/dist/chunk-6N66T7VQ.js.map +1 -0
  99. package/dist/chunk-6YKFTEX4.js +336 -0
  100. package/dist/chunk-6YKFTEX4.js.map +1 -0
  101. package/dist/chunk-7CGXK7KT.js +124 -0
  102. package/dist/chunk-7CGXK7KT.js.map +1 -0
  103. package/dist/chunk-7HDAYWK3.cjs +358 -0
  104. package/dist/chunk-7HDAYWK3.cjs.map +1 -0
  105. package/dist/chunk-7SEHTEMR.js +91 -0
  106. package/dist/chunk-7SEHTEMR.js.map +1 -0
  107. package/dist/chunk-7YV56WPE.js +157 -0
  108. package/dist/chunk-7YV56WPE.js.map +1 -0
  109. package/dist/chunk-B54L2CPW.cjs +165 -0
  110. package/dist/chunk-B54L2CPW.cjs.map +1 -0
  111. package/dist/chunk-BC6KW2OG.js +197 -0
  112. package/dist/chunk-BC6KW2OG.js.map +1 -0
  113. package/dist/chunk-BEYG2HQ4.cjs +178 -0
  114. package/dist/chunk-BEYG2HQ4.cjs.map +1 -0
  115. package/dist/chunk-BR3KTAGW.js +113 -0
  116. package/dist/chunk-BR3KTAGW.js.map +1 -0
  117. package/dist/chunk-BRSHEDTZ.js +30 -0
  118. package/dist/chunk-BRSHEDTZ.js.map +1 -0
  119. package/dist/chunk-BXX2EPFE.cjs +160 -0
  120. package/dist/chunk-BXX2EPFE.cjs.map +1 -0
  121. package/dist/chunk-BYXRFTEI.cjs +114 -0
  122. package/dist/chunk-BYXRFTEI.cjs.map +1 -0
  123. package/dist/chunk-CMEXNS5V.js +158 -0
  124. package/dist/chunk-CMEXNS5V.js.map +1 -0
  125. package/dist/chunk-D2SV6YT3.js +47 -0
  126. package/dist/chunk-D2SV6YT3.js.map +1 -0
  127. package/dist/chunk-DL7P72FA.cjs +273 -0
  128. package/dist/chunk-DL7P72FA.cjs.map +1 -0
  129. package/dist/chunk-DNQ6N3VG.js +208 -0
  130. package/dist/chunk-DNQ6N3VG.js.map +1 -0
  131. package/dist/chunk-E2S7RAT2.js +126 -0
  132. package/dist/chunk-E2S7RAT2.js.map +1 -0
  133. package/dist/chunk-E6PR75X7.js +15 -0
  134. package/dist/chunk-E6PR75X7.js.map +1 -0
  135. package/dist/chunk-EGQ4JCFU.cjs +109 -0
  136. package/dist/chunk-EGQ4JCFU.cjs.map +1 -0
  137. package/dist/chunk-ETPZGEL7.cjs +105 -0
  138. package/dist/chunk-ETPZGEL7.cjs.map +1 -0
  139. package/dist/chunk-FFJ6BVEY.cjs +132 -0
  140. package/dist/chunk-FFJ6BVEY.cjs.map +1 -0
  141. package/dist/chunk-FKYNRMPX.js +133 -0
  142. package/dist/chunk-FKYNRMPX.js.map +1 -0
  143. package/dist/chunk-FRWC6GUR.cjs +126 -0
  144. package/dist/chunk-FRWC6GUR.cjs.map +1 -0
  145. package/dist/chunk-FUZFNUCJ.js +104 -0
  146. package/dist/chunk-FUZFNUCJ.js.map +1 -0
  147. package/dist/chunk-GM3ZO5WV.js +13 -0
  148. package/dist/chunk-GM3ZO5WV.js.map +1 -0
  149. package/dist/chunk-GNRRK7TZ.cjs +149 -0
  150. package/dist/chunk-GNRRK7TZ.cjs.map +1 -0
  151. package/dist/chunk-H2AE7JMU.js +128 -0
  152. package/dist/chunk-H2AE7JMU.js.map +1 -0
  153. package/dist/chunk-H4RNJ7FK.cjs +115 -0
  154. package/dist/chunk-H4RNJ7FK.cjs.map +1 -0
  155. package/dist/chunk-HB4EGHJ7.cjs +153 -0
  156. package/dist/chunk-HB4EGHJ7.cjs.map +1 -0
  157. package/dist/chunk-HDWWUEPD.js +122 -0
  158. package/dist/chunk-HDWWUEPD.js.map +1 -0
  159. package/dist/chunk-HOOOCSU4.js +216 -0
  160. package/dist/chunk-HOOOCSU4.js.map +1 -0
  161. package/dist/chunk-J6NPRKT4.cjs +49 -0
  162. package/dist/chunk-J6NPRKT4.cjs.map +1 -0
  163. package/dist/chunk-JO6U4YR4.js +355 -0
  164. package/dist/chunk-JO6U4YR4.js.map +1 -0
  165. package/dist/chunk-KE7ILTM4.cjs +162 -0
  166. package/dist/chunk-KE7ILTM4.cjs.map +1 -0
  167. package/dist/chunk-KSRROKYC.cjs +135 -0
  168. package/dist/chunk-KSRROKYC.cjs.map +1 -0
  169. package/dist/chunk-KVI6DN3R.js +147 -0
  170. package/dist/chunk-KVI6DN3R.js.map +1 -0
  171. package/dist/chunk-L54BZYAI.cjs +111 -0
  172. package/dist/chunk-L54BZYAI.cjs.map +1 -0
  173. package/dist/chunk-LB4MV5CG.js +109 -0
  174. package/dist/chunk-LB4MV5CG.js.map +1 -0
  175. package/dist/chunk-LEJGLR2P.cjs +106 -0
  176. package/dist/chunk-LEJGLR2P.cjs.map +1 -0
  177. package/dist/chunk-LU35QDPN.cjs +30 -0
  178. package/dist/chunk-LU35QDPN.cjs.map +1 -0
  179. package/dist/chunk-LUTU4WW3.js +151 -0
  180. package/dist/chunk-LUTU4WW3.js.map +1 -0
  181. package/dist/chunk-M4AQD6WX.cjs +220 -0
  182. package/dist/chunk-M4AQD6WX.cjs.map +1 -0
  183. package/dist/chunk-M4UST3UU.cjs +159 -0
  184. package/dist/chunk-M4UST3UU.cjs.map +1 -0
  185. package/dist/chunk-MLD5ZIDO.cjs +359 -0
  186. package/dist/chunk-MLD5ZIDO.cjs.map +1 -0
  187. package/dist/chunk-MLX3EO2C.js +107 -0
  188. package/dist/chunk-MLX3EO2C.js.map +1 -0
  189. package/dist/chunk-MVWERW4G.cjs +28 -0
  190. package/dist/chunk-MVWERW4G.cjs.map +1 -0
  191. package/dist/chunk-MYPCA25Y.cjs +115 -0
  192. package/dist/chunk-MYPCA25Y.cjs.map +1 -0
  193. package/dist/chunk-N6IVODRJ.js +114 -0
  194. package/dist/chunk-N6IVODRJ.js.map +1 -0
  195. package/dist/chunk-NFD6VAVM.js +155 -0
  196. package/dist/chunk-NFD6VAVM.js.map +1 -0
  197. package/dist/chunk-NHGKZMD7.cjs +18 -0
  198. package/dist/chunk-NHGKZMD7.cjs.map +1 -0
  199. package/dist/chunk-NMLDPXOL.js +167 -0
  200. package/dist/chunk-NMLDPXOL.js.map +1 -0
  201. package/dist/chunk-NONU7KUN.cjs +32 -0
  202. package/dist/chunk-NONU7KUN.cjs.map +1 -0
  203. package/dist/chunk-NQSC7CKN.js +130 -0
  204. package/dist/chunk-NQSC7CKN.js.map +1 -0
  205. package/dist/chunk-NYVFC2SF.js +87 -0
  206. package/dist/chunk-NYVFC2SF.js.map +1 -0
  207. package/dist/chunk-O5KGKQSU.js +218 -0
  208. package/dist/chunk-O5KGKQSU.js.map +1 -0
  209. package/dist/chunk-OGSZO22B.js +26 -0
  210. package/dist/chunk-OGSZO22B.js.map +1 -0
  211. package/dist/chunk-Q2BDB5ZA.cjs +218 -0
  212. package/dist/chunk-Q2BDB5ZA.cjs.map +1 -0
  213. package/dist/chunk-Q6U5D2ZB.cjs +210 -0
  214. package/dist/chunk-Q6U5D2ZB.cjs.map +1 -0
  215. package/dist/chunk-QAYX6XN3.js +26 -0
  216. package/dist/chunk-QAYX6XN3.js.map +1 -0
  217. package/dist/chunk-QHJHYV3H.cjs +15 -0
  218. package/dist/chunk-QHJHYV3H.cjs.map +1 -0
  219. package/dist/chunk-QIJZZOX7.js +249 -0
  220. package/dist/chunk-QIJZZOX7.js.map +1 -0
  221. package/dist/chunk-QLRK46YY.cjs +93 -0
  222. package/dist/chunk-QLRK46YY.cjs.map +1 -0
  223. package/dist/chunk-QZ4ZYTP6.js +124 -0
  224. package/dist/chunk-QZ4ZYTP6.js.map +1 -0
  225. package/dist/chunk-RCVRZCCC.cjs +156 -0
  226. package/dist/chunk-RCVRZCCC.cjs.map +1 -0
  227. package/dist/chunk-ROYZ7W4M.cjs +81 -0
  228. package/dist/chunk-ROYZ7W4M.cjs.map +1 -0
  229. package/dist/chunk-SJS4ISBA.js +163 -0
  230. package/dist/chunk-SJS4ISBA.js.map +1 -0
  231. package/dist/chunk-SKVCPG5R.cjs +124 -0
  232. package/dist/chunk-SKVCPG5R.cjs.map +1 -0
  233. package/dist/chunk-SQJAKZ4W.js +113 -0
  234. package/dist/chunk-SQJAKZ4W.js.map +1 -0
  235. package/dist/chunk-SUNC3CPV.js +79 -0
  236. package/dist/chunk-SUNC3CPV.js.map +1 -0
  237. package/dist/chunk-SWYCVF46.js +145 -0
  238. package/dist/chunk-SWYCVF46.js.map +1 -0
  239. package/dist/chunk-SZVK6RRQ.js +98 -0
  240. package/dist/chunk-SZVK6RRQ.js.map +1 -0
  241. package/dist/chunk-TOS2RYHQ.js +154 -0
  242. package/dist/chunk-TOS2RYHQ.js.map +1 -0
  243. package/dist/chunk-TTOX27BZ.cjs +116 -0
  244. package/dist/chunk-TTOX27BZ.cjs.map +1 -0
  245. package/dist/chunk-TW6KPQIS.js +160 -0
  246. package/dist/chunk-TW6KPQIS.js.map +1 -0
  247. package/dist/chunk-UN7K3YQG.cjs +81 -0
  248. package/dist/chunk-UN7K3YQG.cjs.map +1 -0
  249. package/dist/chunk-UQBDGCS6.cjs +89 -0
  250. package/dist/chunk-UQBDGCS6.cjs.map +1 -0
  251. package/dist/chunk-VPI7UGB3.cjs +130 -0
  252. package/dist/chunk-VPI7UGB3.cjs.map +1 -0
  253. package/dist/chunk-X2JNB5UN.js +271 -0
  254. package/dist/chunk-X2JNB5UN.js.map +1 -0
  255. package/dist/chunk-XBIM73HV.cjs +126 -0
  256. package/dist/chunk-XBIM73HV.cjs.map +1 -0
  257. package/dist/chunk-XHQWWR5M.cjs +4892 -0
  258. package/dist/chunk-XHQWWR5M.cjs.map +1 -0
  259. package/dist/chunk-Y2JZHX3P.cjs +169 -0
  260. package/dist/chunk-Y2JZHX3P.cjs.map +1 -0
  261. package/dist/chunk-Y4P4VY7A.cjs +251 -0
  262. package/dist/chunk-Y4P4VY7A.cjs.map +1 -0
  263. package/dist/chunk-Y54XVNXB.cjs +157 -0
  264. package/dist/chunk-Y54XVNXB.cjs.map +1 -0
  265. package/dist/chunk-Z6E3XVP2.cjs +132 -0
  266. package/dist/chunk-Z6E3XVP2.cjs.map +1 -0
  267. package/dist/chunk-ZDYCIP6N.cjs +100 -0
  268. package/dist/chunk-ZDYCIP6N.cjs.map +1 -0
  269. package/dist/chunk-ZDZQG3GH.cjs +148 -0
  270. package/dist/chunk-ZDZQG3GH.cjs.map +1 -0
  271. package/dist/chunk-ZL25X5WW.js +66 -0
  272. package/dist/chunk-ZL25X5WW.js.map +1 -0
  273. package/dist/combo-type-BdSohVyy.d.ts +16 -0
  274. package/dist/combo-type-wodyLq1f.d.cts +16 -0
  275. package/dist/combo.cjs +34 -0
  276. package/dist/combo.cjs.map +1 -0
  277. package/dist/combo.d.cts +8 -0
  278. package/dist/combo.d.ts +8 -0
  279. package/dist/combo.js +13 -0
  280. package/dist/combo.js.map +1 -0
  281. package/dist/custom-type-B6w1n5Ua.d.cts +23 -0
  282. package/dist/custom-type-XFjuPwCr.d.ts +23 -0
  283. package/dist/custom.cjs +33 -0
  284. package/dist/custom.cjs.map +1 -0
  285. package/dist/custom.d.cts +8 -0
  286. package/dist/custom.d.ts +8 -0
  287. package/dist/custom.js +12 -0
  288. package/dist/custom.js.map +1 -0
  289. package/dist/donut.cjs +33 -0
  290. package/dist/donut.cjs.map +1 -0
  291. package/dist/donut.d.cts +8 -0
  292. package/dist/donut.d.ts +8 -0
  293. package/dist/donut.js +12 -0
  294. package/dist/donut.js.map +1 -0
  295. package/dist/dumbbell-type-DPSE0OTg.d.ts +12 -0
  296. package/dist/dumbbell-type-w424KzI2.d.cts +12 -0
  297. package/dist/dumbbell.cjs +34 -0
  298. package/dist/dumbbell.cjs.map +1 -0
  299. package/dist/dumbbell.d.cts +8 -0
  300. package/dist/dumbbell.d.ts +8 -0
  301. package/dist/dumbbell.js +13 -0
  302. package/dist/dumbbell.js.map +1 -0
  303. package/dist/engine-CNukbv7k.d.cts +426 -0
  304. package/dist/engine-CNukbv7k.d.ts +426 -0
  305. package/dist/factory-dbngWV4d.d.cts +8 -0
  306. package/dist/factory-jRzNNxwj.d.ts +8 -0
  307. package/dist/finance.cjs +92 -0
  308. package/dist/finance.cjs.map +1 -0
  309. package/dist/finance.d.cts +192 -0
  310. package/dist/finance.d.ts +192 -0
  311. package/dist/finance.js +3 -0
  312. package/dist/finance.js.map +1 -0
  313. package/dist/funnel-type-BeVl3ohX.d.cts +9 -0
  314. package/dist/funnel-type-CMXGQq4T.d.ts +9 -0
  315. package/dist/funnel.cjs +33 -0
  316. package/dist/funnel.cjs.map +1 -0
  317. package/dist/funnel.d.cts +8 -0
  318. package/dist/funnel.d.ts +8 -0
  319. package/dist/funnel.js +12 -0
  320. package/dist/funnel.js.map +1 -0
  321. package/dist/gauge-type-BpDBwCeD.d.ts +11 -0
  322. package/dist/gauge-type-DmjDdfsY.d.cts +11 -0
  323. package/dist/gauge.cjs +33 -0
  324. package/dist/gauge.cjs.map +1 -0
  325. package/dist/gauge.d.cts +8 -0
  326. package/dist/gauge.d.ts +8 -0
  327. package/dist/gauge.js +12 -0
  328. package/dist/gauge.js.map +1 -0
  329. package/dist/geo.cjs +41 -0
  330. package/dist/geo.cjs.map +1 -0
  331. package/dist/geo.d.cts +8 -0
  332. package/dist/geo.d.ts +8 -0
  333. package/dist/geo.js +12 -0
  334. package/dist/geo.js.map +1 -0
  335. package/dist/graph-type-DkdAB6Vc.d.cts +5 -0
  336. package/dist/graph-type-ul9xwdf5.d.ts +5 -0
  337. package/dist/graph.cjs +33 -0
  338. package/dist/graph.cjs.map +1 -0
  339. package/dist/graph.d.cts +8 -0
  340. package/dist/graph.d.ts +8 -0
  341. package/dist/graph.js +12 -0
  342. package/dist/graph.js.map +1 -0
  343. package/dist/heatmap-type-D0shkxK7.d.cts +12 -0
  344. package/dist/heatmap-type-q6nNhVTr.d.ts +12 -0
  345. package/dist/heatmap.cjs +33 -0
  346. package/dist/heatmap.cjs.map +1 -0
  347. package/dist/heatmap.d.cts +8 -0
  348. package/dist/heatmap.d.ts +8 -0
  349. package/dist/heatmap.js +12 -0
  350. package/dist/heatmap.js.map +1 -0
  351. package/dist/histogram-type-BWil-Rb7.d.cts +13 -0
  352. package/dist/histogram-type-CF593WSp.d.ts +13 -0
  353. package/dist/histogram.cjs +33 -0
  354. package/dist/histogram.cjs.map +1 -0
  355. package/dist/histogram.d.cts +8 -0
  356. package/dist/histogram.d.ts +8 -0
  357. package/dist/histogram.js +12 -0
  358. package/dist/histogram.js.map +1 -0
  359. package/dist/horizontal-bar-type-0mqnyMUR.d.cts +11 -0
  360. package/dist/horizontal-bar-type-DAlrxY0h.d.ts +11 -0
  361. package/dist/horizontal-bar.cjs +34 -0
  362. package/dist/horizontal-bar.cjs.map +1 -0
  363. package/dist/horizontal-bar.d.cts +8 -0
  364. package/dist/horizontal-bar.d.ts +8 -0
  365. package/dist/horizontal-bar.js +13 -0
  366. package/dist/horizontal-bar.js.map +1 -0
  367. package/dist/index.cjs +1604 -0
  368. package/dist/index.cjs.map +1 -0
  369. package/dist/index.d.cts +594 -0
  370. package/dist/index.d.ts +594 -0
  371. package/dist/index.js +1201 -0
  372. package/dist/index.js.map +1 -0
  373. package/dist/kagi-type-Y5XUVm3h.d.ts +26 -0
  374. package/dist/kagi-type-aG-Q1bWk.d.cts +26 -0
  375. package/dist/kagi.cjs +33 -0
  376. package/dist/kagi.cjs.map +1 -0
  377. package/dist/kagi.d.cts +8 -0
  378. package/dist/kagi.d.ts +8 -0
  379. package/dist/kagi.js +12 -0
  380. package/dist/kagi.js.map +1 -0
  381. package/dist/line-type-B8lgTFjb.d.cts +5 -0
  382. package/dist/line-type-Diw0nXj7.d.ts +5 -0
  383. package/dist/line.cjs +33 -0
  384. package/dist/line.cjs.map +1 -0
  385. package/dist/line.d.cts +8 -0
  386. package/dist/line.d.ts +8 -0
  387. package/dist/line.js +12 -0
  388. package/dist/line.js.map +1 -0
  389. package/dist/lines-type-CGrg_mDi.d.cts +31 -0
  390. package/dist/lines-type-V-seVNZ4.d.ts +31 -0
  391. package/dist/lines.cjs +33 -0
  392. package/dist/lines.cjs.map +1 -0
  393. package/dist/lines.d.cts +8 -0
  394. package/dist/lines.d.ts +8 -0
  395. package/dist/lines.js +12 -0
  396. package/dist/lines.js.map +1 -0
  397. package/dist/lollipop-type-CoTzKLSu.d.cts +11 -0
  398. package/dist/lollipop-type-Ygc4rRql.d.ts +11 -0
  399. package/dist/lollipop.cjs +33 -0
  400. package/dist/lollipop.cjs.map +1 -0
  401. package/dist/lollipop.d.cts +8 -0
  402. package/dist/lollipop.d.ts +8 -0
  403. package/dist/lollipop.js +12 -0
  404. package/dist/lollipop.js.map +1 -0
  405. package/dist/matrix-type-Crb_NwUJ.d.cts +26 -0
  406. package/dist/matrix-type-Dag1VLCY.d.ts +26 -0
  407. package/dist/matrix.cjs +33 -0
  408. package/dist/matrix.cjs.map +1 -0
  409. package/dist/matrix.d.cts +8 -0
  410. package/dist/matrix.d.ts +8 -0
  411. package/dist/matrix.js +12 -0
  412. package/dist/matrix.js.map +1 -0
  413. package/dist/ohlc-type-BQBHvVJO.d.cts +28 -0
  414. package/dist/ohlc-type-CGR-vRGL.d.ts +28 -0
  415. package/dist/ohlc.cjs +33 -0
  416. package/dist/ohlc.cjs.map +1 -0
  417. package/dist/ohlc.d.cts +8 -0
  418. package/dist/ohlc.d.ts +8 -0
  419. package/dist/ohlc.js +12 -0
  420. package/dist/ohlc.js.map +1 -0
  421. package/dist/parallel-type-6t6xyHMg.d.ts +14 -0
  422. package/dist/parallel-type-D-S-EqeC.d.cts +14 -0
  423. package/dist/parallel.cjs +33 -0
  424. package/dist/parallel.cjs.map +1 -0
  425. package/dist/parallel.d.cts +8 -0
  426. package/dist/parallel.d.ts +8 -0
  427. package/dist/parallel.js +12 -0
  428. package/dist/parallel.js.map +1 -0
  429. package/dist/pictorialbar-type-A-8mTX1N.d.ts +5 -0
  430. package/dist/pictorialbar-type-CnJEH9GZ.d.cts +5 -0
  431. package/dist/pictorialbar.cjs +33 -0
  432. package/dist/pictorialbar.cjs.map +1 -0
  433. package/dist/pictorialbar.d.cts +8 -0
  434. package/dist/pictorialbar.d.ts +8 -0
  435. package/dist/pictorialbar.js +12 -0
  436. package/dist/pictorialbar.js.map +1 -0
  437. package/dist/pie-type-S7kUKGRS.d.ts +7 -0
  438. package/dist/pie-type-duX5xkfQ.d.cts +7 -0
  439. package/dist/pie.cjs +33 -0
  440. package/dist/pie.cjs.map +1 -0
  441. package/dist/pie.d.cts +8 -0
  442. package/dist/pie.d.ts +8 -0
  443. package/dist/pie.js +12 -0
  444. package/dist/pie.js.map +1 -0
  445. package/dist/polar-type-CI6-I8Yg.d.cts +11 -0
  446. package/dist/polar-type-Dj21GIc9.d.ts +11 -0
  447. package/dist/polar.cjs +33 -0
  448. package/dist/polar.cjs.map +1 -0
  449. package/dist/polar.d.cts +8 -0
  450. package/dist/polar.d.ts +8 -0
  451. package/dist/polar.js +12 -0
  452. package/dist/polar.js.map +1 -0
  453. package/dist/radar-type-CYjZHuKm.d.ts +9 -0
  454. package/dist/radar-type-Dgyr13Zd.d.cts +9 -0
  455. package/dist/radar.cjs +33 -0
  456. package/dist/radar.cjs.map +1 -0
  457. package/dist/radar.d.cts +8 -0
  458. package/dist/radar.d.ts +8 -0
  459. package/dist/radar.js +12 -0
  460. package/dist/radar.js.map +1 -0
  461. package/dist/radialbar-type-CLZ8XgST.d.ts +11 -0
  462. package/dist/radialbar-type-C_SBXoaa.d.cts +11 -0
  463. package/dist/radialbar.cjs +33 -0
  464. package/dist/radialbar.cjs.map +1 -0
  465. package/dist/radialbar.d.cts +8 -0
  466. package/dist/radialbar.d.ts +8 -0
  467. package/dist/radialbar.js +12 -0
  468. package/dist/radialbar.js.map +1 -0
  469. package/dist/range-type-CkriEnLm.d.cts +26 -0
  470. package/dist/range-type-_LbMV4tl.d.ts +26 -0
  471. package/dist/range.cjs +32 -0
  472. package/dist/range.cjs.map +1 -0
  473. package/dist/range.d.cts +8 -0
  474. package/dist/range.d.ts +8 -0
  475. package/dist/range.js +11 -0
  476. package/dist/range.js.map +1 -0
  477. package/dist/renko-type-43OXCtZ3.d.cts +24 -0
  478. package/dist/renko-type-ejoXt2ro.d.ts +24 -0
  479. package/dist/renko.cjs +33 -0
  480. package/dist/renko.cjs.map +1 -0
  481. package/dist/renko.d.cts +8 -0
  482. package/dist/renko.d.ts +8 -0
  483. package/dist/renko.js +12 -0
  484. package/dist/renko.js.map +1 -0
  485. package/dist/sankey-type-CN7PL5MQ.d.ts +5 -0
  486. package/dist/sankey-type-DfkTUnXB.d.cts +5 -0
  487. package/dist/sankey.cjs +33 -0
  488. package/dist/sankey.cjs.map +1 -0
  489. package/dist/sankey.d.cts +8 -0
  490. package/dist/sankey.d.ts +8 -0
  491. package/dist/sankey.js +12 -0
  492. package/dist/sankey.js.map +1 -0
  493. package/dist/scatter-type-CD7X1pZb.d.cts +5 -0
  494. package/dist/scatter-type-D3HdPkZ6.d.ts +5 -0
  495. package/dist/scatter.cjs +32 -0
  496. package/dist/scatter.cjs.map +1 -0
  497. package/dist/scatter.d.cts +8 -0
  498. package/dist/scatter.d.ts +8 -0
  499. package/dist/scatter.js +11 -0
  500. package/dist/scatter.js.map +1 -0
  501. package/dist/sparkline-type-COW3izgF.d.ts +9 -0
  502. package/dist/sparkline-type-qt3yoLs7.d.cts +9 -0
  503. package/dist/sparkline.cjs +33 -0
  504. package/dist/sparkline.cjs.map +1 -0
  505. package/dist/sparkline.d.cts +8 -0
  506. package/dist/sparkline.d.ts +8 -0
  507. package/dist/sparkline.js +12 -0
  508. package/dist/sparkline.js.map +1 -0
  509. package/dist/stacked-bar-type-C-5pmb-D.d.ts +8 -0
  510. package/dist/stacked-bar-type-Ct6HK2A0.d.cts +8 -0
  511. package/dist/stacked-bar.cjs +34 -0
  512. package/dist/stacked-bar.cjs.map +1 -0
  513. package/dist/stacked-bar.d.cts +8 -0
  514. package/dist/stacked-bar.d.ts +8 -0
  515. package/dist/stacked-bar.js +13 -0
  516. package/dist/stacked-bar.js.map +1 -0
  517. package/dist/step-type-BSL-CUny.d.cts +11 -0
  518. package/dist/step-type-Cleoshov.d.ts +11 -0
  519. package/dist/step.cjs +34 -0
  520. package/dist/step.cjs.map +1 -0
  521. package/dist/step.d.cts +8 -0
  522. package/dist/step.d.ts +8 -0
  523. package/dist/step.js +13 -0
  524. package/dist/step.js.map +1 -0
  525. package/dist/sunburst-type-BOztb_Px.d.cts +5 -0
  526. package/dist/sunburst-type-ZFw2w6m8.d.ts +5 -0
  527. package/dist/sunburst.cjs +33 -0
  528. package/dist/sunburst.cjs.map +1 -0
  529. package/dist/sunburst.d.cts +8 -0
  530. package/dist/sunburst.d.ts +8 -0
  531. package/dist/sunburst.js +12 -0
  532. package/dist/sunburst.js.map +1 -0
  533. package/dist/themeriver-type-B5PYChcC.d.ts +15 -0
  534. package/dist/themeriver-type-BnRSsyal.d.cts +15 -0
  535. package/dist/themeriver.cjs +33 -0
  536. package/dist/themeriver.cjs.map +1 -0
  537. package/dist/themeriver.d.cts +8 -0
  538. package/dist/themeriver.d.ts +8 -0
  539. package/dist/themeriver.js +12 -0
  540. package/dist/themeriver.js.map +1 -0
  541. package/dist/tree-type-B-bOSg0l.d.ts +5 -0
  542. package/dist/tree-type-Cz84wHnR.d.cts +5 -0
  543. package/dist/tree.cjs +33 -0
  544. package/dist/tree.cjs.map +1 -0
  545. package/dist/tree.d.cts +8 -0
  546. package/dist/tree.d.ts +8 -0
  547. package/dist/tree.js +12 -0
  548. package/dist/tree.js.map +1 -0
  549. package/dist/treemap-type-DYDj-rWv.d.ts +11 -0
  550. package/dist/treemap-type-OBCGexiK.d.cts +11 -0
  551. package/dist/treemap.cjs +33 -0
  552. package/dist/treemap.cjs.map +1 -0
  553. package/dist/treemap.d.cts +8 -0
  554. package/dist/treemap.d.ts +8 -0
  555. package/dist/treemap.js +12 -0
  556. package/dist/treemap.js.map +1 -0
  557. package/dist/volume-type-C9wRmKR6.d.ts +21 -0
  558. package/dist/volume-type-CfERBN5g.d.cts +21 -0
  559. package/dist/volume.cjs +33 -0
  560. package/dist/volume.cjs.map +1 -0
  561. package/dist/volume.d.cts +8 -0
  562. package/dist/volume.d.ts +8 -0
  563. package/dist/volume.js +12 -0
  564. package/dist/volume.js.map +1 -0
  565. package/dist/waterfall-type-CpdVtBi_.d.ts +10 -0
  566. package/dist/waterfall-type-hm5ylGgW.d.cts +10 -0
  567. package/dist/waterfall.cjs +33 -0
  568. package/dist/waterfall.cjs.map +1 -0
  569. package/dist/waterfall.d.cts +8 -0
  570. package/dist/waterfall.d.ts +8 -0
  571. package/dist/waterfall.js +12 -0
  572. package/dist/waterfall.js.map +1 -0
  573. package/dist/world-regions-Cfvxpbx0.d.ts +50 -0
  574. package/dist/world-regions-cMulhqDW.d.cts +50 -0
  575. package/package.json +262 -0
@@ -0,0 +1,4892 @@
1
+ 'use strict';
2
+
3
+ // src/constants.ts
4
+ var CSS_PREFIX = "--chartts";
5
+ var PALETTE = [
6
+ "var(--color-blue-500, #3b82f6)",
7
+ "var(--color-red-500, #ef4444)",
8
+ "var(--color-emerald-500, #10b981)",
9
+ "var(--color-amber-500, #f59e0b)",
10
+ "var(--color-violet-500, #8b5cf6)",
11
+ "var(--color-pink-500, #ec4899)",
12
+ "var(--color-cyan-500, #06b6d4)",
13
+ "var(--color-lime-500, #84cc16)",
14
+ "var(--color-orange-500, #f97316)",
15
+ "var(--color-indigo-500, #6366f1)"
16
+ ];
17
+ var FONT_FAMILY = 'var(--font-sans, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif)';
18
+ var LIGHT_THEME = {
19
+ colors: [...PALETTE],
20
+ background: "transparent",
21
+ textColor: "var(--color-gray-800, #1f2937)",
22
+ textMuted: "var(--color-gray-500, #6b7280)",
23
+ axisColor: "var(--color-gray-300, #d1d5db)",
24
+ gridColor: "var(--color-gray-200, #e5e7eb)",
25
+ tooltipBackground: "var(--color-white, #ffffff)",
26
+ tooltipText: "var(--color-gray-800, #1f2937)",
27
+ tooltipBorder: "var(--color-gray-200, #e5e7eb)",
28
+ fontFamily: FONT_FAMILY,
29
+ fontSize: 12,
30
+ fontSizeSmall: 10,
31
+ fontSizeLarge: 14,
32
+ borderRadius: 8,
33
+ gridStyle: "dashed",
34
+ gridWidth: 1,
35
+ axisWidth: 1,
36
+ pointRadius: 4,
37
+ lineWidth: 2.5
38
+ };
39
+ var DARK_THEME = {
40
+ colors: [...PALETTE],
41
+ background: "transparent",
42
+ textColor: "var(--color-gray-100, #f3f4f6)",
43
+ textMuted: "var(--color-gray-500, #6b7280)",
44
+ axisColor: "var(--color-gray-700, #374151)",
45
+ gridColor: "var(--color-gray-800, #1f2937)",
46
+ tooltipBackground: "var(--color-gray-900, #111827)",
47
+ tooltipText: "var(--color-gray-100, #f3f4f6)",
48
+ tooltipBorder: "var(--color-gray-700, #374151)",
49
+ fontFamily: FONT_FAMILY,
50
+ fontSize: 12,
51
+ fontSizeSmall: 10,
52
+ fontSizeLarge: 14,
53
+ borderRadius: 8,
54
+ gridStyle: "dashed",
55
+ gridWidth: 1,
56
+ axisWidth: 1,
57
+ pointRadius: 4,
58
+ lineWidth: 2.5
59
+ };
60
+ var NO_AXES_TYPES = /* @__PURE__ */ new Set([
61
+ "sparkline",
62
+ "gauge",
63
+ "funnel",
64
+ "radar",
65
+ "pie",
66
+ "donut",
67
+ "heatmap",
68
+ "horizontal-bar",
69
+ "treemap",
70
+ "polar",
71
+ "radial-bar",
72
+ "bullet",
73
+ "dumbbell",
74
+ "calendar",
75
+ "sankey",
76
+ "sunburst",
77
+ "tree",
78
+ "graph",
79
+ "parallel",
80
+ "themeriver",
81
+ "pictorialbar",
82
+ "chord",
83
+ "geo",
84
+ "lines",
85
+ "matrix",
86
+ "custom",
87
+ "kagi",
88
+ "renko"
89
+ ]);
90
+ var BAND_SCALE_TYPES = /* @__PURE__ */ new Set([
91
+ "bar",
92
+ "stacked-bar",
93
+ "horizontal-bar",
94
+ "candlestick",
95
+ "waterfall",
96
+ "histogram",
97
+ "boxplot",
98
+ "lollipop",
99
+ "combo",
100
+ "ohlc",
101
+ "volume"
102
+ ]);
103
+ function resolveOptions(opts, seriesCount) {
104
+ const multi = seriesCount > 1;
105
+ return {
106
+ ...opts,
107
+ width: opts.width ?? 0,
108
+ height: opts.height ?? 0,
109
+ padding: opts.padding ?? [12, 16, 8, 8],
110
+ renderer: opts.renderer ?? "svg",
111
+ theme: opts.theme ?? "auto",
112
+ xLabel: opts.xLabel ?? "",
113
+ yLabel: opts.yLabel ?? "",
114
+ xFormat: opts.xFormat ?? formatX,
115
+ yFormat: opts.yFormat ?? formatY,
116
+ xGrid: opts.xGrid ?? false,
117
+ yGrid: opts.yGrid ?? true,
118
+ xAxis: opts.xAxis ?? true,
119
+ yAxis: opts.yAxis ?? true,
120
+ xTicks: opts.xTicks ?? 0,
121
+ yTicks: opts.yTicks ?? 5,
122
+ yMin: opts.yMin,
123
+ yMax: opts.yMax,
124
+ legend: opts.legend === void 0 ? multi ? "top" : false : opts.legend === true ? "top" : opts.legend,
125
+ tooltip: opts.tooltip === void 0 || opts.tooltip === true ? { enabled: true } : opts.tooltip === false ? false : opts.tooltip,
126
+ animate: opts.animate ?? true,
127
+ duration: opts.duration ?? 300,
128
+ zoom: opts.zoom ?? false,
129
+ pan: opts.pan ?? false,
130
+ crosshair: !opts.crosshair ? false : opts.crosshair === true ? { enabled: true, mode: "vertical" } : { enabled: opts.crosshair.enabled ?? true, mode: opts.crosshair.mode ?? "vertical" },
131
+ brush: opts.brush ?? false,
132
+ colors: opts.colors ?? [...PALETTE],
133
+ fontFamily: opts.fontFamily ?? FONT_FAMILY,
134
+ fontSize: opts.fontSize ?? 12,
135
+ curve: opts.curve ?? "monotone",
136
+ barRadius: opts.barRadius ?? 4,
137
+ barGap: opts.barGap ?? 0.2,
138
+ ariaLabel: opts.ariaLabel ?? "Chart",
139
+ ariaDescription: opts.ariaDescription ?? "",
140
+ onClick: opts.onClick,
141
+ onHover: opts.onHover,
142
+ className: opts.className ?? ""
143
+ };
144
+ }
145
+ function formatX(value) {
146
+ if (value instanceof Date) return value.toLocaleDateString();
147
+ return String(value);
148
+ }
149
+ function formatY(value) {
150
+ const abs = Math.abs(value);
151
+ if (abs >= 1e12) return trim(value / 1e12, "T");
152
+ if (abs >= 1e9) return trim(value / 1e9, "B");
153
+ if (abs >= 1e6) return trim(value / 1e6, "M");
154
+ if (abs >= 1e3) return trim(value / 1e3, "K");
155
+ if (Number.isInteger(value)) return String(value);
156
+ return value.toFixed(1);
157
+ }
158
+ function trim(v, suffix) {
159
+ const s = v.toFixed(1);
160
+ return (s.endsWith(".0") ? s.slice(0, -2) : s) + suffix;
161
+ }
162
+
163
+ // src/theme/presets.ts
164
+ var CORPORATE_THEME = {
165
+ colors: [
166
+ "#1e40af",
167
+ "#2563eb",
168
+ "#3b82f6",
169
+ "#60a5fa",
170
+ "#93c5fd",
171
+ "#1e3a5f",
172
+ "#0f766e",
173
+ "#4f46e5",
174
+ "#7c3aed",
175
+ "#be185d"
176
+ ],
177
+ background: "transparent",
178
+ textColor: "#1e293b",
179
+ textMuted: "#64748b",
180
+ axisColor: "#94a3b8",
181
+ gridColor: "#e2e8f0",
182
+ tooltipBackground: "#ffffff",
183
+ tooltipText: "#1e293b",
184
+ tooltipBorder: "#cbd5e1",
185
+ fontFamily: '"Inter", "Segoe UI", -apple-system, sans-serif',
186
+ fontSize: 11,
187
+ fontSizeSmall: 9,
188
+ fontSizeLarge: 13,
189
+ borderRadius: 4,
190
+ gridStyle: "solid",
191
+ gridWidth: 1,
192
+ axisWidth: 1,
193
+ pointRadius: 3,
194
+ lineWidth: 2
195
+ };
196
+ var SAAS_THEME = {
197
+ colors: [
198
+ "#6366f1",
199
+ "#8b5cf6",
200
+ "#ec4899",
201
+ "#f43f5e",
202
+ "#f97316",
203
+ "#eab308",
204
+ "#22c55e",
205
+ "#06b6d4",
206
+ "#0ea5e9",
207
+ "#a855f7"
208
+ ],
209
+ background: "transparent",
210
+ textColor: "#0f172a",
211
+ textMuted: "#475569",
212
+ axisColor: "#cbd5e1",
213
+ gridColor: "#f1f5f9",
214
+ tooltipBackground: "#0f172a",
215
+ tooltipText: "#f8fafc",
216
+ tooltipBorder: "#334155",
217
+ fontFamily: FONT_FAMILY,
218
+ fontSize: 12,
219
+ fontSizeSmall: 10,
220
+ fontSizeLarge: 14,
221
+ borderRadius: 8,
222
+ gridStyle: "dashed",
223
+ gridWidth: 1,
224
+ axisWidth: 1,
225
+ pointRadius: 4,
226
+ lineWidth: 2.5
227
+ };
228
+ var STARTUP_THEME = {
229
+ colors: [
230
+ "#ff6b6b",
231
+ "#feca57",
232
+ "#48dbfb",
233
+ "#ff9ff3",
234
+ "#54a0ff",
235
+ "#5f27cd",
236
+ "#01a3a4",
237
+ "#f368e0",
238
+ "#ff6348",
239
+ "#2ed573"
240
+ ],
241
+ background: "transparent",
242
+ textColor: "#2d3436",
243
+ textMuted: "#636e72",
244
+ axisColor: "#b2bec3",
245
+ gridColor: "#dfe6e9",
246
+ tooltipBackground: "#2d3436",
247
+ tooltipText: "#dfe6e9",
248
+ tooltipBorder: "#636e72",
249
+ fontFamily: '"DM Sans", "Inter", -apple-system, sans-serif',
250
+ fontSize: 12,
251
+ fontSizeSmall: 10,
252
+ fontSizeLarge: 15,
253
+ borderRadius: 10,
254
+ gridStyle: "dotted",
255
+ gridWidth: 1,
256
+ axisWidth: 2,
257
+ pointRadius: 5,
258
+ lineWidth: 3
259
+ };
260
+ var EDITORIAL_THEME = {
261
+ colors: [
262
+ "#1a1a1a",
263
+ "#c0392b",
264
+ "#2980b9",
265
+ "#8e44ad",
266
+ "#27ae60",
267
+ "#d35400",
268
+ "#2c3e50",
269
+ "#16a085",
270
+ "#f39c12",
271
+ "#7f8c8d"
272
+ ],
273
+ background: "transparent",
274
+ textColor: "#1a1a1a",
275
+ textMuted: "#7f8c8d",
276
+ axisColor: "#bdc3c7",
277
+ gridColor: "#ecf0f1",
278
+ tooltipBackground: "#1a1a1a",
279
+ tooltipText: "#ecf0f1",
280
+ tooltipBorder: "#34495e",
281
+ fontFamily: '"Georgia", "Times New Roman", serif',
282
+ fontSize: 12,
283
+ fontSizeSmall: 10,
284
+ fontSizeLarge: 14,
285
+ borderRadius: 0,
286
+ gridStyle: "solid",
287
+ gridWidth: 0.5,
288
+ axisWidth: 1.5,
289
+ pointRadius: 3,
290
+ lineWidth: 1.5
291
+ };
292
+ var OCEAN_THEME = {
293
+ colors: [
294
+ "#00d2ff",
295
+ "#3a7bd5",
296
+ "#00b894",
297
+ "#fdcb6e",
298
+ "#e17055",
299
+ "#a29bfe",
300
+ "#fd79a8",
301
+ "#55efc4",
302
+ "#74b9ff",
303
+ "#ffeaa7"
304
+ ],
305
+ background: "transparent",
306
+ textColor: "#dfe6e9",
307
+ textMuted: "#636e72",
308
+ axisColor: "#2d3436",
309
+ gridColor: "#2d3436",
310
+ tooltipBackground: "#0a0a23",
311
+ tooltipText: "#dfe6e9",
312
+ tooltipBorder: "#2d3436",
313
+ fontFamily: FONT_FAMILY,
314
+ fontSize: 12,
315
+ fontSizeSmall: 10,
316
+ fontSizeLarge: 14,
317
+ borderRadius: 6,
318
+ gridStyle: "dashed",
319
+ gridWidth: 1,
320
+ axisWidth: 1,
321
+ pointRadius: 3.5,
322
+ lineWidth: 2
323
+ };
324
+ var THEME_PRESETS = {
325
+ corporate: CORPORATE_THEME,
326
+ saas: SAAS_THEME,
327
+ startup: STARTUP_THEME,
328
+ editorial: EDITORIAL_THEME,
329
+ ocean: OCEAN_THEME
330
+ };
331
+
332
+ // src/theme/engine.ts
333
+ function resolveTheme(theme) {
334
+ if (typeof theme === "object") return { ...LIGHT_THEME, ...theme };
335
+ switch (theme) {
336
+ case "dark":
337
+ return DARK_THEME;
338
+ case "light":
339
+ return LIGHT_THEME;
340
+ case "auto":
341
+ return detectScheme() === "dark" ? DARK_THEME : LIGHT_THEME;
342
+ default: {
343
+ const preset = THEME_PRESETS[theme];
344
+ if (preset) return preset;
345
+ return LIGHT_THEME;
346
+ }
347
+ }
348
+ }
349
+ function applyTheme(el2, theme) {
350
+ const s = el2.style;
351
+ theme.colors.forEach((c, i) => s.setProperty(`${CSS_PREFIX}-color-${i + 1}`, c));
352
+ s.setProperty(`${CSS_PREFIX}-bg`, theme.background);
353
+ s.setProperty(`${CSS_PREFIX}-text`, theme.textColor);
354
+ s.setProperty(`${CSS_PREFIX}-text-muted`, theme.textMuted);
355
+ s.setProperty(`${CSS_PREFIX}-axis`, theme.axisColor);
356
+ s.setProperty(`${CSS_PREFIX}-grid`, theme.gridColor);
357
+ s.setProperty(`${CSS_PREFIX}-tooltip-bg`, theme.tooltipBackground);
358
+ s.setProperty(`${CSS_PREFIX}-tooltip-text`, theme.tooltipText);
359
+ s.setProperty(`${CSS_PREFIX}-tooltip-border`, theme.tooltipBorder);
360
+ s.setProperty(`${CSS_PREFIX}-font-family`, theme.fontFamily);
361
+ s.setProperty(`${CSS_PREFIX}-font-size`, `${theme.fontSize}px`);
362
+ s.setProperty(`${CSS_PREFIX}-font-size-sm`, `${theme.fontSizeSmall}px`);
363
+ s.setProperty(`${CSS_PREFIX}-font-size-lg`, `${theme.fontSizeLarge}px`);
364
+ s.setProperty(`${CSS_PREFIX}-radius`, `${theme.borderRadius}px`);
365
+ s.setProperty(`${CSS_PREFIX}-grid-width`, String(theme.gridWidth));
366
+ s.setProperty(`${CSS_PREFIX}-axis-width`, String(theme.axisWidth));
367
+ s.setProperty(`${CSS_PREFIX}-point-r`, String(theme.pointRadius));
368
+ s.setProperty(`${CSS_PREFIX}-line-w`, String(theme.lineWidth));
369
+ }
370
+ function watchScheme(cb) {
371
+ if (typeof window === "undefined" || !window.matchMedia) return () => {
372
+ };
373
+ const mql = window.matchMedia("(prefers-color-scheme: dark)");
374
+ const handler = (e) => cb(e.matches ? "dark" : "light");
375
+ mql.addEventListener("change", handler);
376
+ return () => mql.removeEventListener("change", handler);
377
+ }
378
+ function detectScheme() {
379
+ if (typeof window === "undefined" || !window.matchMedia) return "light";
380
+ return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
381
+ }
382
+
383
+ // src/events/bus.ts
384
+ function createEventBus() {
385
+ const listeners = /* @__PURE__ */ new Map();
386
+ return {
387
+ on(event, handler) {
388
+ let set = listeners.get(event);
389
+ if (!set) {
390
+ set = /* @__PURE__ */ new Set();
391
+ listeners.set(event, set);
392
+ }
393
+ set.add(handler);
394
+ return () => {
395
+ set.delete(handler);
396
+ if (set.size === 0) listeners.delete(event);
397
+ };
398
+ },
399
+ emit(event, payload) {
400
+ const set = listeners.get(event);
401
+ if (set) for (const h of set) h(payload);
402
+ },
403
+ off(event, handler) {
404
+ const set = listeners.get(event);
405
+ if (!set) return;
406
+ set.delete(handler);
407
+ if (set.size === 0) listeners.delete(event);
408
+ },
409
+ destroy() {
410
+ listeners.clear();
411
+ }
412
+ };
413
+ }
414
+
415
+ // src/styles/chart.ts
416
+ var CHART_CSS = (
417
+ /* css */
418
+ `
419
+ /* ---- Base ---- */
420
+ .chartts {
421
+ -webkit-font-smoothing: antialiased;
422
+ -moz-osx-font-smoothing: grayscale;
423
+ }
424
+
425
+ /* ---- Lines ---- */
426
+ .chartts-line {
427
+ stroke-linecap: round;
428
+ stroke-linejoin: round;
429
+ transition: stroke-width 0.2s ease, opacity 0.2s ease;
430
+ }
431
+ .chartts-series:hover .chartts-line {
432
+ stroke-width: 2.5;
433
+ }
434
+
435
+ /* Line draw animation */
436
+ @keyframes chartts-draw {
437
+ from { stroke-dashoffset: var(--chartts-path-len); }
438
+ to { stroke-dashoffset: 0; }
439
+ }
440
+ .chartts-line[style*="--chartts-path-len"] {
441
+ animation: chartts-draw 0.8s cubic-bezier(0.4, 0, 0.2, 1) forwards;
442
+ }
443
+
444
+ /* ---- Area fills ---- */
445
+ @keyframes chartts-fade-up {
446
+ from { opacity: 0; }
447
+ to { opacity: 1; }
448
+ }
449
+ .chartts-area {
450
+ animation: chartts-fade-up 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
451
+ transition: opacity 0.2s ease;
452
+ }
453
+
454
+ /* ---- Line glow ---- */
455
+ .chartts-line-glow {
456
+ stroke-linecap: round;
457
+ stroke-linejoin: round;
458
+ pointer-events: none;
459
+ }
460
+
461
+ /* ---- Point glow ---- */
462
+ .chartts-point-glow, .chartts-dot-glow {
463
+ pointer-events: none;
464
+ }
465
+
466
+ /* ---- Data points ---- */
467
+ @keyframes chartts-point-pop {
468
+ 0% { opacity: 0; transform: scale(0); }
469
+ 60% { transform: scale(1.3); }
470
+ 100% { opacity: 1; transform: scale(1); }
471
+ }
472
+ .chartts-point {
473
+ transform-origin: center;
474
+ transform-box: fill-box;
475
+ animation: chartts-point-pop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
476
+ animation-delay: calc(var(--chartts-i, 0) * 30ms + 0.4s);
477
+ opacity: 0;
478
+ transition: opacity 0.15s ease, stroke-width 0.15s ease;
479
+ cursor: pointer;
480
+ }
481
+ .chartts-point:hover {
482
+ stroke-width: 3;
483
+ filter: brightness(1.15);
484
+ }
485
+
486
+ /* ---- Bars ---- */
487
+ @keyframes chartts-bar-in {
488
+ from { opacity: 0; transform: scaleY(0.3); }
489
+ to { opacity: 1; transform: scaleY(1); }
490
+ }
491
+ .chartts-bar {
492
+ transform-origin: bottom center;
493
+ transform-box: fill-box;
494
+ animation: chartts-bar-in 0.4s cubic-bezier(0.22, 1, 0.36, 1) forwards;
495
+ animation-delay: calc(var(--chartts-i, 0) * 40ms);
496
+ opacity: 0;
497
+ transition: filter 0.15s ease, opacity 0.15s ease;
498
+ cursor: pointer;
499
+ }
500
+ .chartts-bar:hover {
501
+ filter: brightness(1.12) saturate(1.1);
502
+ }
503
+ .chartts-bar-horizontal {
504
+ transform-origin: left center;
505
+ animation-name: chartts-hbar-in;
506
+ }
507
+ @keyframes chartts-hbar-in {
508
+ from { opacity: 0; transform: scaleX(0.3); }
509
+ to { opacity: 1; transform: scaleX(1); }
510
+ }
511
+
512
+ /* ---- Pie / Donut slices ---- */
513
+ @keyframes chartts-slice-in {
514
+ from { opacity: 0; transform: scale(0.85); }
515
+ to { opacity: 1; transform: scale(1); }
516
+ }
517
+ .chartts-slice {
518
+ transform-origin: center;
519
+ transform-box: fill-box;
520
+ animation: chartts-slice-in 0.5s cubic-bezier(0.34, 1.3, 0.64, 1) forwards;
521
+ animation-delay: calc(var(--chartts-i, 0) * 60ms);
522
+ opacity: 0;
523
+ transition: filter 0.15s ease, opacity 0.15s ease;
524
+ cursor: pointer;
525
+ pointer-events: all;
526
+ }
527
+ .chartts-slice:hover {
528
+ filter: brightness(1.1) saturate(1.15);
529
+ opacity: 0.92;
530
+ }
531
+
532
+ /* ---- Sparkline ---- */
533
+ .chartts-sparkline-line {
534
+ stroke-linecap: round;
535
+ stroke-linejoin: round;
536
+ }
537
+ .chartts-sparkline-line[style*="--chartts-path-len"] {
538
+ animation: chartts-draw 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
539
+ }
540
+
541
+ /* ---- Radar ---- */
542
+ @keyframes chartts-radar-in {
543
+ from { opacity: 0; transform: scale(0.3); }
544
+ to { opacity: 1; transform: scale(1); }
545
+ }
546
+ .chartts-radar-area {
547
+ transform-origin: center;
548
+ transform-box: fill-box;
549
+ animation: chartts-radar-in 0.7s cubic-bezier(0.22, 1, 0.36, 1) forwards;
550
+ animation-delay: calc(var(--chartts-radar-i, 0) * 120ms);
551
+ opacity: 0;
552
+ transition: opacity 0.2s ease, fill-opacity 0.2s ease;
553
+ }
554
+ .chartts-radar-area:hover {
555
+ fill-opacity: 0.35;
556
+ }
557
+ .chartts-radar-point {
558
+ transform-origin: center;
559
+ transform-box: fill-box;
560
+ animation: chartts-point-pop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
561
+ animation-delay: calc(var(--chartts-i, 0) * 30ms + 0.5s);
562
+ opacity: 0;
563
+ transition: r 0.15s ease, opacity 0.15s ease;
564
+ cursor: pointer;
565
+ }
566
+ .chartts-radar-point:hover {
567
+ filter: brightness(1.15);
568
+ }
569
+
570
+ /* ---- Gauge ---- */
571
+ @keyframes chartts-gauge-draw {
572
+ from { stroke-dashoffset: var(--chartts-path-len); }
573
+ to { stroke-dashoffset: 0; }
574
+ }
575
+ @keyframes chartts-gauge-fade {
576
+ from { opacity: 0; }
577
+ to { opacity: 1; }
578
+ }
579
+ .chartts-gauge-fill[style*="--chartts-path-len"] {
580
+ animation: chartts-gauge-draw 1s cubic-bezier(0.22, 1, 0.36, 1) forwards;
581
+ }
582
+ .chartts-gauge-needle {
583
+ animation: chartts-gauge-fade 0.4s ease 0.6s forwards;
584
+ opacity: 0;
585
+ }
586
+ .chartts-gauge-needle-cap {
587
+ animation: chartts-gauge-fade 0.3s ease 0.65s forwards;
588
+ opacity: 0;
589
+ }
590
+ .chartts-gauge-tick {
591
+ opacity: 0.5;
592
+ }
593
+
594
+ /* ---- Funnel ---- */
595
+ @keyframes chartts-funnel-slide {
596
+ from { opacity: 0; transform: translateY(-8px); }
597
+ to { opacity: 1; transform: translateY(0); }
598
+ }
599
+ .chartts-funnel-step {
600
+ animation: chartts-funnel-slide 0.4s cubic-bezier(0.4, 0, 0.2, 1) forwards;
601
+ animation-delay: calc(var(--chartts-i, 0) * 80ms);
602
+ opacity: 0;
603
+ transition: filter 0.15s ease;
604
+ cursor: pointer;
605
+ }
606
+ .chartts-funnel-step:hover {
607
+ filter: brightness(1.1);
608
+ }
609
+
610
+ /* ---- Waterfall ---- */
611
+ @keyframes chartts-waterfall-in {
612
+ from { opacity: 0; transform: translateY(6px); }
613
+ to { opacity: 1; transform: translateY(0); }
614
+ }
615
+ .chartts-waterfall-bar {
616
+ animation: chartts-waterfall-in 0.35s cubic-bezier(0.22, 1, 0.36, 1) forwards;
617
+ animation-delay: calc(var(--chartts-i, 0) * 50ms);
618
+ opacity: 0;
619
+ transition: filter 0.15s ease;
620
+ cursor: pointer;
621
+ }
622
+ .chartts-waterfall-bar:hover {
623
+ filter: brightness(1.12);
624
+ }
625
+
626
+ /* ---- Candlestick ---- */
627
+ @keyframes chartts-candle-in {
628
+ from { opacity: 0; transform: scaleY(0.5); }
629
+ to { opacity: 1; transform: scaleY(1); }
630
+ }
631
+ .chartts-candle {
632
+ transform-origin: center;
633
+ transform-box: fill-box;
634
+ animation: chartts-candle-in 0.3s cubic-bezier(0.22, 1, 0.36, 1) forwards;
635
+ animation-delay: calc(var(--chartts-i, 0) * 50ms);
636
+ opacity: 0;
637
+ transition: filter 0.15s ease;
638
+ cursor: pointer;
639
+ }
640
+ .chartts-candle:hover {
641
+ filter: brightness(1.15);
642
+ }
643
+ .chartts-wick {
644
+ transition: opacity 0.15s ease;
645
+ }
646
+
647
+ /* ---- Bubble ---- */
648
+ @keyframes chartts-bubble-pop {
649
+ 0% { opacity: 0; transform: scale(0); }
650
+ 70% { transform: scale(1.08); }
651
+ 100% { opacity: 1; transform: scale(1); }
652
+ }
653
+ .chartts-bubble {
654
+ transform-origin: center;
655
+ transform-box: fill-box;
656
+ animation: chartts-bubble-pop 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
657
+ animation-delay: calc(var(--chartts-i, 0) * 50ms);
658
+ opacity: 0;
659
+ transition: filter 0.15s ease, opacity 0.15s ease;
660
+ cursor: pointer;
661
+ }
662
+ .chartts-bubble:hover {
663
+ filter: brightness(1.1) saturate(1.1);
664
+ }
665
+
666
+ /* ---- Scatter dots ---- */
667
+ .chartts-dot {
668
+ transform-origin: center;
669
+ transform-box: fill-box;
670
+ animation: chartts-point-pop 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
671
+ animation-delay: calc(var(--chartts-i, 0) * 30ms);
672
+ opacity: 0;
673
+ transition: filter 0.15s ease, opacity 0.15s ease;
674
+ cursor: pointer;
675
+ }
676
+ .chartts-dot:hover {
677
+ filter: brightness(1.15);
678
+ opacity: 1;
679
+ }
680
+
681
+ /* ---- Heatmap ---- */
682
+ @keyframes chartts-cell-in {
683
+ from { opacity: 0; }
684
+ to { opacity: 1; }
685
+ }
686
+ .chartts-heatmap-cell {
687
+ animation: chartts-cell-in 0.3s ease forwards;
688
+ animation-delay: calc(var(--chartts-i, 0) * 15ms);
689
+ opacity: 0;
690
+ transition: filter 0.15s ease, opacity 0.15s ease;
691
+ cursor: pointer;
692
+ }
693
+ .chartts-heatmap-cell:hover {
694
+ filter: brightness(1.2) saturate(1.2);
695
+ stroke: currentColor;
696
+ stroke-width: 1.5;
697
+ }
698
+
699
+ /* ---- Boxplot ---- */
700
+ @keyframes chartts-boxplot-in {
701
+ from { opacity: 0; transform: scaleY(0.4); }
702
+ to { opacity: 1; transform: scaleY(1); }
703
+ }
704
+ .chartts-boxplot-box {
705
+ transform-origin: center;
706
+ transform-box: fill-box;
707
+ animation: chartts-boxplot-in 0.35s cubic-bezier(0.22, 1, 0.36, 1) forwards;
708
+ animation-delay: calc(var(--chartts-i, 0) * 60ms);
709
+ opacity: 0;
710
+ transition: filter 0.15s ease;
711
+ cursor: pointer;
712
+ }
713
+ .chartts-boxplot-box:hover {
714
+ filter: brightness(1.15);
715
+ }
716
+ .chartts-boxplot-whisker, .chartts-boxplot-cap, .chartts-boxplot-median {
717
+ pointer-events: none;
718
+ }
719
+
720
+ /* ---- Treemap ---- */
721
+ @keyframes chartts-cell-grow {
722
+ from { opacity: 0; transform: scale(0.85); }
723
+ to { opacity: 1; transform: scale(1); }
724
+ }
725
+ .chartts-treemap-cell {
726
+ transform-origin: center;
727
+ transform-box: fill-box;
728
+ animation: chartts-cell-grow 0.4s ease forwards;
729
+ animation-delay: calc(var(--chartts-i, 0) * 40ms);
730
+ cursor: pointer;
731
+ transition: filter 0.15s ease;
732
+ }
733
+ .chartts-treemap-cell:hover {
734
+ filter: brightness(1.15);
735
+ }
736
+ .chartts-treemap-label, .chartts-treemap-value {
737
+ pointer-events: none;
738
+ }
739
+
740
+ /* ---- Polar ---- */
741
+ @keyframes chartts-wedge-in {
742
+ from { opacity: 0; transform: scale(0); }
743
+ to { opacity: 1; transform: scale(1); }
744
+ }
745
+ .chartts-polar-wedge {
746
+ transform-origin: center;
747
+ transform-box: fill-box;
748
+ animation: chartts-wedge-in 0.5s ease forwards;
749
+ animation-delay: calc(var(--chartts-i, 0) * 50ms);
750
+ cursor: pointer;
751
+ transition: filter 0.15s ease;
752
+ }
753
+ .chartts-polar-wedge:hover {
754
+ filter: brightness(1.15);
755
+ }
756
+ .chartts-polar-label {
757
+ pointer-events: none;
758
+ }
759
+ .chartts-polar-grid {
760
+ pointer-events: none;
761
+ }
762
+
763
+ /* ---- Radial Bar ---- */
764
+ @keyframes chartts-radialbar-in {
765
+ from { opacity: 0; stroke-dashoffset: var(--chartts-path-len, 200); }
766
+ to { opacity: 1; stroke-dashoffset: 0; }
767
+ }
768
+ .chartts-radialbar-arc {
769
+ animation: chartts-radialbar-in 0.6s cubic-bezier(0.4, 0, 0.2, 1) forwards;
770
+ animation-delay: calc(var(--chartts-i, 0) * 100ms);
771
+ opacity: 0;
772
+ transition: filter 0.15s ease;
773
+ cursor: pointer;
774
+ }
775
+ .chartts-radialbar-arc:hover {
776
+ filter: brightness(1.15);
777
+ }
778
+ .chartts-radialbar-track {
779
+ pointer-events: none;
780
+ }
781
+
782
+ /* ---- Lollipop ---- */
783
+ @keyframes chartts-lollipop-in {
784
+ from { opacity: 0; transform: scaleY(0.3); }
785
+ to { opacity: 1; transform: scaleY(1); }
786
+ }
787
+ .chartts-lollipop-stem {
788
+ transform-origin: bottom center;
789
+ transform-box: fill-box;
790
+ animation: chartts-lollipop-in 0.4s ease forwards;
791
+ animation-delay: calc(var(--chartts-i, 0) * 40ms);
792
+ }
793
+ .chartts-lollipop-dot {
794
+ cursor: pointer;
795
+ transition: r 0.15s ease, filter 0.15s ease;
796
+ }
797
+ .chartts-lollipop-dot:hover {
798
+ filter: brightness(1.15);
799
+ }
800
+
801
+ /* ---- Bullet ---- */
802
+ @keyframes chartts-bullet-in {
803
+ from { transform: scaleX(0); }
804
+ to { transform: scaleX(1); }
805
+ }
806
+ .chartts-bullet-bar {
807
+ transform-origin: left center;
808
+ transform-box: fill-box;
809
+ animation: chartts-bullet-in 0.5s ease forwards;
810
+ animation-delay: calc(var(--chartts-i, 0) * 80ms);
811
+ cursor: pointer;
812
+ transition: filter 0.15s ease;
813
+ }
814
+ .chartts-bullet-bar:hover {
815
+ filter: brightness(1.15);
816
+ }
817
+ .chartts-bullet-range, .chartts-bullet-target, .chartts-bullet-label {
818
+ pointer-events: none;
819
+ }
820
+
821
+ /* ---- Dumbbell ---- */
822
+ @keyframes chartts-dumbbell-in {
823
+ 0% { opacity: 0; transform: scale(0); }
824
+ 70% { transform: scale(1.15); }
825
+ 100% { opacity: 1; transform: scale(1); }
826
+ }
827
+ .chartts-dumbbell-dot {
828
+ transform-origin: center;
829
+ transform-box: fill-box;
830
+ animation: chartts-dumbbell-in 0.35s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
831
+ animation-delay: calc(var(--chartts-i, 0) * 60ms);
832
+ opacity: 0;
833
+ cursor: pointer;
834
+ transition: filter 0.15s ease;
835
+ }
836
+ .chartts-dumbbell-dot:hover {
837
+ filter: brightness(1.15);
838
+ }
839
+ .chartts-dumbbell-connector, .chartts-dumbbell-label {
840
+ pointer-events: none;
841
+ }
842
+
843
+ /* ---- Calendar ---- */
844
+ .chartts-calendar-cell {
845
+ cursor: pointer;
846
+ transition: filter 0.15s ease;
847
+ animation: chartts-cell-in 0.3s ease forwards;
848
+ animation-delay: calc(var(--chartts-i, 0) * 8ms);
849
+ }
850
+ .chartts-calendar-cell:hover {
851
+ filter: brightness(1.2);
852
+ }
853
+ .chartts-calendar-daylabel {
854
+ pointer-events: none;
855
+ }
856
+
857
+ /* ---- Sankey ---- */
858
+ @keyframes chartts-sankey-link-in {
859
+ from { opacity: 0; }
860
+ to { opacity: 1; }
861
+ }
862
+ .chartts-sankey-link {
863
+ animation: chartts-sankey-link-in 0.5s ease forwards;
864
+ opacity: 0;
865
+ transition: fill-opacity 0.15s ease;
866
+ cursor: pointer;
867
+ }
868
+ .chartts-sankey-link:hover {
869
+ fill-opacity: 0.5 !important;
870
+ }
871
+ .chartts-sankey-node {
872
+ cursor: pointer;
873
+ transition: filter 0.15s ease;
874
+ }
875
+ .chartts-sankey-node:hover {
876
+ filter: brightness(1.15);
877
+ }
878
+ .chartts-sankey-label {
879
+ pointer-events: none;
880
+ }
881
+
882
+ /* ---- Combo ---- */
883
+ .chartts-combo-line {
884
+ pointer-events: none;
885
+ }
886
+ .chartts-combo-point {
887
+ cursor: pointer;
888
+ transition: r 0.15s ease;
889
+ }
890
+
891
+ /* ---- Sunburst ---- */
892
+ @keyframes chartts-sunburst-in {
893
+ from { opacity: 0; transform: scale(0.85); }
894
+ to { opacity: 1; transform: scale(1); }
895
+ }
896
+ .chartts-sunburst-sector {
897
+ transform-origin: center;
898
+ transform-box: fill-box;
899
+ animation: chartts-sunburst-in 0.5s cubic-bezier(0.34, 1.3, 0.64, 1) forwards;
900
+ animation-delay: calc(var(--chartts-i, 0) * 30ms);
901
+ opacity: 0;
902
+ transition: filter 0.15s ease, fill-opacity 0.15s ease;
903
+ cursor: pointer;
904
+ }
905
+ .chartts-sunburst-sector:hover {
906
+ filter: brightness(1.15);
907
+ fill-opacity: 0.95 !important;
908
+ }
909
+ .chartts-sunburst-label {
910
+ pointer-events: none;
911
+ }
912
+
913
+ /* ---- Tree ---- */
914
+ @keyframes chartts-tree-node-in {
915
+ 0% { opacity: 0; transform: scale(0); }
916
+ 70% { transform: scale(1.2); }
917
+ 100% { opacity: 1; transform: scale(1); }
918
+ }
919
+ .chartts-tree-node {
920
+ transform-origin: center;
921
+ transform-box: fill-box;
922
+ animation: chartts-tree-node-in 0.4s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
923
+ animation-delay: calc(var(--chartts-i, 0) * 50ms);
924
+ opacity: 0;
925
+ transition: filter 0.15s ease, r 0.15s ease;
926
+ cursor: pointer;
927
+ }
928
+ .chartts-tree-node:hover {
929
+ filter: brightness(1.15);
930
+ }
931
+ .chartts-tree-edge {
932
+ opacity: 0.6;
933
+ pointer-events: none;
934
+ }
935
+ .chartts-tree-label {
936
+ pointer-events: none;
937
+ }
938
+
939
+ /* ---- Graph ---- */
940
+ @keyframes chartts-graph-node-in {
941
+ 0% { opacity: 0; transform: scale(0); }
942
+ 70% { transform: scale(1.15); }
943
+ 100% { opacity: 1; transform: scale(1); }
944
+ }
945
+ .chartts-graph-node {
946
+ transform-origin: center;
947
+ transform-box: fill-box;
948
+ animation: chartts-graph-node-in 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
949
+ animation-delay: calc(var(--chartts-i, 0) * 40ms);
950
+ opacity: 0;
951
+ transition: filter 0.15s ease, r 0.15s ease;
952
+ cursor: pointer;
953
+ }
954
+ .chartts-graph-node:hover {
955
+ filter: brightness(1.15) saturate(1.2);
956
+ }
957
+ .chartts-graph-edge {
958
+ pointer-events: none;
959
+ animation: chartts-sankey-link-in 0.5s ease forwards;
960
+ opacity: 0;
961
+ }
962
+ .chartts-graph-label {
963
+ pointer-events: none;
964
+ }
965
+
966
+ /* ---- Parallel ---- */
967
+ @keyframes chartts-parallel-in {
968
+ from { opacity: 0; stroke-dashoffset: var(--chartts-path-len, 1000); }
969
+ to { opacity: 1; stroke-dashoffset: 0; }
970
+ }
971
+ .chartts-parallel-line {
972
+ transition: opacity 0.2s ease, stroke-width 0.2s ease;
973
+ cursor: pointer;
974
+ }
975
+ .chartts-parallel-line:hover {
976
+ opacity: 1 !important;
977
+ stroke-width: 3;
978
+ }
979
+ .chartts-parallel-axis {
980
+ pointer-events: none;
981
+ }
982
+ .chartts-parallel-label, .chartts-parallel-tick {
983
+ pointer-events: none;
984
+ }
985
+
986
+ /* ---- ThemeRiver ---- */
987
+ @keyframes chartts-stream-in {
988
+ from { opacity: 0; transform: scaleY(0.3); }
989
+ to { opacity: 1; transform: scaleY(1); }
990
+ }
991
+ .chartts-themeriver-stream {
992
+ transform-origin: center;
993
+ transform-box: fill-box;
994
+ animation: chartts-stream-in 0.6s cubic-bezier(0.22, 1, 0.36, 1) forwards;
995
+ animation-delay: calc(var(--chartts-i, 0) * 80ms);
996
+ opacity: 0;
997
+ transition: fill-opacity 0.15s ease;
998
+ cursor: pointer;
999
+ }
1000
+ .chartts-themeriver-stream:hover {
1001
+ fill-opacity: 0.9 !important;
1002
+ }
1003
+ .chartts-themeriver-label {
1004
+ pointer-events: none;
1005
+ }
1006
+
1007
+ /* ---- PictorialBar ---- */
1008
+ @keyframes chartts-pictorial-in {
1009
+ 0% { opacity: 0; transform: scale(0); }
1010
+ 70% { transform: scale(1.1); }
1011
+ 100% { opacity: 1; transform: scale(1); }
1012
+ }
1013
+ .chartts-pictorialbar-symbol {
1014
+ transform-origin: center;
1015
+ transform-box: fill-box;
1016
+ animation: chartts-pictorial-in 0.3s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
1017
+ animation-delay: calc(var(--chartts-i, 0) * 20ms);
1018
+ opacity: 0;
1019
+ transition: filter 0.15s ease;
1020
+ cursor: pointer;
1021
+ }
1022
+ .chartts-pictorialbar-symbol:hover {
1023
+ filter: brightness(1.15);
1024
+ }
1025
+ .chartts-pictorialbar-value, .chartts-pictorialbar-label {
1026
+ pointer-events: none;
1027
+ }
1028
+
1029
+ /* ---- Chord ---- */
1030
+ @keyframes chartts-chord-arc-in {
1031
+ from { opacity: 0; transform: scale(0.9); }
1032
+ to { opacity: 1; transform: scale(1); }
1033
+ }
1034
+ .chartts-chord-arc {
1035
+ transform-origin: center;
1036
+ transform-box: fill-box;
1037
+ animation: chartts-chord-arc-in 0.5s ease forwards;
1038
+ animation-delay: calc(var(--chartts-i, 0) * 60ms);
1039
+ opacity: 0;
1040
+ transition: filter 0.15s ease;
1041
+ cursor: pointer;
1042
+ }
1043
+ .chartts-chord-arc:hover {
1044
+ filter: brightness(1.15);
1045
+ }
1046
+ @keyframes chartts-chord-ribbon-in {
1047
+ from { opacity: 0; }
1048
+ to { opacity: 1; }
1049
+ }
1050
+ .chartts-chord-ribbon {
1051
+ animation: chartts-chord-ribbon-in 0.6s ease 0.3s forwards;
1052
+ opacity: 0;
1053
+ transition: fill-opacity 0.15s ease;
1054
+ cursor: pointer;
1055
+ }
1056
+ .chartts-chord-ribbon:hover {
1057
+ fill-opacity: 0.55 !important;
1058
+ }
1059
+ .chartts-chord-label {
1060
+ pointer-events: none;
1061
+ }
1062
+
1063
+ /* ---- States (empty / loading / error) ---- */
1064
+ .chartts-state {
1065
+ opacity: 1;
1066
+ }
1067
+ @keyframes chartts-shimmer {
1068
+ 0% { opacity: 0.3; }
1069
+ 50% { opacity: 0.7; }
1070
+ 100% { opacity: 0.3; }
1071
+ }
1072
+ .chartts-skeleton-bar {
1073
+ animation: chartts-shimmer 1.5s ease-in-out infinite;
1074
+ animation-delay: calc(var(--chartts-i, 0) * 150ms);
1075
+ }
1076
+
1077
+ /* ---- Grid ---- */
1078
+ .chartts-grid-h, .chartts-grid-v {
1079
+ opacity: 0.6;
1080
+ }
1081
+
1082
+ /* ---- GEO/Map ---- */
1083
+ @keyframes chartts-geo-in {
1084
+ from { opacity: 0; }
1085
+ to { opacity: 1; }
1086
+ }
1087
+ .chartts-geo-region {
1088
+ animation: chartts-geo-in 0.4s ease calc(var(--chartts-i, 0) * 30ms) forwards;
1089
+ opacity: 0;
1090
+ transition: fill-opacity 0.2s ease, filter 0.15s ease;
1091
+ cursor: pointer;
1092
+ }
1093
+ .chartts-geo-region:hover {
1094
+ filter: brightness(1.1);
1095
+ fill-opacity: 0.95 !important;
1096
+ }
1097
+ .chartts-geo-scatter {
1098
+ transition: r 0.15s ease;
1099
+ cursor: pointer;
1100
+ }
1101
+
1102
+ /* ---- Lines (flow) ---- */
1103
+ @keyframes chartts-lines-flow-in {
1104
+ from { stroke-dashoffset: 300; opacity: 0; }
1105
+ to { stroke-dashoffset: 0; opacity: 1; }
1106
+ }
1107
+ .chartts-lines-flow {
1108
+ stroke-dasharray: 300;
1109
+ animation: chartts-lines-flow-in 0.8s ease calc(var(--chartts-i, 0) * 80ms) forwards;
1110
+ opacity: 0;
1111
+ transition: stroke-width 0.2s ease, stroke-opacity 0.2s ease;
1112
+ cursor: pointer;
1113
+ }
1114
+ .chartts-lines-flow:hover {
1115
+ stroke-opacity: 0.9;
1116
+ stroke-width: 4;
1117
+ }
1118
+ .chartts-lines-node {
1119
+ transition: r 0.15s ease;
1120
+ cursor: pointer;
1121
+ }
1122
+
1123
+ /* ---- Matrix ---- */
1124
+ @keyframes chartts-matrix-in {
1125
+ from { opacity: 0; transform: scale(0.8); }
1126
+ to { opacity: 1; transform: scale(1); }
1127
+ }
1128
+ .chartts-matrix-cell {
1129
+ animation: chartts-matrix-in 0.3s ease calc(var(--chartts-i, 0) * 15ms) forwards;
1130
+ opacity: 0;
1131
+ transition: filter 0.15s ease;
1132
+ cursor: pointer;
1133
+ }
1134
+ .chartts-matrix-cell:hover {
1135
+ filter: brightness(1.15);
1136
+ }
1137
+
1138
+ /* ---- OHLC ---- */
1139
+ @keyframes chartts-ohlc-in {
1140
+ from { opacity: 0; transform: scaleY(0.5); }
1141
+ to { opacity: 1; transform: scaleY(1); }
1142
+ }
1143
+ .chartts-ohlc-stem, .chartts-ohlc-tick {
1144
+ animation: chartts-ohlc-in 0.3s ease calc(var(--chartts-i, 0) * 20ms) forwards;
1145
+ opacity: 0;
1146
+ transition: stroke-width 0.15s ease;
1147
+ }
1148
+ .chartts-ohlc-stem:hover, .chartts-ohlc-tick:hover {
1149
+ stroke-width: 2.5;
1150
+ }
1151
+
1152
+ /* ---- Volume ---- */
1153
+ @keyframes chartts-volume-in {
1154
+ from { opacity: 0; transform: scaleY(0); transform-origin: bottom; }
1155
+ to { opacity: 1; transform: scaleY(1); }
1156
+ }
1157
+ .chartts-volume-bar {
1158
+ animation: chartts-volume-in 0.3s ease calc(var(--chartts-i, 0) * 20ms) forwards;
1159
+ opacity: 0;
1160
+ transition: filter 0.15s ease, fill-opacity 0.15s ease;
1161
+ cursor: pointer;
1162
+ }
1163
+ .chartts-volume-bar:hover {
1164
+ filter: brightness(1.1);
1165
+ fill-opacity: 1 !important;
1166
+ }
1167
+
1168
+ /* ---- Range / Band ---- */
1169
+ @keyframes chartts-range-in {
1170
+ from { opacity: 0; }
1171
+ to { opacity: 1; }
1172
+ }
1173
+ .chartts-range-band {
1174
+ animation: chartts-range-in 0.5s ease forwards;
1175
+ opacity: 0;
1176
+ }
1177
+ .chartts-range-center {
1178
+ animation: chartts-range-in 0.6s ease 0.1s forwards;
1179
+ opacity: 0;
1180
+ }
1181
+ .chartts-range-bound {
1182
+ animation: chartts-range-in 0.5s ease 0.05s forwards;
1183
+ opacity: 0;
1184
+ }
1185
+
1186
+ /* ---- Baseline ---- */
1187
+ @keyframes chartts-baseline-in {
1188
+ from { opacity: 0; }
1189
+ to { opacity: 1; }
1190
+ }
1191
+ .chartts-baseline-pos, .chartts-baseline-neg {
1192
+ animation: chartts-baseline-in 0.5s ease forwards;
1193
+ opacity: 0;
1194
+ }
1195
+ .chartts-baseline-line {
1196
+ animation: chartts-baseline-in 0.6s ease 0.1s forwards;
1197
+ opacity: 0;
1198
+ }
1199
+ .chartts-baseline-ref {
1200
+ animation: chartts-baseline-in 0.3s ease forwards;
1201
+ opacity: 0;
1202
+ }
1203
+
1204
+ /* ---- Kagi ---- */
1205
+ @keyframes chartts-kagi-in {
1206
+ from { stroke-dashoffset: 200; opacity: 0; }
1207
+ to { stroke-dashoffset: 0; opacity: 1; }
1208
+ }
1209
+ .chartts-kagi-line {
1210
+ stroke-dasharray: 200;
1211
+ animation: chartts-kagi-in 0.6s ease calc(var(--chartts-i, 0) * 40ms) forwards;
1212
+ opacity: 0;
1213
+ transition: stroke-width 0.15s ease;
1214
+ }
1215
+
1216
+ /* ---- Renko ---- */
1217
+ @keyframes chartts-renko-in {
1218
+ from { opacity: 0; transform: scale(0.8); }
1219
+ to { opacity: 1; transform: scale(1); }
1220
+ }
1221
+ .chartts-renko-brick {
1222
+ animation: chartts-renko-in 0.3s ease calc(var(--chartts-i, 0) * 25ms) forwards;
1223
+ opacity: 0;
1224
+ transition: filter 0.15s ease;
1225
+ cursor: pointer;
1226
+ }
1227
+ .chartts-renko-brick:hover {
1228
+ filter: brightness(1.15);
1229
+ }
1230
+
1231
+ /* ---- DataZoom ---- */
1232
+ .chartts-datazoom-handle {
1233
+ cursor: ew-resize;
1234
+ transition: fill 0.15s ease;
1235
+ }
1236
+ .chartts-datazoom-handle:hover {
1237
+ fill: #374151;
1238
+ }
1239
+ .chartts-datazoom-selected {
1240
+ cursor: grab;
1241
+ }
1242
+
1243
+ /* ---- Text ---- */
1244
+ .chartts-x-label, .chartts-y-label,
1245
+ .chartts-x-axis-label, .chartts-y-axis-label,
1246
+ .chartts-legend text, .chartts-slice-label {
1247
+ user-select: none;
1248
+ pointer-events: none;
1249
+ }
1250
+
1251
+ /* ---- Legend ---- */
1252
+ .chartts-legend-item {
1253
+ cursor: pointer;
1254
+ transition: opacity 0.15s ease;
1255
+ }
1256
+ .chartts-legend-item:hover {
1257
+ opacity: 0.7;
1258
+ }
1259
+
1260
+ /* ---- Reduced motion ---- */
1261
+ @media (prefers-reduced-motion: reduce) {
1262
+ .chartts-line, .chartts-area, .chartts-point,
1263
+ .chartts-bar, .chartts-slice, .chartts-bubble,
1264
+ .chartts-funnel-step, .chartts-waterfall-bar,
1265
+ .chartts-sparkline-line, .chartts-gauge-fill,
1266
+ .chartts-gauge-needle, .chartts-gauge-needle-cap,
1267
+ .chartts-radar-area, .chartts-radar-point,
1268
+ .chartts-treemap-cell, .chartts-polar-wedge,
1269
+ .chartts-lollipop-stem, .chartts-bullet-bar,
1270
+ .chartts-dumbbell-dot, .chartts-boxplot-box,
1271
+ .chartts-radialbar-arc, .chartts-sankey-link,
1272
+ .chartts-calendar-cell,
1273
+ .chartts-sunburst-sector, .chartts-tree-node,
1274
+ .chartts-graph-node, .chartts-graph-edge,
1275
+ .chartts-themeriver-stream, .chartts-pictorialbar-symbol,
1276
+ .chartts-chord-arc, .chartts-chord-ribbon,
1277
+ .chartts-geo-region, .chartts-lines-flow,
1278
+ .chartts-lines-node, .chartts-matrix-cell,
1279
+ .chartts-ohlc-stem, .chartts-ohlc-tick,
1280
+ .chartts-volume-bar, .chartts-range-band,
1281
+ .chartts-baseline-pos, .chartts-baseline-neg,
1282
+ .chartts-baseline-line, .chartts-kagi-line,
1283
+ .chartts-renko-brick {
1284
+ animation: none !important;
1285
+ opacity: 1 !important;
1286
+ transform: none !important;
1287
+ }
1288
+ .chartts-gauge-fill {
1289
+ stroke-dashoffset: 0 !important;
1290
+ }
1291
+ }
1292
+ `
1293
+ );
1294
+
1295
+ // src/render/svg.ts
1296
+ var NS = "http://www.w3.org/2000/svg";
1297
+ function createSVGRenderer() {
1298
+ return {
1299
+ createRoot(target, width, height, attrs) {
1300
+ const svg = document.createElementNS(NS, "svg");
1301
+ svg.setAttribute("xmlns", NS);
1302
+ svg.setAttribute("width", "100%");
1303
+ svg.setAttribute("height", "100%");
1304
+ svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
1305
+ svg.setAttribute("preserveAspectRatio", "xMidYMid meet");
1306
+ svg.classList.add("chartts");
1307
+ if (attrs) applyAttrs(svg, attrs);
1308
+ const styleEl = document.createElementNS(NS, "style");
1309
+ styleEl.textContent = CHART_CSS;
1310
+ svg.appendChild(styleEl);
1311
+ target.appendChild(svg);
1312
+ return { element: svg };
1313
+ },
1314
+ render(root, nodes) {
1315
+ const styleEl = root.element.querySelector("style");
1316
+ clear(root.element);
1317
+ if (styleEl) root.element.appendChild(styleEl);
1318
+ appendNodes(root.element, nodes);
1319
+ applyEntryAnimations(root.element);
1320
+ },
1321
+ update(root, nodes) {
1322
+ const styleEl = root.element.querySelector("style");
1323
+ clear(root.element);
1324
+ if (styleEl) root.element.appendChild(styleEl);
1325
+ appendNodes(root.element, nodes);
1326
+ applyEntryAnimations(root.element);
1327
+ },
1328
+ clear(root) {
1329
+ clear(root.element);
1330
+ },
1331
+ destroy(root) {
1332
+ root.element.remove();
1333
+ }
1334
+ };
1335
+ }
1336
+ function clear(el2) {
1337
+ while (el2.firstChild) el2.removeChild(el2.firstChild);
1338
+ }
1339
+ function appendNodes(parent, nodes) {
1340
+ for (const node of nodes) {
1341
+ const el2 = createNode(node);
1342
+ if (el2) parent.appendChild(el2);
1343
+ }
1344
+ }
1345
+ function createNode(node) {
1346
+ switch (node.type) {
1347
+ case "group": {
1348
+ const g = el("g");
1349
+ if (node.attrs) applyAttrs(g, node.attrs);
1350
+ for (const child of node.children) {
1351
+ const c = createNode(child);
1352
+ if (c) g.appendChild(c);
1353
+ }
1354
+ return g;
1355
+ }
1356
+ case "path": {
1357
+ const p = el("path");
1358
+ p.setAttribute("d", node.d);
1359
+ if (!node.attrs?.fill) p.setAttribute("fill", "none");
1360
+ if (node.attrs) applyAttrs(p, node.attrs);
1361
+ return p;
1362
+ }
1363
+ case "rect": {
1364
+ const r = el("rect");
1365
+ setNum(r, "x", node.x);
1366
+ setNum(r, "y", node.y);
1367
+ setNum(r, "width", node.width);
1368
+ setNum(r, "height", node.height);
1369
+ if (node.rx != null) setNum(r, "rx", node.rx);
1370
+ if (node.ry != null) setNum(r, "ry", node.ry);
1371
+ if (node.attrs) applyAttrs(r, node.attrs);
1372
+ return r;
1373
+ }
1374
+ case "circle": {
1375
+ const c = el("circle");
1376
+ setNum(c, "cx", node.cx);
1377
+ setNum(c, "cy", node.cy);
1378
+ setNum(c, "r", node.r);
1379
+ if (node.attrs) applyAttrs(c, node.attrs);
1380
+ return c;
1381
+ }
1382
+ case "line": {
1383
+ const l = el("line");
1384
+ setNum(l, "x1", node.x1);
1385
+ setNum(l, "y1", node.y1);
1386
+ setNum(l, "x2", node.x2);
1387
+ setNum(l, "y2", node.y2);
1388
+ if (node.attrs) applyAttrs(l, node.attrs);
1389
+ return l;
1390
+ }
1391
+ case "text": {
1392
+ const t = el("text");
1393
+ setNum(t, "x", node.x);
1394
+ setNum(t, "y", node.y);
1395
+ t.textContent = node.content;
1396
+ if (node.attrs) {
1397
+ const a = node.attrs;
1398
+ if ("textAnchor" in a && a.textAnchor) t.setAttribute("text-anchor", a.textAnchor);
1399
+ if ("dominantBaseline" in a && a.dominantBaseline) t.setAttribute("dominant-baseline", a.dominantBaseline);
1400
+ if ("fontSize" in a && a.fontSize) t.setAttribute("font-size", String(a.fontSize));
1401
+ if ("fontFamily" in a && a.fontFamily) t.setAttribute("font-family", a.fontFamily);
1402
+ if ("fontWeight" in a && a.fontWeight) t.setAttribute("font-weight", String(a.fontWeight));
1403
+ applyAttrs(t, a);
1404
+ }
1405
+ return t;
1406
+ }
1407
+ case "clipPath": {
1408
+ const cp = el("clipPath");
1409
+ cp.setAttribute("id", node.id);
1410
+ for (const child of node.children) {
1411
+ const c = createNode(child);
1412
+ if (c) cp.appendChild(c);
1413
+ }
1414
+ return cp;
1415
+ }
1416
+ case "defs": {
1417
+ const d = el("defs");
1418
+ for (const child of node.children) {
1419
+ const c = createNode(child);
1420
+ if (c) d.appendChild(c);
1421
+ }
1422
+ return d;
1423
+ }
1424
+ default:
1425
+ return null;
1426
+ }
1427
+ }
1428
+ function el(tag) {
1429
+ return document.createElementNS(NS, tag);
1430
+ }
1431
+ function setNum(el2, attr, val) {
1432
+ el2.setAttribute(attr, String(val));
1433
+ }
1434
+ function applyAttrs(el2, attrs) {
1435
+ const ATTR_MAP2 = {
1436
+ class: "class",
1437
+ style: "style",
1438
+ stroke: "stroke",
1439
+ strokeWidth: "stroke-width",
1440
+ strokeDasharray: "stroke-dasharray",
1441
+ fill: "fill",
1442
+ fillOpacity: "fill-opacity",
1443
+ opacity: "opacity",
1444
+ transform: "transform",
1445
+ role: "role",
1446
+ ariaLabel: "aria-label",
1447
+ tabindex: "tabindex"
1448
+ };
1449
+ for (const [key, value] of Object.entries(attrs)) {
1450
+ if (value == null) continue;
1451
+ if (["textAnchor", "dominantBaseline", "fontSize", "fontFamily", "fontWeight", "rx", "ry"].includes(key)) continue;
1452
+ if (key === "clipPath") {
1453
+ el2.setAttribute("clip-path", `url(#${value})`);
1454
+ } else if (key.startsWith("data-")) {
1455
+ el2.setAttribute(key, String(value));
1456
+ } else if (ATTR_MAP2[key]) {
1457
+ el2.setAttribute(ATTR_MAP2[key], String(value));
1458
+ }
1459
+ }
1460
+ }
1461
+ function applyEntryAnimations(svg) {
1462
+ svg.querySelectorAll(".chartts-line, .chartts-sparkline-line").forEach((p) => {
1463
+ try {
1464
+ const existing = p.getAttribute("stroke-dasharray");
1465
+ if (existing && existing.includes(",")) return;
1466
+ const len = p.getTotalLength();
1467
+ p.style.setProperty("--chartts-path-len", String(len));
1468
+ p.setAttribute("stroke-dasharray", String(len));
1469
+ p.setAttribute("stroke-dashoffset", String(len));
1470
+ } catch {
1471
+ }
1472
+ });
1473
+ const STAGGER_SELECTORS = [
1474
+ ".chartts-point",
1475
+ ".chartts-bar",
1476
+ ".chartts-slice",
1477
+ ".chartts-bubble",
1478
+ ".chartts-funnel-step",
1479
+ ".chartts-waterfall-bar",
1480
+ ".chartts-candle",
1481
+ ".chartts-dot",
1482
+ ".chartts-radar-point",
1483
+ ".chartts-heatmap-cell",
1484
+ ".chartts-treemap-cell",
1485
+ ".chartts-polar-wedge",
1486
+ ".chartts-lollipop-stem",
1487
+ ".chartts-bullet-bar",
1488
+ ".chartts-calendar-cell",
1489
+ ".chartts-radialbar-arc",
1490
+ ".chartts-dumbbell-dot",
1491
+ ".chartts-boxplot-box",
1492
+ ".chartts-sankey-link",
1493
+ ".chartts-sunburst-sector",
1494
+ ".chartts-tree-node",
1495
+ ".chartts-graph-node",
1496
+ ".chartts-themeriver-stream",
1497
+ ".chartts-pictorialbar-symbol",
1498
+ ".chartts-chord-arc",
1499
+ ".chartts-chord-ribbon",
1500
+ ".chartts-geo-region",
1501
+ ".chartts-lines-flow",
1502
+ ".chartts-lines-node",
1503
+ ".chartts-matrix-cell",
1504
+ ".chartts-ohlc-stem",
1505
+ ".chartts-volume-bar",
1506
+ ".chartts-kagi-line",
1507
+ ".chartts-renko-brick"
1508
+ ];
1509
+ for (const selector of STAGGER_SELECTORS) {
1510
+ svg.querySelectorAll(selector).forEach((el2, i) => {
1511
+ el2.style.setProperty("--chartts-i", String(i));
1512
+ });
1513
+ }
1514
+ svg.querySelectorAll(".chartts-gauge-fill").forEach((p) => {
1515
+ try {
1516
+ const len = p.getTotalLength();
1517
+ p.style.setProperty("--chartts-path-len", String(len));
1518
+ p.setAttribute("stroke-dasharray", String(len));
1519
+ p.setAttribute("stroke-dashoffset", String(len));
1520
+ } catch {
1521
+ }
1522
+ });
1523
+ svg.querySelectorAll(".chartts-radar-area").forEach((el2, i) => {
1524
+ el2.style.setProperty("--chartts-radar-i", String(i));
1525
+ });
1526
+ }
1527
+
1528
+ // src/render/canvas.ts
1529
+ function resolveColor(value, fallback = "#000") {
1530
+ if (!value) return fallback;
1531
+ if (value === "none" || value === "transparent") return "transparent";
1532
+ if (value.startsWith("url(")) return value;
1533
+ const match = value.match(/var\([^,]+,\s*([^)]+)\)/);
1534
+ return match ? match[1].trim() : value;
1535
+ }
1536
+ function hexToRgba(hex, alpha) {
1537
+ const h = hex.replace("#", "");
1538
+ const full = h.length === 3 ? h[0] + h[0] + h[1] + h[1] + h[2] + h[2] : h;
1539
+ const r = parseInt(full.substring(0, 2), 16);
1540
+ const g = parseInt(full.substring(2, 4), 16);
1541
+ const b = parseInt(full.substring(4, 6), 16);
1542
+ return `rgba(${r},${g},${b},${alpha})`;
1543
+ }
1544
+ function resolveGradientRef(id, colors, ctx, x, y, _w, h) {
1545
+ const areaMatch = id.match(/^chartts-area-(\d+)$/);
1546
+ if (areaMatch) {
1547
+ const color = resolveColor(colors[parseInt(areaMatch[1])]);
1548
+ const grad = ctx.createLinearGradient(x, y, x, y + h);
1549
+ grad.addColorStop(0, hexToRgba(color, 0.35));
1550
+ grad.addColorStop(1, hexToRgba(color, 0.02));
1551
+ return grad;
1552
+ }
1553
+ const barMatch = id.match(/^chartts-bar-(\d+)$/);
1554
+ if (barMatch) {
1555
+ const color = resolveColor(colors[parseInt(barMatch[1])]);
1556
+ const grad = ctx.createLinearGradient(x, y, x, y + h);
1557
+ grad.addColorStop(0, hexToRgba(color, 1));
1558
+ grad.addColorStop(1, hexToRgba(color, 0.75));
1559
+ return grad;
1560
+ }
1561
+ const pieMatch = id.match(/^chartts-pie-(\d+)$/);
1562
+ if (pieMatch) {
1563
+ const color = resolveColor(colors[parseInt(pieMatch[1])]);
1564
+ return color;
1565
+ }
1566
+ const glowMatch = id.match(/^chartts-pglow-(\d+)$/);
1567
+ if (glowMatch) {
1568
+ const color = resolveColor(colors[parseInt(glowMatch[1])]);
1569
+ return hexToRgba(color, 0.4);
1570
+ }
1571
+ return null;
1572
+ }
1573
+ function parseUrlRef(value) {
1574
+ const match = value.match(/url\(#([^)]+)\)/);
1575
+ return match ? match[1] : null;
1576
+ }
1577
+ function mapTextAlign(anchor) {
1578
+ switch (anchor) {
1579
+ case "middle":
1580
+ return "center";
1581
+ case "end":
1582
+ return "right";
1583
+ default:
1584
+ return "left";
1585
+ }
1586
+ }
1587
+ function mapTextBaseline(baseline) {
1588
+ switch (baseline) {
1589
+ case "middle":
1590
+ case "central":
1591
+ return "middle";
1592
+ case "hanging":
1593
+ return "hanging";
1594
+ default:
1595
+ return "alphabetic";
1596
+ }
1597
+ }
1598
+ function createCanvasRenderer(theme) {
1599
+ const colors = theme.colors;
1600
+ let clipPaths = /* @__PURE__ */ new Map();
1601
+ return {
1602
+ createRoot(target, width, height, attrs) {
1603
+ const canvas = document.createElement("canvas");
1604
+ const dpr = window.devicePixelRatio || 1;
1605
+ canvas.width = Math.round(width * dpr);
1606
+ canvas.height = Math.round(height * dpr);
1607
+ canvas.style.width = `${width}px`;
1608
+ canvas.style.height = `${height}px`;
1609
+ canvas.style.display = "block";
1610
+ if (attrs?.class) canvas.className = attrs.class;
1611
+ if (attrs?.role) canvas.setAttribute("role", attrs.role);
1612
+ if (attrs?.ariaLabel) canvas.setAttribute("aria-label", attrs.ariaLabel);
1613
+ const ctx2d = canvas.getContext("2d", { alpha: false });
1614
+ ctx2d.setTransform(dpr, 0, 0, dpr, 0, 0);
1615
+ target.appendChild(canvas);
1616
+ const root = { element: canvas };
1617
+ root.ctx = ctx2d;
1618
+ root.width = width;
1619
+ root.height = height;
1620
+ root.dpr = dpr;
1621
+ return root;
1622
+ },
1623
+ render(root, nodes) {
1624
+ const cr = root;
1625
+ const canvas = cr.element;
1626
+ const dpr = window.devicePixelRatio || 1;
1627
+ cr.width = canvas.width / dpr;
1628
+ cr.height = canvas.height / dpr;
1629
+ cr.dpr = dpr;
1630
+ clipPaths = /* @__PURE__ */ new Map();
1631
+ const ctx = cr.ctx;
1632
+ ctx.imageSmoothingEnabled = true;
1633
+ ctx.imageSmoothingQuality = "high";
1634
+ ctx.save();
1635
+ ctx.fillStyle = resolveColor(theme.background, "#ffffff");
1636
+ ctx.fillRect(0, 0, cr.width, cr.height);
1637
+ ctx.restore();
1638
+ collectClipPaths(nodes);
1639
+ drawNodes(cr.ctx, nodes, cr.width, cr.height);
1640
+ },
1641
+ update(root, nodes) {
1642
+ this.render(root, nodes);
1643
+ },
1644
+ clear(root) {
1645
+ const cr = root;
1646
+ cr.ctx.clearRect(0, 0, cr.width, cr.height);
1647
+ },
1648
+ destroy(root) {
1649
+ root.element.remove();
1650
+ }
1651
+ };
1652
+ function collectClipPaths(nodes) {
1653
+ for (const node of nodes) {
1654
+ if (node.type === "defs") {
1655
+ for (const child of node.children) {
1656
+ if (child.type === "clipPath") {
1657
+ const path2 = new Path2D();
1658
+ for (const cp of child.children) {
1659
+ if (cp.type === "rect") {
1660
+ path2.rect(cp.x, cp.y, cp.width, cp.height);
1661
+ }
1662
+ }
1663
+ clipPaths.set(child.id, path2);
1664
+ }
1665
+ }
1666
+ } else if (node.type === "group") {
1667
+ collectClipPaths(node.children);
1668
+ }
1669
+ }
1670
+ }
1671
+ function drawNodes(ctx, nodes, cw, ch) {
1672
+ for (const node of nodes) {
1673
+ drawNode(ctx, node, cw, ch);
1674
+ }
1675
+ }
1676
+ function drawNode(ctx, node, cw, ch) {
1677
+ switch (node.type) {
1678
+ case "group": {
1679
+ ctx.save();
1680
+ applyClip(ctx, node.attrs);
1681
+ applyTransform(ctx, node.attrs);
1682
+ applyOpacity(ctx, node.attrs);
1683
+ drawNodes(ctx, node.children, cw, ch);
1684
+ ctx.restore();
1685
+ break;
1686
+ }
1687
+ case "path": {
1688
+ const fill = resolveFill(ctx, node.attrs?.fill, 0, 0, cw, ch);
1689
+ const stroke = resolveColor(node.attrs?.stroke);
1690
+ const strokeWidth = node.attrs?.strokeWidth ?? 0;
1691
+ const opacity = node.attrs?.opacity ?? 1;
1692
+ const fillOpacity = node.attrs?.fillOpacity ?? 1;
1693
+ const strokeOpacity = node.attrs?.strokeOpacity ?? 1;
1694
+ ctx.save();
1695
+ applyClip(ctx, node.attrs);
1696
+ ctx.globalAlpha = opacity;
1697
+ applyLineCaps(ctx, node.attrs);
1698
+ const p = new Path2D(node.d);
1699
+ if (fill && fill !== "transparent" && fill !== "none" && node.attrs?.fill) {
1700
+ ctx.save();
1701
+ ctx.globalAlpha = opacity * fillOpacity;
1702
+ ctx.fillStyle = typeof fill === "object" ? fill : fill;
1703
+ ctx.fill(p);
1704
+ ctx.restore();
1705
+ }
1706
+ if (stroke && stroke !== "transparent" && stroke !== "none" && strokeWidth > 0) {
1707
+ ctx.save();
1708
+ ctx.globalAlpha = opacity * strokeOpacity;
1709
+ ctx.strokeStyle = typeof stroke === "string" ? stroke : "#000";
1710
+ ctx.lineWidth = strokeWidth;
1711
+ applyDash(ctx, node.attrs?.strokeDasharray);
1712
+ ctx.stroke(p);
1713
+ ctx.restore();
1714
+ }
1715
+ ctx.restore();
1716
+ break;
1717
+ }
1718
+ case "rect": {
1719
+ const fill = resolveFill(ctx, node.attrs?.fill, node.x, node.y, node.width, node.height);
1720
+ const stroke = resolveColor(node.attrs?.stroke);
1721
+ const strokeWidth = node.attrs?.strokeWidth ?? 0;
1722
+ const opacity = node.attrs?.opacity ?? 1;
1723
+ const fillOpacity = node.attrs?.fillOpacity ?? 1;
1724
+ const strokeOpacity = node.attrs?.strokeOpacity ?? 1;
1725
+ const rx = node.rx ?? 0;
1726
+ ctx.save();
1727
+ applyClip(ctx, node.attrs);
1728
+ ctx.globalAlpha = opacity;
1729
+ if (fill && fill !== "transparent" && fill !== "none") {
1730
+ ctx.save();
1731
+ ctx.globalAlpha = opacity * fillOpacity;
1732
+ ctx.fillStyle = typeof fill === "object" ? fill : fill;
1733
+ if (rx > 0) {
1734
+ roundRect(ctx, node.x, node.y, node.width, node.height, rx);
1735
+ ctx.fill();
1736
+ } else {
1737
+ ctx.fillRect(node.x, node.y, node.width, node.height);
1738
+ }
1739
+ ctx.restore();
1740
+ }
1741
+ if (stroke && stroke !== "transparent" && stroke !== "none" && strokeWidth > 0) {
1742
+ ctx.save();
1743
+ ctx.globalAlpha = opacity * strokeOpacity;
1744
+ ctx.strokeStyle = typeof stroke === "string" ? stroke : "#000";
1745
+ ctx.lineWidth = strokeWidth;
1746
+ applyDash(ctx, node.attrs?.strokeDasharray);
1747
+ if (rx > 0) {
1748
+ roundRect(ctx, node.x, node.y, node.width, node.height, rx);
1749
+ ctx.stroke();
1750
+ } else {
1751
+ ctx.strokeRect(node.x, node.y, node.width, node.height);
1752
+ }
1753
+ ctx.restore();
1754
+ }
1755
+ ctx.restore();
1756
+ break;
1757
+ }
1758
+ case "circle": {
1759
+ const fill = resolveFill(ctx, node.attrs?.fill, node.cx - node.r, node.cy - node.r, node.r * 2, node.r * 2);
1760
+ const stroke = resolveColor(node.attrs?.stroke);
1761
+ const strokeWidth = node.attrs?.strokeWidth ?? 0;
1762
+ const opacity = node.attrs?.opacity ?? 1;
1763
+ const fillOpacity = node.attrs?.fillOpacity ?? 1;
1764
+ const strokeOpacity = node.attrs?.strokeOpacity ?? 1;
1765
+ ctx.save();
1766
+ ctx.globalAlpha = opacity;
1767
+ ctx.beginPath();
1768
+ ctx.arc(node.cx, node.cy, node.r, 0, Math.PI * 2);
1769
+ if (fill && fill !== "transparent" && fill !== "none") {
1770
+ ctx.save();
1771
+ ctx.globalAlpha = opacity * fillOpacity;
1772
+ ctx.fillStyle = typeof fill === "object" ? fill : fill;
1773
+ ctx.fill();
1774
+ ctx.restore();
1775
+ }
1776
+ if (stroke && stroke !== "transparent" && stroke !== "none" && strokeWidth > 0) {
1777
+ ctx.save();
1778
+ ctx.globalAlpha = opacity * strokeOpacity;
1779
+ ctx.strokeStyle = typeof stroke === "string" ? stroke : "#000";
1780
+ ctx.lineWidth = strokeWidth;
1781
+ ctx.stroke();
1782
+ ctx.restore();
1783
+ }
1784
+ ctx.restore();
1785
+ break;
1786
+ }
1787
+ case "line": {
1788
+ const stroke = resolveColor(node.attrs?.stroke, "#000");
1789
+ const strokeWidth = node.attrs?.strokeWidth ?? 1;
1790
+ const opacity = node.attrs?.opacity ?? 1;
1791
+ const strokeOpacity = node.attrs?.strokeOpacity ?? 1;
1792
+ if (stroke === "transparent" || stroke === "none") break;
1793
+ ctx.save();
1794
+ ctx.globalAlpha = opacity * strokeOpacity;
1795
+ ctx.strokeStyle = stroke;
1796
+ ctx.lineWidth = strokeWidth;
1797
+ applyLineCaps(ctx, node.attrs);
1798
+ applyDash(ctx, node.attrs?.strokeDasharray);
1799
+ const offset = strokeWidth % 2 === 1 ? 0.5 : 0;
1800
+ ctx.beginPath();
1801
+ ctx.moveTo(snapPixel(node.x1, offset), snapPixel(node.y1, offset));
1802
+ ctx.lineTo(snapPixel(node.x2, offset), snapPixel(node.y2, offset));
1803
+ ctx.stroke();
1804
+ ctx.setLineDash([]);
1805
+ ctx.restore();
1806
+ break;
1807
+ }
1808
+ case "text": {
1809
+ const fill = resolveColor(node.attrs?.fill, theme.textColor);
1810
+ const resolvedFill = resolveColor(fill);
1811
+ const opacity = node.attrs?.opacity ?? 1;
1812
+ const extra = node.attrs;
1813
+ const fontSize = extra?.fontSize ?? theme.fontSize;
1814
+ const fontFamily = extra?.fontFamily ?? theme.fontFamily;
1815
+ const fontWeight = extra?.fontWeight ?? "normal";
1816
+ const textAnchor = extra?.textAnchor;
1817
+ const baseline = extra?.dominantBaseline;
1818
+ ctx.save();
1819
+ ctx.globalAlpha = opacity;
1820
+ ctx.fillStyle = resolvedFill;
1821
+ ctx.font = `${fontWeight} ${fontSize}px ${fontFamily}`;
1822
+ ctx.textAlign = mapTextAlign(textAnchor);
1823
+ ctx.textBaseline = mapTextBaseline(baseline);
1824
+ applyTransform(ctx, node.attrs);
1825
+ ctx.fillText(node.content, node.x, node.y);
1826
+ ctx.restore();
1827
+ break;
1828
+ }
1829
+ }
1830
+ }
1831
+ function resolveFill(ctx, fill, x, y, w, h) {
1832
+ if (!fill) return "transparent";
1833
+ const urlId = parseUrlRef(fill);
1834
+ if (urlId) {
1835
+ const grad = resolveGradientRef(urlId, colors, ctx, x, y, w, h);
1836
+ return grad ?? resolveColor(fill);
1837
+ }
1838
+ return resolveColor(fill);
1839
+ }
1840
+ function applyClip(ctx, attrs) {
1841
+ if (!attrs?.clipPath) return;
1842
+ const clipId = attrs.clipPath;
1843
+ const path2 = clipPaths.get(clipId);
1844
+ if (path2) {
1845
+ ctx.clip(path2);
1846
+ }
1847
+ }
1848
+ function applyTransform(ctx, attrs) {
1849
+ if (!attrs?.transform) return;
1850
+ const t = attrs.transform;
1851
+ const translateMatch = t.match(/translate\(\s*([^,)]+)[,\s]+([^)]+)\)/);
1852
+ if (translateMatch) {
1853
+ ctx.translate(parseFloat(translateMatch[1]), parseFloat(translateMatch[2]));
1854
+ }
1855
+ const rotateMatch = t.match(/rotate\(\s*([^,)]+)(?:[,\s]+([^,)]+)[,\s]+([^)]+))?\)/);
1856
+ if (rotateMatch) {
1857
+ const angle = parseFloat(rotateMatch[1]) * Math.PI / 180;
1858
+ if (rotateMatch[2] && rotateMatch[3]) {
1859
+ const cx = parseFloat(rotateMatch[2]);
1860
+ const cy = parseFloat(rotateMatch[3]);
1861
+ ctx.translate(cx, cy);
1862
+ ctx.rotate(angle);
1863
+ ctx.translate(-cx, -cy);
1864
+ } else {
1865
+ ctx.rotate(angle);
1866
+ }
1867
+ }
1868
+ const scaleMatch = t.match(/scale\(\s*([^,)]+)(?:[,\s]+([^)]+))?\)/);
1869
+ if (scaleMatch) {
1870
+ const sx = parseFloat(scaleMatch[1]);
1871
+ const sy = scaleMatch[2] ? parseFloat(scaleMatch[2]) : sx;
1872
+ ctx.scale(sx, sy);
1873
+ }
1874
+ }
1875
+ function applyOpacity(ctx, attrs) {
1876
+ if (attrs?.opacity != null) {
1877
+ ctx.globalAlpha *= attrs.opacity;
1878
+ }
1879
+ }
1880
+ function applyLineCaps(ctx, attrs) {
1881
+ if (attrs?.strokeLinecap) ctx.lineCap = attrs.strokeLinecap;
1882
+ if (attrs?.strokeLinejoin) ctx.lineJoin = attrs.strokeLinejoin;
1883
+ }
1884
+ function applyDash(ctx, dasharray) {
1885
+ if (!dasharray) return;
1886
+ const segments = dasharray.split(/[\s,]+/).map(Number).filter((n2) => !isNaN(n2));
1887
+ if (segments.length > 0) {
1888
+ ctx.setLineDash(segments);
1889
+ }
1890
+ }
1891
+ function snapPixel(v, offset) {
1892
+ return Math.round(v) + offset;
1893
+ }
1894
+ function roundRect(ctx, x, y, w, h, r) {
1895
+ r = Math.min(r, w / 2, h / 2);
1896
+ ctx.beginPath();
1897
+ ctx.moveTo(x + r, y);
1898
+ ctx.lineTo(x + w - r, y);
1899
+ ctx.arcTo(x + w, y, x + w, y + r, r);
1900
+ ctx.lineTo(x + w, y + h - r);
1901
+ ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
1902
+ ctx.lineTo(x + r, y + h);
1903
+ ctx.arcTo(x, y + h, x, y + h - r, r);
1904
+ ctx.lineTo(x, y + r);
1905
+ ctx.arcTo(x, y, x + r, y, r);
1906
+ ctx.closePath();
1907
+ }
1908
+ }
1909
+
1910
+ // src/render/webgl.ts
1911
+ var LINE_VERT = `
1912
+ attribute vec2 a_position;
1913
+ attribute vec4 a_color;
1914
+ uniform vec2 u_resolution;
1915
+ varying vec4 v_color;
1916
+ void main() {
1917
+ vec2 clip = (a_position / u_resolution) * 2.0 - 1.0;
1918
+ gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);
1919
+ v_color = a_color;
1920
+ }
1921
+ `;
1922
+ var LINE_FRAG = `
1923
+ precision mediump float;
1924
+ varying vec4 v_color;
1925
+ void main() {
1926
+ gl_FragColor = v_color;
1927
+ }
1928
+ `;
1929
+ var POINT_VERT = `
1930
+ attribute vec2 a_position;
1931
+ attribute float a_radius;
1932
+ attribute vec4 a_color;
1933
+ uniform vec2 u_resolution;
1934
+ varying vec4 v_color;
1935
+ void main() {
1936
+ vec2 clip = (a_position / u_resolution) * 2.0 - 1.0;
1937
+ gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);
1938
+ gl_PointSize = a_radius * 2.0;
1939
+ v_color = a_color;
1940
+ }
1941
+ `;
1942
+ var POINT_FRAG = `
1943
+ precision mediump float;
1944
+ varying vec4 v_color;
1945
+ void main() {
1946
+ vec2 coord = gl_PointCoord - vec2(0.5);
1947
+ float dist = length(coord);
1948
+ if (dist > 0.5) discard;
1949
+ float alpha = 1.0 - smoothstep(0.4, 0.5, dist);
1950
+ gl_FragColor = vec4(v_color.rgb, v_color.a * alpha);
1951
+ }
1952
+ `;
1953
+ var RECT_VERT = `
1954
+ attribute vec2 a_position;
1955
+ attribute vec4 a_color;
1956
+ uniform vec2 u_resolution;
1957
+ varying vec4 v_color;
1958
+ void main() {
1959
+ vec2 clip = (a_position / u_resolution) * 2.0 - 1.0;
1960
+ gl_Position = vec4(clip.x, -clip.y, 0.0, 1.0);
1961
+ v_color = a_color;
1962
+ }
1963
+ `;
1964
+ var RECT_FRAG = `
1965
+ precision mediump float;
1966
+ varying vec4 v_color;
1967
+ void main() {
1968
+ gl_FragColor = v_color;
1969
+ }
1970
+ `;
1971
+ function resolveColor2(value, fallback = "#000") {
1972
+ if (!value) return fallback;
1973
+ if (value === "none" || value === "transparent") return "transparent";
1974
+ if (value.startsWith("url(")) return fallback;
1975
+ const match = value.match(/var\([^,]+,\s*([^)]+)\)/);
1976
+ return match ? match[1].trim() : value;
1977
+ }
1978
+ function hexToRGBA(hex, alpha = 1) {
1979
+ const h = hex.replace("#", "");
1980
+ const full = h.length === 3 ? h[0] + h[0] + h[1] + h[1] + h[2] + h[2] : h;
1981
+ return [
1982
+ parseInt(full.substring(0, 2), 16) / 255,
1983
+ parseInt(full.substring(2, 4), 16) / 255,
1984
+ parseInt(full.substring(4, 6), 16) / 255,
1985
+ alpha
1986
+ ];
1987
+ }
1988
+ function colorToRGBA(color, alpha = 1) {
1989
+ const resolved = resolveColor2(color);
1990
+ if (resolved === "transparent") return [0, 0, 0, 0];
1991
+ if (resolved.startsWith("#")) return hexToRGBA(resolved, alpha);
1992
+ const rgbaMatch = resolved.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*(?:,\s*([\d.]+))?\s*\)/);
1993
+ if (rgbaMatch) {
1994
+ return [
1995
+ parseInt(rgbaMatch[1]) / 255,
1996
+ parseInt(rgbaMatch[2]) / 255,
1997
+ parseInt(rgbaMatch[3]) / 255,
1998
+ rgbaMatch[4] ? parseFloat(rgbaMatch[4]) * alpha : alpha
1999
+ ];
2000
+ }
2001
+ return [0, 0, 0, alpha];
2002
+ }
2003
+ function createShader(gl, type, source) {
2004
+ const shader = gl.createShader(type);
2005
+ if (!shader) return null;
2006
+ gl.shaderSource(shader, source);
2007
+ gl.compileShader(shader);
2008
+ if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
2009
+ gl.deleteShader(shader);
2010
+ return null;
2011
+ }
2012
+ return shader;
2013
+ }
2014
+ function createProgram(gl, vertSrc, fragSrc) {
2015
+ const vert = createShader(gl, gl.VERTEX_SHADER, vertSrc);
2016
+ const frag = createShader(gl, gl.FRAGMENT_SHADER, fragSrc);
2017
+ if (!vert || !frag) return null;
2018
+ const program = gl.createProgram();
2019
+ if (!program) return null;
2020
+ gl.attachShader(program, vert);
2021
+ gl.attachShader(program, frag);
2022
+ gl.linkProgram(program);
2023
+ if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
2024
+ gl.deleteProgram(program);
2025
+ return null;
2026
+ }
2027
+ return program;
2028
+ }
2029
+ function mapTextAlign2(anchor) {
2030
+ switch (anchor) {
2031
+ case "middle":
2032
+ return "center";
2033
+ case "end":
2034
+ return "right";
2035
+ default:
2036
+ return "left";
2037
+ }
2038
+ }
2039
+ function mapTextBaseline2(baseline) {
2040
+ switch (baseline) {
2041
+ case "middle":
2042
+ case "central":
2043
+ return "middle";
2044
+ case "hanging":
2045
+ return "hanging";
2046
+ default:
2047
+ return "alphabetic";
2048
+ }
2049
+ }
2050
+ function createWebGLRenderer(theme) {
2051
+ return {
2052
+ createRoot(target, width, height, attrs) {
2053
+ const container = document.createElement("div");
2054
+ container.style.cssText = `position:relative;width:${width}px;height:${height}px;`;
2055
+ if (attrs?.class) container.className = attrs.class;
2056
+ if (attrs?.role) container.setAttribute("role", attrs.role);
2057
+ if (attrs?.ariaLabel) container.setAttribute("aria-label", attrs.ariaLabel);
2058
+ const dpr = window.devicePixelRatio || 1;
2059
+ const glCanvas = document.createElement("canvas");
2060
+ glCanvas.width = width * dpr;
2061
+ glCanvas.height = height * dpr;
2062
+ glCanvas.style.cssText = `position:absolute;top:0;left:0;width:${width}px;height:${height}px;`;
2063
+ container.appendChild(glCanvas);
2064
+ const gl = glCanvas.getContext("webgl", {
2065
+ alpha: true,
2066
+ antialias: true,
2067
+ premultipliedAlpha: false
2068
+ });
2069
+ const overlay = document.createElement("canvas");
2070
+ overlay.width = width * dpr;
2071
+ overlay.height = height * dpr;
2072
+ overlay.style.cssText = `position:absolute;top:0;left:0;width:${width}px;height:${height}px;`;
2073
+ container.appendChild(overlay);
2074
+ const ctx2d = overlay.getContext("2d");
2075
+ ctx2d.scale(dpr, dpr);
2076
+ const lineProgram = createProgram(gl, LINE_VERT, LINE_FRAG);
2077
+ const pointProgram = createProgram(gl, POINT_VERT, POINT_FRAG);
2078
+ const rectProgram = createProgram(gl, RECT_VERT, RECT_FRAG);
2079
+ gl.viewport(0, 0, width * dpr, height * dpr);
2080
+ gl.enable(gl.BLEND);
2081
+ gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
2082
+ target.appendChild(container);
2083
+ const webglRoot = {
2084
+ container,
2085
+ glCanvas,
2086
+ gl,
2087
+ overlay,
2088
+ ctx2d,
2089
+ width,
2090
+ height,
2091
+ dpr,
2092
+ programs: {
2093
+ line: lineProgram,
2094
+ point: pointProgram,
2095
+ rect: rectProgram
2096
+ }
2097
+ };
2098
+ container.__chartts_webgl = webglRoot;
2099
+ return { element: container };
2100
+ },
2101
+ render(root, nodes) {
2102
+ const wr = getWebGLRoot(root);
2103
+ if (!wr) return;
2104
+ const { gl, ctx2d, width, height, dpr } = wr;
2105
+ gl.viewport(0, 0, width * dpr, height * dpr);
2106
+ gl.clearColor(0, 0, 0, 0);
2107
+ gl.clear(gl.COLOR_BUFFER_BIT);
2108
+ ctx2d.setTransform(dpr, 0, 0, dpr, 0, 0);
2109
+ ctx2d.clearRect(0, 0, width, height);
2110
+ const lines = [];
2111
+ const points = [];
2112
+ const rects = [];
2113
+ collectGLNodes(nodes, lines, points, rects);
2114
+ drawGLLines(wr, lines);
2115
+ drawGLPoints(wr, points);
2116
+ drawGLRects(wr, rects);
2117
+ draw2DNodes(ctx2d, nodes, width, height, theme);
2118
+ },
2119
+ update(root, nodes) {
2120
+ this.render(root, nodes);
2121
+ },
2122
+ clear(root) {
2123
+ const wr = getWebGLRoot(root);
2124
+ if (!wr) return;
2125
+ wr.gl.clear(wr.gl.COLOR_BUFFER_BIT);
2126
+ wr.ctx2d.clearRect(0, 0, wr.width, wr.height);
2127
+ },
2128
+ destroy(root) {
2129
+ const el2 = root.element;
2130
+ el2.remove();
2131
+ }
2132
+ };
2133
+ }
2134
+ function getWebGLRoot(root) {
2135
+ const container = root.element;
2136
+ const glCanvas = container.querySelector("canvas:first-child");
2137
+ const overlay = container.querySelector("canvas:last-child");
2138
+ if (!glCanvas || !overlay) return null;
2139
+ const gl = glCanvas.getContext("webgl");
2140
+ const ctx2d = overlay.getContext("2d");
2141
+ if (!gl || !ctx2d) return null;
2142
+ const dpr = window.devicePixelRatio || 1;
2143
+ const width = parseInt(container.style.width) || 400;
2144
+ const height = parseInt(container.style.height) || 300;
2145
+ const lineProgram = createProgram(gl, LINE_VERT, LINE_FRAG);
2146
+ const pointProgram = createProgram(gl, POINT_VERT, POINT_FRAG);
2147
+ const rectProgram = createProgram(gl, RECT_VERT, RECT_FRAG);
2148
+ return {
2149
+ container,
2150
+ glCanvas,
2151
+ gl,
2152
+ overlay,
2153
+ ctx2d,
2154
+ width,
2155
+ height,
2156
+ dpr,
2157
+ programs: {
2158
+ line: lineProgram,
2159
+ point: pointProgram,
2160
+ rect: rectProgram
2161
+ }
2162
+ };
2163
+ }
2164
+ function collectGLNodes(nodes, lines, points, rects, _parentAttrs) {
2165
+ for (const node of nodes) {
2166
+ switch (node.type) {
2167
+ case "group":
2168
+ collectGLNodes(node.children, lines, points, rects, node.attrs);
2169
+ break;
2170
+ case "line": {
2171
+ const stroke = resolveColor2(node.attrs?.stroke, "#000");
2172
+ if (stroke === "transparent" || stroke === "none") break;
2173
+ const opacity = node.attrs?.opacity ?? 1;
2174
+ lines.push({
2175
+ x1: node.x1,
2176
+ y1: node.y1,
2177
+ x2: node.x2,
2178
+ y2: node.y2,
2179
+ color: colorToRGBA(stroke, opacity),
2180
+ width: node.attrs?.strokeWidth ?? 1
2181
+ });
2182
+ break;
2183
+ }
2184
+ case "circle": {
2185
+ const fill = resolveColor2(node.attrs?.fill);
2186
+ if (fill === "transparent" || fill === "none") break;
2187
+ const opacity = (node.attrs?.opacity ?? 1) * (node.attrs?.fillOpacity ?? 1);
2188
+ points.push({
2189
+ x: node.cx,
2190
+ y: node.cy,
2191
+ radius: node.r,
2192
+ color: colorToRGBA(fill, opacity)
2193
+ });
2194
+ break;
2195
+ }
2196
+ case "rect": {
2197
+ const fill = resolveColor2(node.attrs?.fill);
2198
+ if (fill === "transparent" || fill === "none") break;
2199
+ const opacity = (node.attrs?.opacity ?? 1) * (node.attrs?.fillOpacity ?? 1);
2200
+ rects.push({
2201
+ x: node.x,
2202
+ y: node.y,
2203
+ w: node.width,
2204
+ h: node.height,
2205
+ color: colorToRGBA(fill, opacity)
2206
+ });
2207
+ break;
2208
+ }
2209
+ case "path": {
2210
+ const stroke = resolveColor2(node.attrs?.stroke);
2211
+ if (stroke && stroke !== "transparent" && stroke !== "none") {
2212
+ const segs = pathToSegments(node.d);
2213
+ const opacity = node.attrs?.opacity ?? 1;
2214
+ const color = colorToRGBA(stroke, opacity);
2215
+ const width = node.attrs?.strokeWidth ?? 2;
2216
+ for (const seg of segs) {
2217
+ lines.push({ ...seg, color, width });
2218
+ }
2219
+ }
2220
+ break;
2221
+ }
2222
+ }
2223
+ }
2224
+ }
2225
+ function pathToSegments(d) {
2226
+ const segments = [];
2227
+ const commands = d.match(/[MLHVCSQTAZmlhvcsqtaz][^MLHVCSQTAZmlhvcsqtaz]*/g);
2228
+ if (!commands) return segments;
2229
+ let cx = 0, cy = 0;
2230
+ let startX = 0, startY = 0;
2231
+ for (const cmd of commands) {
2232
+ const type = cmd[0];
2233
+ const nums = (cmd.slice(1).match(/-?[\d.]+(?:e[+-]?\d+)?/gi) ?? []).map(Number);
2234
+ switch (type) {
2235
+ case "M":
2236
+ cx = nums[0] ?? 0;
2237
+ cy = nums[1] ?? 0;
2238
+ startX = cx;
2239
+ startY = cy;
2240
+ for (let i = 2; i < nums.length; i += 2) {
2241
+ const nx = nums[i] ?? 0;
2242
+ const ny = nums[i + 1] ?? 0;
2243
+ segments.push({ x1: cx, y1: cy, x2: nx, y2: ny });
2244
+ cx = nx;
2245
+ cy = ny;
2246
+ }
2247
+ break;
2248
+ case "m":
2249
+ cx += nums[0] ?? 0;
2250
+ cy += nums[1] ?? 0;
2251
+ startX = cx;
2252
+ startY = cy;
2253
+ for (let i = 2; i < nums.length; i += 2) {
2254
+ const nx = cx + (nums[i] ?? 0);
2255
+ const ny = cy + (nums[i + 1] ?? 0);
2256
+ segments.push({ x1: cx, y1: cy, x2: nx, y2: ny });
2257
+ cx = nx;
2258
+ cy = ny;
2259
+ }
2260
+ break;
2261
+ case "L":
2262
+ for (let i = 0; i < nums.length; i += 2) {
2263
+ const nx = nums[i] ?? 0;
2264
+ const ny = nums[i + 1] ?? 0;
2265
+ segments.push({ x1: cx, y1: cy, x2: nx, y2: ny });
2266
+ cx = nx;
2267
+ cy = ny;
2268
+ }
2269
+ break;
2270
+ case "l":
2271
+ for (let i = 0; i < nums.length; i += 2) {
2272
+ const nx = cx + (nums[i] ?? 0);
2273
+ const ny = cy + (nums[i + 1] ?? 0);
2274
+ segments.push({ x1: cx, y1: cy, x2: nx, y2: ny });
2275
+ cx = nx;
2276
+ cy = ny;
2277
+ }
2278
+ break;
2279
+ case "H":
2280
+ for (const n2 of nums) {
2281
+ segments.push({ x1: cx, y1: cy, x2: n2, y2: cy });
2282
+ cx = n2;
2283
+ }
2284
+ break;
2285
+ case "h":
2286
+ for (const n2 of nums) {
2287
+ const nx = cx + n2;
2288
+ segments.push({ x1: cx, y1: cy, x2: nx, y2: cy });
2289
+ cx = nx;
2290
+ }
2291
+ break;
2292
+ case "V":
2293
+ for (const n2 of nums) {
2294
+ segments.push({ x1: cx, y1: cy, x2: cx, y2: n2 });
2295
+ cy = n2;
2296
+ }
2297
+ break;
2298
+ case "v":
2299
+ for (const n2 of nums) {
2300
+ const ny = cy + n2;
2301
+ segments.push({ x1: cx, y1: cy, x2: cx, y2: ny });
2302
+ cy = ny;
2303
+ }
2304
+ break;
2305
+ case "C": {
2306
+ for (let i = 0; i < nums.length; i += 6) {
2307
+ const cp1x = nums[i], cp1y = nums[i + 1];
2308
+ const cp2x = nums[i + 2], cp2y = nums[i + 3];
2309
+ const ex = nums[i + 4], ey = nums[i + 5];
2310
+ approximateCubic(segments, cx, cy, cp1x, cp1y, cp2x, cp2y, ex, ey);
2311
+ cx = ex;
2312
+ cy = ey;
2313
+ }
2314
+ break;
2315
+ }
2316
+ case "c": {
2317
+ for (let i = 0; i < nums.length; i += 6) {
2318
+ const cp1x = cx + nums[i], cp1y = cy + nums[i + 1];
2319
+ const cp2x = cx + nums[i + 2], cp2y = cy + nums[i + 3];
2320
+ const ex = cx + nums[i + 4], ey = cy + nums[i + 5];
2321
+ approximateCubic(segments, cx, cy, cp1x, cp1y, cp2x, cp2y, ex, ey);
2322
+ cx = ex;
2323
+ cy = ey;
2324
+ }
2325
+ break;
2326
+ }
2327
+ case "S": {
2328
+ for (let i = 0; i < nums.length; i += 4) {
2329
+ const cp2x = nums[i], cp2y = nums[i + 1];
2330
+ const ex = nums[i + 2], ey = nums[i + 3];
2331
+ approximateCubic(segments, cx, cy, cx, cy, cp2x, cp2y, ex, ey);
2332
+ cx = ex;
2333
+ cy = ey;
2334
+ }
2335
+ break;
2336
+ }
2337
+ case "Q": {
2338
+ for (let i = 0; i < nums.length; i += 4) {
2339
+ const cpx = nums[i], cpy = nums[i + 1];
2340
+ const ex = nums[i + 2], ey = nums[i + 3];
2341
+ approximateQuadratic(segments, cx, cy, cpx, cpy, ex, ey);
2342
+ cx = ex;
2343
+ cy = ey;
2344
+ }
2345
+ break;
2346
+ }
2347
+ case "Z":
2348
+ case "z":
2349
+ if (cx !== startX || cy !== startY) {
2350
+ segments.push({ x1: cx, y1: cy, x2: startX, y2: startY });
2351
+ }
2352
+ cx = startX;
2353
+ cy = startY;
2354
+ break;
2355
+ // A (arc) — simplified: just connect endpoints
2356
+ case "A":
2357
+ if (nums.length >= 7) {
2358
+ const ex = nums[5], ey = nums[6];
2359
+ segments.push({ x1: cx, y1: cy, x2: ex, y2: ey });
2360
+ cx = ex;
2361
+ cy = ey;
2362
+ }
2363
+ break;
2364
+ }
2365
+ }
2366
+ return segments;
2367
+ }
2368
+ function approximateCubic(out, x0, y0, cp1x, cp1y, cp2x, cp2y, x3, y3, steps = 8) {
2369
+ let px = x0, py = y0;
2370
+ for (let i = 1; i <= steps; i++) {
2371
+ const t = i / steps;
2372
+ const t2 = t * t;
2373
+ const t3 = t2 * t;
2374
+ const mt = 1 - t;
2375
+ const mt2 = mt * mt;
2376
+ const mt3 = mt2 * mt;
2377
+ const nx = mt3 * x0 + 3 * mt2 * t * cp1x + 3 * mt * t2 * cp2x + t3 * x3;
2378
+ const ny = mt3 * y0 + 3 * mt2 * t * cp1y + 3 * mt * t2 * cp2y + t3 * y3;
2379
+ out.push({ x1: px, y1: py, x2: nx, y2: ny });
2380
+ px = nx;
2381
+ py = ny;
2382
+ }
2383
+ }
2384
+ function approximateQuadratic(out, x0, y0, cpx, cpy, x2, y2, steps = 6) {
2385
+ let px = x0, py = y0;
2386
+ for (let i = 1; i <= steps; i++) {
2387
+ const t = i / steps;
2388
+ const mt = 1 - t;
2389
+ const nx = mt * mt * x0 + 2 * mt * t * cpx + t * t * x2;
2390
+ const ny = mt * mt * y0 + 2 * mt * t * cpy + t * t * y2;
2391
+ out.push({ x1: px, y1: py, x2: nx, y2: ny });
2392
+ px = nx;
2393
+ py = ny;
2394
+ }
2395
+ }
2396
+ function drawGLLines(wr, lines) {
2397
+ if (lines.length === 0) return;
2398
+ const { gl, programs, width, height, dpr } = wr;
2399
+ gl.useProgram(programs.line);
2400
+ const posLoc = gl.getAttribLocation(programs.line, "a_position");
2401
+ const colorLoc = gl.getAttribLocation(programs.line, "a_color");
2402
+ const resLoc = gl.getUniformLocation(programs.line, "u_resolution");
2403
+ gl.uniform2f(resLoc, width, height);
2404
+ const data = new Float32Array(lines.length * 12);
2405
+ for (let i = 0; i < lines.length; i++) {
2406
+ const l = lines[i];
2407
+ const off = i * 12;
2408
+ data[off] = l.x1;
2409
+ data[off + 1] = l.y1;
2410
+ data[off + 2] = l.color[0];
2411
+ data[off + 3] = l.color[1];
2412
+ data[off + 4] = l.color[2];
2413
+ data[off + 5] = l.color[3];
2414
+ data[off + 6] = l.x2;
2415
+ data[off + 7] = l.y2;
2416
+ data[off + 8] = l.color[0];
2417
+ data[off + 9] = l.color[1];
2418
+ data[off + 10] = l.color[2];
2419
+ data[off + 11] = l.color[3];
2420
+ }
2421
+ const buf = gl.createBuffer();
2422
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
2423
+ gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
2424
+ const stride = 24;
2425
+ gl.enableVertexAttribArray(posLoc);
2426
+ gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, stride, 0);
2427
+ gl.enableVertexAttribArray(colorLoc);
2428
+ gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, stride, 8);
2429
+ const maxWidth = lines.reduce((m, l) => Math.max(m, l.width), 1);
2430
+ gl.lineWidth(Math.min(maxWidth * dpr, gl.getParameter(gl.ALIASED_LINE_WIDTH_RANGE)[1]));
2431
+ gl.drawArrays(gl.LINES, 0, lines.length * 2);
2432
+ gl.disableVertexAttribArray(posLoc);
2433
+ gl.disableVertexAttribArray(colorLoc);
2434
+ gl.deleteBuffer(buf);
2435
+ }
2436
+ function drawGLPoints(wr, points) {
2437
+ if (points.length === 0) return;
2438
+ const { gl, programs, width, height, dpr } = wr;
2439
+ gl.useProgram(programs.point);
2440
+ const posLoc = gl.getAttribLocation(programs.point, "a_position");
2441
+ const radiusLoc = gl.getAttribLocation(programs.point, "a_radius");
2442
+ const colorLoc = gl.getAttribLocation(programs.point, "a_color");
2443
+ const resLoc = gl.getUniformLocation(programs.point, "u_resolution");
2444
+ gl.uniform2f(resLoc, width, height);
2445
+ const data = new Float32Array(points.length * 7);
2446
+ for (let i = 0; i < points.length; i++) {
2447
+ const p = points[i];
2448
+ const off = i * 7;
2449
+ data[off] = p.x;
2450
+ data[off + 1] = p.y;
2451
+ data[off + 2] = p.radius * dpr;
2452
+ data[off + 3] = p.color[0];
2453
+ data[off + 4] = p.color[1];
2454
+ data[off + 5] = p.color[2];
2455
+ data[off + 6] = p.color[3];
2456
+ }
2457
+ const buf = gl.createBuffer();
2458
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
2459
+ gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
2460
+ const stride = 28;
2461
+ gl.enableVertexAttribArray(posLoc);
2462
+ gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, stride, 0);
2463
+ gl.enableVertexAttribArray(radiusLoc);
2464
+ gl.vertexAttribPointer(radiusLoc, 1, gl.FLOAT, false, stride, 8);
2465
+ gl.enableVertexAttribArray(colorLoc);
2466
+ gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, stride, 12);
2467
+ gl.drawArrays(gl.POINTS, 0, points.length);
2468
+ gl.disableVertexAttribArray(posLoc);
2469
+ gl.disableVertexAttribArray(radiusLoc);
2470
+ gl.disableVertexAttribArray(colorLoc);
2471
+ gl.deleteBuffer(buf);
2472
+ }
2473
+ function drawGLRects(wr, rects) {
2474
+ if (rects.length === 0) return;
2475
+ const { gl, programs, width, height } = wr;
2476
+ gl.useProgram(programs.rect);
2477
+ const posLoc = gl.getAttribLocation(programs.rect, "a_position");
2478
+ const colorLoc = gl.getAttribLocation(programs.rect, "a_color");
2479
+ const resLoc = gl.getUniformLocation(programs.rect, "u_resolution");
2480
+ gl.uniform2f(resLoc, width, height);
2481
+ const data = new Float32Array(rects.length * 36);
2482
+ for (let i = 0; i < rects.length; i++) {
2483
+ const r = rects[i];
2484
+ const off = i * 36;
2485
+ const x0 = r.x, y0 = r.y, x1 = r.x + r.w, y1 = r.y + r.h;
2486
+ const c = r.color;
2487
+ data[off] = x0;
2488
+ data[off + 1] = y0;
2489
+ data[off + 2] = c[0];
2490
+ data[off + 3] = c[1];
2491
+ data[off + 4] = c[2];
2492
+ data[off + 5] = c[3];
2493
+ data[off + 6] = x1;
2494
+ data[off + 7] = y0;
2495
+ data[off + 8] = c[0];
2496
+ data[off + 9] = c[1];
2497
+ data[off + 10] = c[2];
2498
+ data[off + 11] = c[3];
2499
+ data[off + 12] = x0;
2500
+ data[off + 13] = y1;
2501
+ data[off + 14] = c[0];
2502
+ data[off + 15] = c[1];
2503
+ data[off + 16] = c[2];
2504
+ data[off + 17] = c[3];
2505
+ data[off + 18] = x1;
2506
+ data[off + 19] = y0;
2507
+ data[off + 20] = c[0];
2508
+ data[off + 21] = c[1];
2509
+ data[off + 22] = c[2];
2510
+ data[off + 23] = c[3];
2511
+ data[off + 24] = x1;
2512
+ data[off + 25] = y1;
2513
+ data[off + 26] = c[0];
2514
+ data[off + 27] = c[1];
2515
+ data[off + 28] = c[2];
2516
+ data[off + 29] = c[3];
2517
+ data[off + 30] = x0;
2518
+ data[off + 31] = y1;
2519
+ data[off + 32] = c[0];
2520
+ data[off + 33] = c[1];
2521
+ data[off + 34] = c[2];
2522
+ data[off + 35] = c[3];
2523
+ }
2524
+ const buf = gl.createBuffer();
2525
+ gl.bindBuffer(gl.ARRAY_BUFFER, buf);
2526
+ gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW);
2527
+ const stride = 24;
2528
+ gl.enableVertexAttribArray(posLoc);
2529
+ gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, stride, 0);
2530
+ gl.enableVertexAttribArray(colorLoc);
2531
+ gl.vertexAttribPointer(colorLoc, 4, gl.FLOAT, false, stride, 8);
2532
+ gl.drawArrays(gl.TRIANGLES, 0, rects.length * 6);
2533
+ gl.disableVertexAttribArray(posLoc);
2534
+ gl.disableVertexAttribArray(colorLoc);
2535
+ gl.deleteBuffer(buf);
2536
+ }
2537
+ function draw2DNodes(ctx, nodes, _cw, _ch, theme) {
2538
+ for (const node of nodes) {
2539
+ draw2DNode(ctx, node, theme);
2540
+ }
2541
+ }
2542
+ function draw2DNode(ctx, node, theme) {
2543
+ switch (node.type) {
2544
+ case "group":
2545
+ ctx.save();
2546
+ if (node.attrs?.transform) apply2DTransform(ctx, node.attrs.transform);
2547
+ if (node.attrs?.opacity != null) ctx.globalAlpha *= node.attrs.opacity;
2548
+ for (const child of node.children) draw2DNode(ctx, child, theme);
2549
+ ctx.restore();
2550
+ break;
2551
+ case "text": {
2552
+ const fill = resolveColor2(node.attrs?.fill, resolveColor2(theme.textColor));
2553
+ const opacity = node.attrs?.opacity ?? 1;
2554
+ const fontSize = node.attrs?.fontSize ?? theme.fontSize;
2555
+ const fontFamily = node.attrs?.fontFamily ?? theme.fontFamily;
2556
+ const fontWeight = node.attrs?.fontWeight ?? "normal";
2557
+ const textAnchor = node.attrs?.textAnchor;
2558
+ const baseline = node.attrs?.dominantBaseline;
2559
+ ctx.save();
2560
+ ctx.globalAlpha = opacity;
2561
+ ctx.fillStyle = resolveColor2(fill);
2562
+ ctx.font = `${fontWeight} ${fontSize}px ${resolveColor2(fontFamily)}`;
2563
+ ctx.textAlign = mapTextAlign2(textAnchor);
2564
+ ctx.textBaseline = mapTextBaseline2(baseline);
2565
+ if (node.attrs?.transform) apply2DTransform(ctx, node.attrs.transform);
2566
+ ctx.fillText(node.content, node.x, node.y);
2567
+ ctx.restore();
2568
+ break;
2569
+ }
2570
+ case "path": {
2571
+ const fill = resolveColor2(node.attrs?.fill);
2572
+ if (fill && fill !== "transparent" && fill !== "none" && node.attrs?.fill) {
2573
+ const opacity = (node.attrs?.opacity ?? 1) * (node.attrs?.fillOpacity ?? 1);
2574
+ ctx.save();
2575
+ ctx.globalAlpha = opacity;
2576
+ ctx.fillStyle = fill;
2577
+ const p = new Path2D(node.d);
2578
+ ctx.fill(p);
2579
+ ctx.restore();
2580
+ }
2581
+ break;
2582
+ }
2583
+ }
2584
+ }
2585
+ function apply2DTransform(ctx, t) {
2586
+ const translateMatch = t.match(/translate\(\s*([^,)]+)[,\s]+([^)]+)\)/);
2587
+ if (translateMatch) {
2588
+ ctx.translate(parseFloat(translateMatch[1]), parseFloat(translateMatch[2]));
2589
+ }
2590
+ const rotateMatch = t.match(/rotate\(\s*([^,)]+)(?:[,\s]+([^,)]+)[,\s]+([^)]+))?\)/);
2591
+ if (rotateMatch) {
2592
+ const angle = parseFloat(rotateMatch[1]) * Math.PI / 180;
2593
+ if (rotateMatch[2] && rotateMatch[3]) {
2594
+ const cx = parseFloat(rotateMatch[2]);
2595
+ const cy = parseFloat(rotateMatch[3]);
2596
+ ctx.translate(cx, cy);
2597
+ ctx.rotate(angle);
2598
+ ctx.translate(-cx, -cy);
2599
+ } else {
2600
+ ctx.rotate(angle);
2601
+ }
2602
+ }
2603
+ }
2604
+
2605
+ // src/scales/nice.ts
2606
+ function niceNumber(value, round) {
2607
+ if (value === 0) return 0;
2608
+ const exp = Math.floor(Math.log10(Math.abs(value)));
2609
+ const frac = value / Math.pow(10, exp);
2610
+ let nice;
2611
+ if (round) {
2612
+ nice = frac < 1.5 ? 1 : frac < 3 ? 2 : frac < 7 ? 5 : 10;
2613
+ } else {
2614
+ nice = frac <= 1 ? 1 : frac <= 2 ? 2 : frac <= 5 ? 5 : 10;
2615
+ }
2616
+ return nice * Math.pow(10, exp);
2617
+ }
2618
+ function niceRange(dataMin, dataMax, tickCount) {
2619
+ if (tickCount < 2) tickCount = 2;
2620
+ if (dataMin === dataMax) {
2621
+ if (dataMin === 0) return { min: 0, max: 1, spacing: 0.2 };
2622
+ const pad = Math.abs(dataMin) * 0.1 || 1;
2623
+ return niceRange(dataMin - pad, dataMax + pad, tickCount);
2624
+ }
2625
+ const range = niceNumber(dataMax - dataMin, false);
2626
+ const spacing = niceNumber(range / (tickCount - 1), true);
2627
+ const min = Math.floor(dataMin / spacing) * spacing;
2628
+ const max = Math.ceil(dataMax / spacing) * spacing;
2629
+ return { min, max, spacing };
2630
+ }
2631
+ function generateTicks(min, max, spacing) {
2632
+ if (spacing <= 0) return [min];
2633
+ const ticks = [];
2634
+ const eps = spacing * 1e-10;
2635
+ for (let v = min; v <= max + eps; v += spacing) {
2636
+ ticks.push(Math.round(v * 1e12) / 1e12);
2637
+ }
2638
+ return ticks;
2639
+ }
2640
+
2641
+ // src/scales/linear.ts
2642
+ function createLinearScale(opts) {
2643
+ let dMin = opts?.domain?.[0] ?? 0;
2644
+ let dMax = opts?.domain?.[1] ?? 1;
2645
+ let rMin = opts?.range?.[0] ?? 0;
2646
+ let rMax = opts?.range?.[1] ?? 1;
2647
+ const useNice = opts?.nice ?? true;
2648
+ const clamp = opts?.clamp ?? false;
2649
+ const fmt = opts?.format ?? defaultFmt;
2650
+ if (useNice && dMin !== dMax) {
2651
+ const { min, max } = niceRange(dMin, dMax, 5);
2652
+ dMin = min;
2653
+ dMax = max;
2654
+ }
2655
+ function map(value) {
2656
+ const v = Number(value);
2657
+ const span = dMax - dMin;
2658
+ if (span === 0) return (rMin + rMax) / 2;
2659
+ let t = (v - dMin) / span;
2660
+ if (clamp) t = Math.max(0, Math.min(1, t));
2661
+ return round2(rMin + t * (rMax - rMin));
2662
+ }
2663
+ function invert(px) {
2664
+ const span = rMax - rMin;
2665
+ if (span === 0) return (dMin + dMax) / 2;
2666
+ return dMin + (px - rMin) / span * (dMax - dMin);
2667
+ }
2668
+ function ticks(count = 5) {
2669
+ const { min, max, spacing } = niceRange(dMin, dMax, count);
2670
+ const vals = generateTicks(useNice ? min : dMin, useNice ? max : dMax, spacing);
2671
+ return vals.map((v) => ({ value: v, position: map(v), label: fmt(v) }));
2672
+ }
2673
+ return {
2674
+ map,
2675
+ invert,
2676
+ ticks,
2677
+ setDomain(min, max) {
2678
+ dMin = Number(min);
2679
+ dMax = Number(max);
2680
+ },
2681
+ setRange(min, max) {
2682
+ rMin = min;
2683
+ rMax = max;
2684
+ },
2685
+ getDomain() {
2686
+ return [dMin, dMax];
2687
+ },
2688
+ getRange() {
2689
+ return [rMin, rMax];
2690
+ },
2691
+ bandwidth() {
2692
+ return 0;
2693
+ }
2694
+ };
2695
+ }
2696
+ function round2(n2) {
2697
+ return Math.round(n2 * 100) / 100;
2698
+ }
2699
+ function defaultFmt(v) {
2700
+ const abs = Math.abs(v);
2701
+ if (abs >= 1e12) return trimSuffix(v / 1e12, "T");
2702
+ if (abs >= 1e9) return trimSuffix(v / 1e9, "B");
2703
+ if (abs >= 1e6) return trimSuffix(v / 1e6, "M");
2704
+ if (abs >= 1e3) return trimSuffix(v / 1e3, "K");
2705
+ if (Number.isInteger(v)) return String(v);
2706
+ return v.toFixed(1);
2707
+ }
2708
+ function trimSuffix(v, suffix) {
2709
+ const s = v.toFixed(1);
2710
+ return (s.endsWith(".0") ? s.slice(0, -2) : s) + suffix;
2711
+ }
2712
+
2713
+ // src/scales/categorical.ts
2714
+ function createCategoricalScale(opts) {
2715
+ let cats = opts?.categories ?? [];
2716
+ let rMin = opts?.range?.[0] ?? 0;
2717
+ let rMax = opts?.range?.[1] ?? 1;
2718
+ const fmt = opts?.format ?? String;
2719
+ const useBand = opts?.band ?? false;
2720
+ function round22(n2) {
2721
+ return Math.round(n2 * 100) / 100;
2722
+ }
2723
+ function pos(index) {
2724
+ const n2 = cats.length;
2725
+ if (n2 === 0) return round22((rMin + rMax) / 2);
2726
+ if (useBand) {
2727
+ const slotWidth = (rMax - rMin) / n2;
2728
+ return round22(rMin + (index + 0.5) * slotWidth);
2729
+ }
2730
+ if (n2 <= 1) return round22((rMin + rMax) / 2);
2731
+ return round22(rMin + index / (n2 - 1) * (rMax - rMin));
2732
+ }
2733
+ function bandwidth() {
2734
+ const n2 = cats.length;
2735
+ if (n2 === 0) return 0;
2736
+ if (useBand) return (rMax - rMin) / n2;
2737
+ if (n2 <= 1) return rMax - rMin;
2738
+ return (rMax - rMin) / (n2 - 1);
2739
+ }
2740
+ function map(value) {
2741
+ if (typeof value === "number" && Number.isFinite(value)) return pos(value);
2742
+ const idx = cats.findIndex(
2743
+ (c) => c instanceof Date && value instanceof Date ? c.getTime() === value.getTime() : String(c) === String(value)
2744
+ );
2745
+ return idx === -1 ? (rMin + rMax) / 2 : pos(idx);
2746
+ }
2747
+ function invert(px) {
2748
+ const n2 = cats.length;
2749
+ if (n2 === 0) return 0;
2750
+ if (useBand) {
2751
+ const slotWidth = (rMax - rMin) / n2;
2752
+ return Math.floor((px - rMin) / slotWidth);
2753
+ }
2754
+ if (n2 <= 1) return 0;
2755
+ const t = (px - rMin) / (rMax - rMin);
2756
+ return Math.round(t * (n2 - 1));
2757
+ }
2758
+ function ticks() {
2759
+ return cats.map((c, i) => ({ value: c, position: pos(i), label: fmt(c) }));
2760
+ }
2761
+ return {
2762
+ map,
2763
+ invert,
2764
+ ticks,
2765
+ setDomain() {
2766
+ },
2767
+ setRange(min, max) {
2768
+ rMin = min;
2769
+ rMax = max;
2770
+ },
2771
+ getDomain() {
2772
+ return [cats[0] ?? "", cats[cats.length - 1] ?? ""];
2773
+ },
2774
+ getRange() {
2775
+ return [rMin, rMax];
2776
+ },
2777
+ setCategories(c) {
2778
+ cats = c;
2779
+ },
2780
+ bandwidth
2781
+ };
2782
+ }
2783
+
2784
+ // src/interaction/zoom-pan.ts
2785
+ function createZoomPan(config, onUpdate, interactionState) {
2786
+ const cfg = {
2787
+ x: config.x ?? true,
2788
+ y: config.y ?? false,
2789
+ wheel: config.wheel ?? true,
2790
+ drag: config.drag ?? true,
2791
+ pinch: config.pinch ?? true,
2792
+ minZoom: config.minZoom ?? 1,
2793
+ maxZoom: config.maxZoom ?? 20
2794
+ };
2795
+ const state = {
2796
+ zoomX: 1,
2797
+ zoomY: 1,
2798
+ panX: 0,
2799
+ panY: 0
2800
+ };
2801
+ let el2 = null;
2802
+ let getArea = null;
2803
+ let isDragging = false;
2804
+ let dragStartX = 0;
2805
+ let dragStartY = 0;
2806
+ let dragStartPanX = 0;
2807
+ let dragStartPanY = 0;
2808
+ let lastPinchDist = 0;
2809
+ function onWheel(e) {
2810
+ if (!cfg.wheel || !getArea) return;
2811
+ e.preventDefault();
2812
+ const area = getArea();
2813
+ const rect2 = el2.getBoundingClientRect();
2814
+ const mouseX = e.clientX - rect2.left;
2815
+ const relX = (mouseX - area.x) / area.width;
2816
+ const delta = e.deltaY > 0 ? 0.9 : 1.1;
2817
+ if (cfg.x) {
2818
+ const newZoom = clamp(state.zoomX * delta, cfg.minZoom, cfg.maxZoom);
2819
+ const zoomRatio = newZoom / state.zoomX;
2820
+ state.panX = relX - (relX - state.panX) * zoomRatio;
2821
+ state.zoomX = newZoom;
2822
+ }
2823
+ if (cfg.y) {
2824
+ state.zoomY = clamp(state.zoomY * delta, cfg.minZoom, cfg.maxZoom);
2825
+ }
2826
+ clampPan();
2827
+ onUpdate();
2828
+ }
2829
+ function onPointerDown(e) {
2830
+ if (!cfg.drag) return;
2831
+ if (e.shiftKey) return;
2832
+ isDragging = true;
2833
+ if (interactionState) interactionState.isPanning = true;
2834
+ dragStartX = e.clientX;
2835
+ dragStartY = e.clientY;
2836
+ dragStartPanX = state.panX;
2837
+ dragStartPanY = state.panY;
2838
+ el2.setPointerCapture(e.pointerId);
2839
+ el2.style.cursor = "grabbing";
2840
+ }
2841
+ function onPointerMove(e) {
2842
+ if (!isDragging || !getArea) return;
2843
+ const area = getArea();
2844
+ const dx = e.clientX - dragStartX;
2845
+ const dy = e.clientY - dragStartY;
2846
+ if (cfg.x) {
2847
+ state.panX = dragStartPanX + dx / (area.width * state.zoomX);
2848
+ }
2849
+ if (cfg.y) {
2850
+ state.panY = dragStartPanY + dy / (area.height * state.zoomY);
2851
+ }
2852
+ clampPan();
2853
+ onUpdate();
2854
+ }
2855
+ function onPointerUp(e) {
2856
+ if (!isDragging) return;
2857
+ isDragging = false;
2858
+ if (interactionState) interactionState.isPanning = false;
2859
+ el2.releasePointerCapture(e.pointerId);
2860
+ el2.style.cursor = "crosshair";
2861
+ }
2862
+ function onTouchStart(e) {
2863
+ if (!cfg.pinch || e.touches.length !== 2) return;
2864
+ lastPinchDist = pinchDistance(e);
2865
+ }
2866
+ function onTouchMove(e) {
2867
+ if (!cfg.pinch || e.touches.length !== 2) return;
2868
+ e.preventDefault();
2869
+ const dist = pinchDistance(e);
2870
+ const scale = dist / lastPinchDist;
2871
+ lastPinchDist = dist;
2872
+ if (cfg.x) {
2873
+ state.zoomX = clamp(state.zoomX * scale, cfg.minZoom, cfg.maxZoom);
2874
+ }
2875
+ if (cfg.y) {
2876
+ state.zoomY = clamp(state.zoomY * scale, cfg.minZoom, cfg.maxZoom);
2877
+ }
2878
+ clampPan();
2879
+ onUpdate();
2880
+ }
2881
+ function clampPan() {
2882
+ const visibleX = 1 / state.zoomX;
2883
+ state.panX = clamp(state.panX, -(1 - visibleX), 0);
2884
+ const visibleY = 1 / state.zoomY;
2885
+ state.panY = clamp(state.panY, -(1 - visibleY), 0);
2886
+ }
2887
+ function pinchDistance(e) {
2888
+ const dx = e.touches[0].clientX - e.touches[1].clientX;
2889
+ const dy = e.touches[0].clientY - e.touches[1].clientY;
2890
+ return Math.sqrt(dx * dx + dy * dy);
2891
+ }
2892
+ function clamp(v, min, max) {
2893
+ return Math.min(Math.max(v, min), max);
2894
+ }
2895
+ return {
2896
+ attach(element, areaFn, _scalesFn) {
2897
+ el2 = element;
2898
+ getArea = areaFn;
2899
+ if (cfg.wheel) {
2900
+ el2.addEventListener("wheel", onWheel, { passive: false });
2901
+ }
2902
+ if (cfg.drag) {
2903
+ el2.addEventListener("pointerdown", onPointerDown);
2904
+ el2.addEventListener("pointermove", onPointerMove);
2905
+ el2.addEventListener("pointerup", onPointerUp);
2906
+ }
2907
+ if (cfg.pinch) {
2908
+ el2.addEventListener("touchstart", onTouchStart, { passive: false });
2909
+ el2.addEventListener("touchmove", onTouchMove, { passive: false });
2910
+ }
2911
+ },
2912
+ reset() {
2913
+ state.zoomX = 1;
2914
+ state.zoomY = 1;
2915
+ state.panX = 0;
2916
+ state.panY = 0;
2917
+ onUpdate();
2918
+ },
2919
+ getState() {
2920
+ return { ...state };
2921
+ },
2922
+ /**
2923
+ * Apply current zoom/pan to scales by adjusting their domains.
2924
+ * Call this at the start of each render, after creating scales.
2925
+ */
2926
+ applyToScales(xScale, yScale, area) {
2927
+ if (state.zoomX === 1 && state.zoomY === 1 && state.panX === 0 && state.panY === 0) return;
2928
+ if (cfg.x && state.zoomX !== 1) {
2929
+ const [xMin, xMax] = xScale.getRange();
2930
+ const fullWidth = xMax - xMin;
2931
+ const visibleWidth = fullWidth / state.zoomX;
2932
+ const offset = -state.panX * fullWidth;
2933
+ xScale.setRange(area.x + offset, area.x + offset + visibleWidth);
2934
+ }
2935
+ if (cfg.y && state.zoomY !== 1) {
2936
+ const [yMin, yMax] = yScale.getDomain();
2937
+ const fullRange = yMax - yMin;
2938
+ const visibleRange = fullRange / state.zoomY;
2939
+ const offset = -state.panY * fullRange;
2940
+ yScale.setDomain(yMin + offset, yMin + offset + visibleRange);
2941
+ }
2942
+ },
2943
+ destroy() {
2944
+ if (!el2) return;
2945
+ el2.removeEventListener("wheel", onWheel);
2946
+ el2.removeEventListener("pointerdown", onPointerDown);
2947
+ el2.removeEventListener("pointermove", onPointerMove);
2948
+ el2.removeEventListener("pointerup", onPointerUp);
2949
+ el2.removeEventListener("touchstart", onTouchStart);
2950
+ el2.removeEventListener("touchmove", onTouchMove);
2951
+ el2 = null;
2952
+ getArea = null;
2953
+ }
2954
+ };
2955
+ }
2956
+
2957
+ // src/interaction/brush.ts
2958
+ function createBrush(config, bus, el2, getArea, getXScale, getData, isPanEnabled) {
2959
+ const fillColor = config.fillColor ?? "rgba(59,130,246,0.15)";
2960
+ const strokeColor = config.strokeColor ?? "rgba(59,130,246,0.5)";
2961
+ let isBrushing = false;
2962
+ let startClientX = 0;
2963
+ let brushRect = null;
2964
+ let parentEl = null;
2965
+ function getParent() {
2966
+ if (parentEl) return parentEl;
2967
+ parentEl = el2.parentElement;
2968
+ if (!parentEl) parentEl = el2;
2969
+ parentEl.style.position = "relative";
2970
+ return parentEl;
2971
+ }
2972
+ function toLocalX(clientX) {
2973
+ const r = el2.getBoundingClientRect();
2974
+ return clientX - r.left;
2975
+ }
2976
+ function onPointerDown(e) {
2977
+ if (isPanEnabled && !e.shiftKey) return;
2978
+ if (!isPanEnabled && e.shiftKey) return;
2979
+ const area = getArea();
2980
+ const localX = toLocalX(e.clientX);
2981
+ const r = el2.getBoundingClientRect();
2982
+ const localY = e.clientY - r.top;
2983
+ if (localX < area.x || localX > area.x + area.width) return;
2984
+ if (localY < area.y || localY > area.y + area.height) return;
2985
+ isBrushing = true;
2986
+ startClientX = e.clientX;
2987
+ el2.setPointerCapture(e.pointerId);
2988
+ brushRect = document.createElement("div");
2989
+ brushRect.className = "chartts-brush-rect";
2990
+ brushRect.style.cssText = `
2991
+ position: absolute;
2992
+ top: ${area.y}px;
2993
+ height: ${area.height}px;
2994
+ left: ${localX}px;
2995
+ width: 0px;
2996
+ background: ${fillColor};
2997
+ border-left: 1px solid ${strokeColor};
2998
+ border-right: 1px solid ${strokeColor};
2999
+ pointer-events: none;
3000
+ z-index: 10;
3001
+ `;
3002
+ getParent().appendChild(brushRect);
3003
+ }
3004
+ function onPointerMove(e) {
3005
+ if (!isBrushing || !brushRect) return;
3006
+ const area = getArea();
3007
+ const startX = Math.max(toLocalX(startClientX), area.x);
3008
+ const currentX = Math.min(Math.max(toLocalX(e.clientX), area.x), area.x + area.width);
3009
+ const left = Math.min(startX, currentX);
3010
+ const width = Math.abs(currentX - startX);
3011
+ brushRect.style.left = `${left}px`;
3012
+ brushRect.style.width = `${width}px`;
3013
+ }
3014
+ function onPointerUp(e) {
3015
+ if (!isBrushing) return;
3016
+ isBrushing = false;
3017
+ el2.releasePointerCapture(e.pointerId);
3018
+ const area = getArea();
3019
+ const xScale = getXScale();
3020
+ const data = getData();
3021
+ const startX = Math.max(toLocalX(startClientX), area.x);
3022
+ const endX = Math.min(Math.max(toLocalX(e.clientX), area.x), area.x + area.width);
3023
+ brushRect?.remove();
3024
+ brushRect = null;
3025
+ if (Math.abs(endX - startX) < 5) return;
3026
+ const leftX = Math.min(startX, endX);
3027
+ const rightX = Math.max(startX, endX);
3028
+ let startIdx = 0;
3029
+ let endIdx = data.labels.length - 1;
3030
+ let bestStartDist = Infinity;
3031
+ let bestEndDist = Infinity;
3032
+ for (let i = 0; i < data.labels.length; i++) {
3033
+ const pos = xScale.map(data.labels[i]);
3034
+ const distToStart = Math.abs(pos - leftX);
3035
+ const distToEnd = Math.abs(pos - rightX);
3036
+ if (distToStart < bestStartDist) {
3037
+ bestStartDist = distToStart;
3038
+ startIdx = i;
3039
+ }
3040
+ if (distToEnd < bestEndDist) {
3041
+ bestEndDist = distToEnd;
3042
+ endIdx = i;
3043
+ }
3044
+ }
3045
+ if (startIdx > endIdx) [startIdx, endIdx] = [endIdx, startIdx];
3046
+ bus.emit("brush:end", {
3047
+ startIndex: startIdx,
3048
+ endIndex: endIdx,
3049
+ startLabel: data.labels[startIdx],
3050
+ endLabel: data.labels[endIdx]
3051
+ });
3052
+ }
3053
+ function onKeyDown(e) {
3054
+ if (e.key === "Escape" && isBrushing) {
3055
+ isBrushing = false;
3056
+ brushRect?.remove();
3057
+ brushRect = null;
3058
+ }
3059
+ }
3060
+ el2.addEventListener("pointerdown", onPointerDown);
3061
+ el2.addEventListener("pointermove", onPointerMove);
3062
+ el2.addEventListener("pointerup", onPointerUp);
3063
+ document.addEventListener("keydown", onKeyDown);
3064
+ return {
3065
+ destroy() {
3066
+ el2.removeEventListener("pointerdown", onPointerDown);
3067
+ el2.removeEventListener("pointermove", onPointerMove);
3068
+ el2.removeEventListener("pointerup", onPointerUp);
3069
+ document.removeEventListener("keydown", onKeyDown);
3070
+ brushRect?.remove();
3071
+ }
3072
+ };
3073
+ }
3074
+
3075
+ // src/debug/debug.ts
3076
+ function createDebugPanel() {
3077
+ let panel = null;
3078
+ let treeEl = null;
3079
+ let statsEl = null;
3080
+ function attach(container, svg) {
3081
+ panel = document.createElement("div");
3082
+ panel.className = "chartts-debug";
3083
+ panel.style.cssText = `
3084
+ position: absolute;
3085
+ top: 0;
3086
+ right: 0;
3087
+ width: 320px;
3088
+ max-height: 100%;
3089
+ overflow-y: auto;
3090
+ background: rgba(0,0,0,0.9);
3091
+ color: #e5e7eb;
3092
+ font-family: "SF Mono", "Fira Code", monospace;
3093
+ font-size: 11px;
3094
+ padding: 12px;
3095
+ border-left: 1px solid #333;
3096
+ z-index: 10000;
3097
+ `;
3098
+ statsEl = document.createElement("div");
3099
+ statsEl.style.cssText = "margin-bottom: 12px; padding-bottom: 12px; border-bottom: 1px solid #333;";
3100
+ panel.appendChild(statsEl);
3101
+ const treeLabel = document.createElement("div");
3102
+ treeLabel.textContent = "RENDER TREE";
3103
+ treeLabel.style.cssText = "color: #8b5cf6; font-weight: 700; margin-bottom: 8px; font-size: 10px; letter-spacing: 0.05em;";
3104
+ panel.appendChild(treeLabel);
3105
+ treeEl = document.createElement("pre");
3106
+ treeEl.style.cssText = "margin: 0; white-space: pre-wrap; word-break: break-all; line-height: 1.4;";
3107
+ panel.appendChild(treeEl);
3108
+ container.style.position = "relative";
3109
+ container.appendChild(panel);
3110
+ svg.addEventListener("mouseover", (e) => {
3111
+ const target = e.target;
3112
+ if (target !== svg) {
3113
+ target.style.outline = "1px solid rgba(139, 92, 246, 0.5)";
3114
+ }
3115
+ });
3116
+ svg.addEventListener("mouseout", (e) => {
3117
+ const target = e.target;
3118
+ target.style.outline = "";
3119
+ });
3120
+ }
3121
+ function update(ctx, nodes) {
3122
+ if (!statsEl || !treeEl) return;
3123
+ const nodeCount = countNodes(nodes);
3124
+ const { data, options, area } = ctx;
3125
+ statsEl.innerHTML = `
3126
+ <div style="color:#8b5cf6;font-weight:700;font-size:10px;letter-spacing:0.05em;margin-bottom:8px;">CHART INFO</div>
3127
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:4px;">
3128
+ <span style="color:#6b7280;">Series:</span><span>${data.series.length}</span>
3129
+ <span style="color:#6b7280;">Points:</span><span>${data.series[0]?.values.length ?? 0}</span>
3130
+ <span style="color:#6b7280;">SVG nodes:</span><span>${nodeCount}</span>
3131
+ <span style="color:#6b7280;">Area:</span><span>${Math.round(area.width)}x${Math.round(area.height)}</span>
3132
+ <span style="color:#6b7280;">Y range:</span><span>${data.bounds.yMin} \u2014 ${data.bounds.yMax}</span>
3133
+ <span style="color:#6b7280;">Theme:</span><span>${typeof options.theme === "string" ? options.theme : "custom"}</span>
3134
+ <span style="color:#6b7280;">Curve:</span><span>${options.curve}</span>
3135
+ <span style="color:#6b7280;">Animate:</span><span>${options.animate}</span>
3136
+ </div>
3137
+ `;
3138
+ treeEl.innerHTML = renderTreeToString(nodes, 0);
3139
+ }
3140
+ function destroy() {
3141
+ panel?.remove();
3142
+ panel = null;
3143
+ treeEl = null;
3144
+ statsEl = null;
3145
+ }
3146
+ return { attach, update, destroy };
3147
+ }
3148
+ function countNodes(nodes) {
3149
+ let count = 0;
3150
+ for (const node of nodes) {
3151
+ count++;
3152
+ if ("children" in node && Array.isArray(node.children)) {
3153
+ count += countNodes(node.children);
3154
+ }
3155
+ }
3156
+ return count;
3157
+ }
3158
+ function renderTreeToString(nodes, depth) {
3159
+ const indent = " ".repeat(depth);
3160
+ let result = "";
3161
+ for (const node of nodes) {
3162
+ const attrs = "attrs" in node ? node.attrs : void 0;
3163
+ const cls = attrs?.class ? ` <span style="color:#3b82f6">.${attrs.class}</span>` : "";
3164
+ const type = `<span style="color:#10b981">${node.type}</span>`;
3165
+ if ("children" in node && Array.isArray(node.children)) {
3166
+ result += `${indent}${type}${cls} (${node.children.length})
3167
+ `;
3168
+ result += renderTreeToString(node.children, depth + 1);
3169
+ } else if (node.type === "text") {
3170
+ result += `${indent}${type}${cls} "${node.content}"
3171
+ `;
3172
+ } else if (node.type === "path") {
3173
+ const d = node.d;
3174
+ result += `${indent}${type}${cls} d="${d.length > 40 ? d.slice(0, 40) + "..." : d}"
3175
+ `;
3176
+ } else {
3177
+ result += `${indent}${type}${cls}
3178
+ `;
3179
+ }
3180
+ }
3181
+ return result;
3182
+ }
3183
+
3184
+ // src/data/decimate.ts
3185
+ function decimateData(data, opts) {
3186
+ const threshold = opts.threshold ?? 1e3;
3187
+ const algo = opts.algorithm ?? "lttb";
3188
+ const needsDecimation = data.series.some((s) => s.values.length > threshold);
3189
+ if (!needsDecimation) return data;
3190
+ const decimated = data.series.map((s) => {
3191
+ if (s.values.length <= threshold) return s;
3192
+ const values = algo === "lttb" ? lttb(s.values, threshold) : minMax(s.values, threshold);
3193
+ return { ...s, values: values.map((p) => p.value) };
3194
+ });
3195
+ const primarySeries = data.series[0];
3196
+ let labels = data.labels;
3197
+ if (labels && primarySeries && primarySeries.values.length > threshold) {
3198
+ const indices = decimated[0].values.length === threshold ? getDecimatedIndices(primarySeries.values, threshold, algo) : void 0;
3199
+ if (indices) {
3200
+ labels = indices.map((i) => labels[i]);
3201
+ }
3202
+ }
3203
+ return { labels, series: decimated };
3204
+ }
3205
+ function lttb(values, threshold) {
3206
+ const len = values.length;
3207
+ if (len <= threshold) {
3208
+ return values.map((v, i) => ({ index: i, value: v }));
3209
+ }
3210
+ const sampled = [];
3211
+ sampled.push({ index: 0, value: values[0] });
3212
+ const bucketSize = (len - 2) / (threshold - 2);
3213
+ let prevIndex = 0;
3214
+ for (let i = 1; i < threshold - 1; i++) {
3215
+ const bucketStart = Math.floor((i - 1) * bucketSize) + 1;
3216
+ const bucketEnd = Math.min(Math.floor(i * bucketSize) + 1, len - 1);
3217
+ const nextBucketStart = Math.floor(i * bucketSize) + 1;
3218
+ const nextBucketEnd = Math.min(Math.floor((i + 1) * bucketSize) + 1, len - 1);
3219
+ let avgX = 0;
3220
+ let avgY = 0;
3221
+ let avgCount = 0;
3222
+ for (let j = nextBucketStart; j < nextBucketEnd; j++) {
3223
+ avgX += j;
3224
+ avgY += values[j];
3225
+ avgCount++;
3226
+ }
3227
+ if (avgCount > 0) {
3228
+ avgX /= avgCount;
3229
+ avgY /= avgCount;
3230
+ }
3231
+ let maxArea = -1;
3232
+ let bestIndex = bucketStart;
3233
+ const px = prevIndex;
3234
+ const py = values[prevIndex];
3235
+ for (let j = bucketStart; j < bucketEnd; j++) {
3236
+ const area = Math.abs(
3237
+ (px - avgX) * (values[j] - py) - (px - j) * (avgY - py)
3238
+ ) * 0.5;
3239
+ if (area > maxArea) {
3240
+ maxArea = area;
3241
+ bestIndex = j;
3242
+ }
3243
+ }
3244
+ sampled.push({ index: bestIndex, value: values[bestIndex] });
3245
+ prevIndex = bestIndex;
3246
+ }
3247
+ sampled.push({ index: len - 1, value: values[len - 1] });
3248
+ return sampled;
3249
+ }
3250
+ function minMax(values, threshold) {
3251
+ const len = values.length;
3252
+ if (len <= threshold) {
3253
+ return values.map((v, i) => ({ index: i, value: v }));
3254
+ }
3255
+ const sampled = [];
3256
+ const bucketSize = len / (threshold / 2);
3257
+ sampled.push({ index: 0, value: values[0] });
3258
+ for (let i = 0; i < threshold / 2 - 1; i++) {
3259
+ const start = Math.floor(i * bucketSize);
3260
+ const end = Math.min(Math.floor((i + 1) * bucketSize), len);
3261
+ let minVal = Infinity;
3262
+ let maxVal = -Infinity;
3263
+ let minIdx = start;
3264
+ let maxIdx = start;
3265
+ for (let j = start; j < end; j++) {
3266
+ const v = values[j];
3267
+ if (v < minVal) {
3268
+ minVal = v;
3269
+ minIdx = j;
3270
+ }
3271
+ if (v > maxVal) {
3272
+ maxVal = v;
3273
+ maxIdx = j;
3274
+ }
3275
+ }
3276
+ if (minIdx < maxIdx) {
3277
+ sampled.push({ index: minIdx, value: minVal });
3278
+ sampled.push({ index: maxIdx, value: maxVal });
3279
+ } else {
3280
+ sampled.push({ index: maxIdx, value: maxVal });
3281
+ sampled.push({ index: minIdx, value: minVal });
3282
+ }
3283
+ }
3284
+ sampled.push({ index: len - 1, value: values[len - 1] });
3285
+ return sampled;
3286
+ }
3287
+ function getDecimatedIndices(values, threshold, algo) {
3288
+ if (algo === "lttb") {
3289
+ return lttb(values, threshold).map((p) => p.index);
3290
+ }
3291
+ return minMax(values, threshold).map((p) => p.index);
3292
+ }
3293
+
3294
+ // src/layout/compute.ts
3295
+ function computeLayout(width, height, options, data) {
3296
+ const [pt, pr, pb, pl] = options.padding;
3297
+ let top = pt;
3298
+ let right = pr;
3299
+ let bottom = pb;
3300
+ let left = pl;
3301
+ if (options.yAxis) {
3302
+ const maxLabel = estimateYLabelWidth(data, options);
3303
+ left += maxLabel + 10;
3304
+ }
3305
+ if (options.yLabel) {
3306
+ left += options.fontSize + 4;
3307
+ }
3308
+ if (options.xAxis) {
3309
+ bottom += options.fontSize + 8;
3310
+ }
3311
+ if (options.xLabel) {
3312
+ bottom += options.fontSize + 4;
3313
+ }
3314
+ if (options.legend) {
3315
+ switch (options.legend) {
3316
+ case "top":
3317
+ top += 20;
3318
+ break;
3319
+ case "bottom":
3320
+ bottom += 20;
3321
+ break;
3322
+ case "left":
3323
+ left += 80;
3324
+ break;
3325
+ case "right":
3326
+ right += 80;
3327
+ break;
3328
+ }
3329
+ }
3330
+ const area = {
3331
+ x: left,
3332
+ y: top,
3333
+ width: Math.max(0, width - left - right),
3334
+ height: Math.max(0, height - top - bottom)
3335
+ };
3336
+ return { area, margins: { top, right, bottom, left } };
3337
+ }
3338
+ function estimateYLabelWidth(data, options) {
3339
+ const { yMin, yMax } = data.bounds;
3340
+ const labels = [options.yFormat(yMin), options.yFormat(yMax)];
3341
+ const maxLen = Math.max(...labels.map((l) => l.length));
3342
+ return maxLen * (options.fontSize * 0.55);
3343
+ }
3344
+
3345
+ // src/layout/responsive.ts
3346
+ function observeResize(element, callback) {
3347
+ if (typeof ResizeObserver === "undefined") return () => {
3348
+ };
3349
+ const observer = new ResizeObserver((entries) => {
3350
+ const entry = entries[0];
3351
+ if (!entry) return;
3352
+ const { width, height } = entry.contentRect;
3353
+ if (width > 0 && height > 0) {
3354
+ callback(Math.floor(width), Math.floor(height));
3355
+ }
3356
+ });
3357
+ observer.observe(element);
3358
+ return () => observer.disconnect();
3359
+ }
3360
+
3361
+ // src/render/tree.ts
3362
+ function group(children, attrs) {
3363
+ return { type: "group", children, attrs };
3364
+ }
3365
+ function path(d, attrs) {
3366
+ return { type: "path", d, attrs };
3367
+ }
3368
+ function rect(x, y, width, height, attrs) {
3369
+ return { type: "rect", x, y, width, height, rx: attrs?.rx, ry: attrs?.ry, attrs };
3370
+ }
3371
+ function circle(cx, cy, r, attrs) {
3372
+ return { type: "circle", cx, cy, r, attrs };
3373
+ }
3374
+ function line(x1, y1, x2, y2, attrs) {
3375
+ return { type: "line", x1, y1, x2, y2, attrs };
3376
+ }
3377
+ function text(x, y, content, attrs) {
3378
+ return { type: "text", x, y, content, attrs };
3379
+ }
3380
+ function defs(children) {
3381
+ return { type: "defs", children };
3382
+ }
3383
+ function clipPathDef(id, children) {
3384
+ return { type: "clipPath", id, children };
3385
+ }
3386
+ var PathBuilder = class {
3387
+ constructor() {
3388
+ this.cmds = [];
3389
+ }
3390
+ moveTo(x, y) {
3391
+ this.cmds.push(`M${n(x)},${n(y)}`);
3392
+ return this;
3393
+ }
3394
+ lineTo(x, y) {
3395
+ this.cmds.push(`L${n(x)},${n(y)}`);
3396
+ return this;
3397
+ }
3398
+ curveTo(cx1, cy1, cx2, cy2, x, y) {
3399
+ this.cmds.push(`C${n(cx1)},${n(cy1)},${n(cx2)},${n(cy2)},${n(x)},${n(y)}`);
3400
+ return this;
3401
+ }
3402
+ quadTo(cx, cy, x, y) {
3403
+ this.cmds.push(`Q${n(cx)},${n(cy)},${n(x)},${n(y)}`);
3404
+ return this;
3405
+ }
3406
+ arc(rx, ry, rot, large, sweep, x, y) {
3407
+ this.cmds.push(`A${n(rx)},${n(ry)},${rot},${large ? 1 : 0},${sweep ? 1 : 0},${n(x)},${n(y)}`);
3408
+ return this;
3409
+ }
3410
+ hTo(x) {
3411
+ this.cmds.push(`H${n(x)}`);
3412
+ return this;
3413
+ }
3414
+ vTo(y) {
3415
+ this.cmds.push(`V${n(y)}`);
3416
+ return this;
3417
+ }
3418
+ close() {
3419
+ this.cmds.push("Z");
3420
+ return this;
3421
+ }
3422
+ build() {
3423
+ return this.cmds.join("");
3424
+ }
3425
+ reset() {
3426
+ this.cmds = [];
3427
+ return this;
3428
+ }
3429
+ };
3430
+ function n(v) {
3431
+ return Number.isInteger(v) ? String(v) : v.toFixed(2);
3432
+ }
3433
+
3434
+ // src/axis/axis.ts
3435
+ function renderXAxis(scale, area, options, theme) {
3436
+ const nodes = [];
3437
+ const y = area.y + area.height;
3438
+ if (options.xAxis) {
3439
+ nodes.push(line(area.x, y, area.x + area.width, y, {
3440
+ class: "chartts-x-axis",
3441
+ stroke: `var(${CSS_PREFIX}-axis)`,
3442
+ strokeWidth: theme.axisWidth
3443
+ }));
3444
+ }
3445
+ const ticks = scale.ticks(options.xTicks || void 0);
3446
+ for (const tick of ticks) {
3447
+ const x = tick.position;
3448
+ if (x < area.x - 1 || x > area.x + area.width + 1) continue;
3449
+ nodes.push(line(x, y, x, y + 4, {
3450
+ class: "chartts-x-tick",
3451
+ stroke: `var(${CSS_PREFIX}-axis)`,
3452
+ strokeWidth: theme.axisWidth
3453
+ }));
3454
+ nodes.push(text(x, y + 14, tick.label, {
3455
+ class: "chartts-x-label",
3456
+ fill: `var(${CSS_PREFIX}-text-muted)`,
3457
+ textAnchor: "middle",
3458
+ fontSize: theme.fontSizeSmall,
3459
+ fontFamily: `var(${CSS_PREFIX}-font-family)`
3460
+ }));
3461
+ }
3462
+ if (options.xLabel) {
3463
+ nodes.push(text(
3464
+ area.x + area.width / 2,
3465
+ y + 30,
3466
+ options.xLabel,
3467
+ {
3468
+ class: "chartts-x-axis-label",
3469
+ fill: `var(${CSS_PREFIX}-text)`,
3470
+ textAnchor: "middle",
3471
+ fontSize: theme.fontSize,
3472
+ fontFamily: `var(${CSS_PREFIX}-font-family)`,
3473
+ fontWeight: 500
3474
+ }
3475
+ ));
3476
+ }
3477
+ return group(nodes, { class: "chartts-x-axis-group" });
3478
+ }
3479
+ function renderYAxis(scale, area, options, theme) {
3480
+ const nodes = [];
3481
+ if (options.yAxis) {
3482
+ nodes.push(line(area.x, area.y, area.x, area.y + area.height, {
3483
+ class: "chartts-y-axis",
3484
+ stroke: `var(${CSS_PREFIX}-axis)`,
3485
+ strokeWidth: theme.axisWidth
3486
+ }));
3487
+ }
3488
+ const ticks = scale.ticks(options.yTicks);
3489
+ for (const tick of ticks) {
3490
+ const y = tick.position;
3491
+ if (y < area.y - 1 || y > area.y + area.height + 1) continue;
3492
+ nodes.push(line(area.x - 4, y, area.x, y, {
3493
+ class: "chartts-y-tick",
3494
+ stroke: `var(${CSS_PREFIX}-axis)`,
3495
+ strokeWidth: theme.axisWidth
3496
+ }));
3497
+ nodes.push(text(area.x - 7, y, tick.label, {
3498
+ class: "chartts-y-label",
3499
+ fill: `var(${CSS_PREFIX}-text-muted)`,
3500
+ textAnchor: "end",
3501
+ dominantBaseline: "middle",
3502
+ fontSize: theme.fontSizeSmall,
3503
+ fontFamily: `var(${CSS_PREFIX}-font-family)`
3504
+ }));
3505
+ }
3506
+ if (options.yLabel) {
3507
+ nodes.push(text(
3508
+ 12,
3509
+ area.y + area.height / 2,
3510
+ options.yLabel,
3511
+ {
3512
+ class: "chartts-y-axis-label",
3513
+ fill: `var(${CSS_PREFIX}-text)`,
3514
+ textAnchor: "middle",
3515
+ fontSize: theme.fontSize,
3516
+ fontFamily: `var(${CSS_PREFIX}-font-family)`,
3517
+ fontWeight: 500,
3518
+ transform: `rotate(-90, 12, ${area.y + area.height / 2})`
3519
+ }
3520
+ ));
3521
+ }
3522
+ return group(nodes, { class: "chartts-y-axis-group" });
3523
+ }
3524
+ function renderGrid(xScale, yScale, area, options, theme) {
3525
+ const nodes = [];
3526
+ const dasharray = theme.gridStyle === "dashed" ? "4,4" : theme.gridStyle === "dotted" ? "2,2" : void 0;
3527
+ if (options.yGrid) {
3528
+ const ticks = yScale.ticks(options.yTicks);
3529
+ for (const tick of ticks) {
3530
+ if (tick.position < area.y - 1 || tick.position > area.y + area.height + 1) continue;
3531
+ if (Math.abs(tick.position - area.y) < 1 || Math.abs(tick.position - (area.y + area.height)) < 1) continue;
3532
+ nodes.push(line(area.x, tick.position, area.x + area.width, tick.position, {
3533
+ class: "chartts-grid-h",
3534
+ stroke: `var(${CSS_PREFIX}-grid)`,
3535
+ strokeWidth: theme.gridWidth,
3536
+ strokeDasharray: dasharray
3537
+ }));
3538
+ }
3539
+ }
3540
+ if (options.xGrid) {
3541
+ const ticks = xScale.ticks(options.xTicks || void 0);
3542
+ for (const tick of ticks) {
3543
+ if (tick.position < area.x - 1 || tick.position > area.x + area.width + 1) continue;
3544
+ if (Math.abs(tick.position - area.x) < 1 || Math.abs(tick.position - (area.x + area.width)) < 1) continue;
3545
+ nodes.push(line(tick.position, area.y, tick.position, area.y + area.height, {
3546
+ class: "chartts-grid-v",
3547
+ stroke: `var(${CSS_PREFIX}-grid)`,
3548
+ strokeWidth: theme.gridWidth,
3549
+ strokeDasharray: dasharray
3550
+ }));
3551
+ }
3552
+ }
3553
+ return group(nodes, { class: "chartts-grid-group" });
3554
+ }
3555
+
3556
+ // src/legend/legend.ts
3557
+ function renderLegend(data, area, options, theme) {
3558
+ if (!options.legend || data.series.length <= 1) return null;
3559
+ const items = [];
3560
+ const pos = options.legend;
3561
+ if (pos === "top" || pos === "bottom") {
3562
+ const totalWidth = estimateLegendWidth(data, theme);
3563
+ let x = area.x + Math.max(0, (area.width - totalWidth) / 2);
3564
+ const y = pos === "top" ? area.y - 10 : area.y + area.height + 32;
3565
+ for (const series of data.series) {
3566
+ items.push(circle(x + 4, y, 3.5, {
3567
+ class: "chartts-legend-dot",
3568
+ fill: series.color
3569
+ }));
3570
+ items.push(text(x + 12, y, series.name, {
3571
+ class: "chartts-legend-text",
3572
+ fill: `var(${CSS_PREFIX}-text-muted)`,
3573
+ dominantBaseline: "central",
3574
+ fontSize: theme.fontSizeSmall,
3575
+ fontFamily: `var(${CSS_PREFIX}-font-family)`
3576
+ }));
3577
+ x += 12 + series.name.length * (theme.fontSizeSmall * 0.58) + 14;
3578
+ }
3579
+ } else {
3580
+ const x = pos === "left" ? 8 : area.x + area.width + 16;
3581
+ let y = area.y + 4;
3582
+ for (const series of data.series) {
3583
+ items.push(circle(x + 4, y + 5, 3.5, {
3584
+ class: "chartts-legend-dot",
3585
+ fill: series.color
3586
+ }));
3587
+ items.push(text(x + 12, y + 5, series.name, {
3588
+ class: "chartts-legend-text",
3589
+ fill: `var(${CSS_PREFIX}-text-muted)`,
3590
+ dominantBaseline: "central",
3591
+ fontSize: theme.fontSizeSmall,
3592
+ fontFamily: `var(${CSS_PREFIX}-font-family)`
3593
+ }));
3594
+ y += 18;
3595
+ }
3596
+ }
3597
+ return group(items, {
3598
+ class: "chartts-legend",
3599
+ role: "list",
3600
+ ariaLabel: "Chart legend"
3601
+ });
3602
+ }
3603
+ function estimateLegendWidth(data, theme) {
3604
+ let width = 0;
3605
+ for (const series of data.series) {
3606
+ width += 12 + series.name.length * (theme.fontSizeSmall * 0.58) + 14;
3607
+ }
3608
+ return width;
3609
+ }
3610
+
3611
+ // src/render/effects.ts
3612
+ function extractHex(css) {
3613
+ const match = css.match(/#[0-9a-fA-F]{3,8}/);
3614
+ return match ? match[0] : css;
3615
+ }
3616
+ function hexToRgb(hex) {
3617
+ const h = hex.replace("#", "");
3618
+ const full = h.length === 3 ? h[0] + h[0] + h[1] + h[1] + h[2] + h[2] : h;
3619
+ return {
3620
+ r: parseInt(full.substring(0, 2), 16),
3621
+ g: parseInt(full.substring(2, 4), 16),
3622
+ b: parseInt(full.substring(4, 6), 16)
3623
+ };
3624
+ }
3625
+ function createEffectDefs(colors) {
3626
+ let svg = "";
3627
+ for (let i = 0; i < colors.length; i++) {
3628
+ const hex = extractHex(colors[i]);
3629
+ const { r, g, b } = hexToRgb(hex);
3630
+ const rgba = (a) => `rgba(${r},${g},${b},${a})`;
3631
+ svg += `<linearGradient id="chartts-area-${i}" x1="0" y1="0" x2="0" y2="1">`;
3632
+ svg += `<stop offset="0%" stop-color="${hex}" stop-opacity="0.35"/>`;
3633
+ svg += `<stop offset="100%" stop-color="${hex}" stop-opacity="0.02"/>`;
3634
+ svg += `</linearGradient>`;
3635
+ svg += `<linearGradient id="chartts-bar-${i}" x1="0" y1="0" x2="0" y2="1">`;
3636
+ svg += `<stop offset="0%" stop-color="${hex}" stop-opacity="1"/>`;
3637
+ svg += `<stop offset="100%" stop-color="${hex}" stop-opacity="0.75"/>`;
3638
+ svg += `</linearGradient>`;
3639
+ svg += `<radialGradient id="chartts-pglow-${i}">`;
3640
+ svg += `<stop offset="0%" stop-color="${hex}" stop-opacity="0.4"/>`;
3641
+ svg += `<stop offset="100%" stop-color="${hex}" stop-opacity="0"/>`;
3642
+ svg += `</radialGradient>`;
3643
+ svg += `<radialGradient id="chartts-pie-${i}" cx="30%" cy="30%" r="70%">`;
3644
+ svg += `<stop offset="0%" stop-color="${hex}" stop-opacity="1"/>`;
3645
+ svg += `<stop offset="100%" stop-color="${rgba(0.85)}"/>`;
3646
+ svg += `</radialGradient>`;
3647
+ }
3648
+ svg += `<filter id="chartts-glow" filterUnits="userSpaceOnUse">`;
3649
+ svg += `<feGaussianBlur in="SourceGraphic" stdDeviation="3.5" result="blur"/>`;
3650
+ svg += `<feColorMatrix in="blur" type="saturate" values="2" result="saturated"/>`;
3651
+ svg += `<feMerge><feMergeNode in="saturated"/><feMergeNode in="SourceGraphic"/></feMerge>`;
3652
+ svg += `</filter>`;
3653
+ svg += `<filter id="chartts-shadow">`;
3654
+ svg += `<feDropShadow dx="0" dy="2" stdDeviation="4" flood-opacity="0.12"/>`;
3655
+ svg += `</filter>`;
3656
+ return svg;
3657
+ }
3658
+
3659
+ // src/tooltip/tooltip.ts
3660
+ function createTooltip(config, theme) {
3661
+ let el2 = null;
3662
+ function getOrCreate() {
3663
+ if (el2) return el2;
3664
+ el2 = document.createElement("div");
3665
+ el2.className = "chartts-tooltip";
3666
+ el2.style.cssText = `
3667
+ position: absolute;
3668
+ pointer-events: none;
3669
+ z-index: 9999;
3670
+ padding: 10px 14px;
3671
+ border-radius: var(${CSS_PREFIX}-radius, 8px);
3672
+ background: var(${CSS_PREFIX}-tooltip-bg, ${theme.tooltipBackground});
3673
+ color: var(${CSS_PREFIX}-tooltip-text, ${theme.tooltipText});
3674
+ border: 1px solid var(${CSS_PREFIX}-tooltip-border, ${theme.tooltipBorder});
3675
+ font-family: var(${CSS_PREFIX}-font-family, ${theme.fontFamily});
3676
+ font-size: var(${CSS_PREFIX}-font-size-sm, ${theme.fontSizeSmall}px);
3677
+ box-shadow: 0 8px 32px rgba(0,0,0,0.12), 0 2px 8px rgba(0,0,0,0.08);
3678
+ backdrop-filter: blur(12px) saturate(1.5);
3679
+ -webkit-backdrop-filter: blur(12px) saturate(1.5);
3680
+ transition: opacity 0.15s ease, transform 0.1s ease;
3681
+ opacity: 0;
3682
+ transform: translateY(4px);
3683
+ white-space: nowrap;
3684
+ `;
3685
+ return el2;
3686
+ }
3687
+ function show(point, color, x, y, container) {
3688
+ const tip = getOrCreate();
3689
+ if (config.render) {
3690
+ const result = config.render({ ...point, color });
3691
+ if (typeof result === "string") {
3692
+ tip.innerHTML = result;
3693
+ } else {
3694
+ tip.innerHTML = "";
3695
+ tip.appendChild(result);
3696
+ }
3697
+ } else if (config.format) {
3698
+ tip.textContent = config.format(point);
3699
+ } else {
3700
+ tip.innerHTML = `
3701
+ <div style="display:flex;align-items:center;gap:8px;margin-bottom:4px;">
3702
+ <span style="width:8px;height:8px;border-radius:50%;background:${color};display:inline-block;box-shadow:0 0 6px ${color};"></span>
3703
+ <span style="font-weight:600;letter-spacing:-0.01em;">${point.seriesName}</span>
3704
+ </div>
3705
+ <div style="display:flex;justify-content:space-between;align-items:baseline;gap:16px;">
3706
+ <span style="color:var(${CSS_PREFIX}-text-muted, #6b7280);font-size:0.9em;">${point.label}</span>
3707
+ <span style="font-size:1.15em;font-weight:700;font-variant-numeric:tabular-nums;">${point.value}</span>
3708
+ </div>
3709
+ `;
3710
+ }
3711
+ if (!tip.parentElement) {
3712
+ container.style.position = "relative";
3713
+ container.appendChild(tip);
3714
+ }
3715
+ const tipRect = tip.getBoundingClientRect();
3716
+ const containerRect = container.getBoundingClientRect();
3717
+ let left = x - tipRect.width / 2;
3718
+ let top = y - tipRect.height - 12;
3719
+ if (left < 0) left = 0;
3720
+ if (left + tipRect.width > containerRect.width) left = containerRect.width - tipRect.width;
3721
+ if (top < 0) top = y + 16;
3722
+ tip.style.left = `${left}px`;
3723
+ tip.style.top = `${top}px`;
3724
+ tip.style.opacity = "1";
3725
+ tip.style.transform = "translateY(0)";
3726
+ }
3727
+ function hide() {
3728
+ if (el2) {
3729
+ el2.style.opacity = "0";
3730
+ el2.style.transform = "translateY(4px)";
3731
+ }
3732
+ }
3733
+ function destroy() {
3734
+ el2?.remove();
3735
+ el2 = null;
3736
+ }
3737
+ return { show, hide, destroy };
3738
+ }
3739
+
3740
+ // src/interaction/interaction.ts
3741
+ var SVG_NS = "http://www.w3.org/2000/svg";
3742
+ function createInteractionLayer(chartType, getContext, getData, bus, tooltipConfig, theme, onClick, onHover, interactionState) {
3743
+ let targetEl = null;
3744
+ let container = null;
3745
+ let tooltip = null;
3746
+ let crosshairVLineEl = null;
3747
+ let crosshairHLineEl = null;
3748
+ let crosshairTipEl = null;
3749
+ let crosshairYTipEl = null;
3750
+ let crosshairDots = [];
3751
+ let activePoint = null;
3752
+ let activeCrosshairIndex = -1;
3753
+ let linkedUnsubs = [];
3754
+ let overlayEl = null;
3755
+ let isCanvas = false;
3756
+ if (tooltipConfig) {
3757
+ tooltip = createTooltip(
3758
+ typeof tooltipConfig === "object" ? tooltipConfig : { enabled: true },
3759
+ theme
3760
+ );
3761
+ }
3762
+ function getOverlay() {
3763
+ if (isCanvas) return overlayEl;
3764
+ return targetEl;
3765
+ }
3766
+ function syncOverlay() {
3767
+ if (!overlayEl || !targetEl) return;
3768
+ const w = targetEl.clientWidth || parseInt(targetEl.style.width) || 400;
3769
+ const h = targetEl.clientHeight || parseInt(targetEl.style.height) || 300;
3770
+ overlayEl.setAttribute("viewBox", `0 0 ${w} ${h}`);
3771
+ overlayEl.setAttribute("width", String(w));
3772
+ overlayEl.setAttribute("height", String(h));
3773
+ }
3774
+ function getCrosshairConfig() {
3775
+ try {
3776
+ return getContext().options.crosshair;
3777
+ } catch {
3778
+ return false;
3779
+ }
3780
+ }
3781
+ function ensureCrosshairVLine() {
3782
+ if (crosshairVLineEl) return crosshairVLineEl;
3783
+ crosshairVLineEl = document.createElementNS(SVG_NS, "line");
3784
+ crosshairVLineEl.setAttribute("class", "chartts-crosshair-line chartts-crosshair-vline");
3785
+ crosshairVLineEl.setAttribute("stroke", theme.textMuted);
3786
+ crosshairVLineEl.setAttribute("stroke-width", "1");
3787
+ crosshairVLineEl.setAttribute("stroke-dasharray", "4,3");
3788
+ crosshairVLineEl.style.pointerEvents = "none";
3789
+ crosshairVLineEl.style.opacity = "0";
3790
+ crosshairVLineEl.style.transition = "opacity 0.12s ease";
3791
+ return crosshairVLineEl;
3792
+ }
3793
+ function ensureCrosshairHLine() {
3794
+ if (crosshairHLineEl) return crosshairHLineEl;
3795
+ crosshairHLineEl = document.createElementNS(SVG_NS, "line");
3796
+ crosshairHLineEl.setAttribute("class", "chartts-crosshair-line chartts-crosshair-hline");
3797
+ crosshairHLineEl.setAttribute("stroke", theme.textMuted);
3798
+ crosshairHLineEl.setAttribute("stroke-width", "1");
3799
+ crosshairHLineEl.setAttribute("stroke-dasharray", "4,3");
3800
+ crosshairHLineEl.style.pointerEvents = "none";
3801
+ crosshairHLineEl.style.opacity = "0";
3802
+ crosshairHLineEl.style.transition = "opacity 0.12s ease";
3803
+ return crosshairHLineEl;
3804
+ }
3805
+ function ensureCrosshairTip() {
3806
+ if (crosshairTipEl) return crosshairTipEl;
3807
+ crosshairTipEl = document.createElement("div");
3808
+ crosshairTipEl.className = "chartts-crosshair-tooltip";
3809
+ crosshairTipEl.style.cssText = `
3810
+ position: absolute;
3811
+ pointer-events: none;
3812
+ z-index: 9999;
3813
+ padding: 10px 14px;
3814
+ border-radius: var(${CSS_PREFIX}-radius, 8px);
3815
+ background: var(${CSS_PREFIX}-tooltip-bg, ${theme.tooltipBackground});
3816
+ color: var(${CSS_PREFIX}-tooltip-text, ${theme.tooltipText});
3817
+ border: 1px solid var(${CSS_PREFIX}-tooltip-border, ${theme.tooltipBorder});
3818
+ font-family: var(${CSS_PREFIX}-font-family, ${theme.fontFamily});
3819
+ font-size: var(${CSS_PREFIX}-font-size-sm, ${theme.fontSizeSmall}px);
3820
+ box-shadow: 0 8px 32px rgba(0,0,0,0.12), 0 2px 8px rgba(0,0,0,0.08);
3821
+ backdrop-filter: blur(12px) saturate(1.5);
3822
+ -webkit-backdrop-filter: blur(12px) saturate(1.5);
3823
+ transition: opacity 0.15s ease, transform 0.1s ease;
3824
+ opacity: 0;
3825
+ transform: translateY(4px);
3826
+ white-space: nowrap;
3827
+ `;
3828
+ return crosshairTipEl;
3829
+ }
3830
+ function ensureCrosshairYTip() {
3831
+ if (crosshairYTipEl) return crosshairYTipEl;
3832
+ crosshairYTipEl = document.createElement("div");
3833
+ crosshairYTipEl.className = "chartts-crosshair-ytip";
3834
+ crosshairYTipEl.style.cssText = `
3835
+ position: absolute;
3836
+ pointer-events: none;
3837
+ z-index: 9998;
3838
+ padding: 2px 6px;
3839
+ border-radius: 3px;
3840
+ background: ${theme.textMuted};
3841
+ color: ${theme.tooltipBackground};
3842
+ font-family: ${theme.fontFamily};
3843
+ font-size: ${theme.fontSizeSmall - 1}px;
3844
+ font-variant-numeric: tabular-nums;
3845
+ opacity: 0;
3846
+ transition: opacity 0.12s ease;
3847
+ white-space: nowrap;
3848
+ `;
3849
+ return crosshairYTipEl;
3850
+ }
3851
+ function clearCrosshairDots() {
3852
+ for (const d of crosshairDots) d.remove();
3853
+ crosshairDots = [];
3854
+ }
3855
+ function showCrosshair(pointIndex, mouseX, mouseY, svgY) {
3856
+ if (!targetEl || !container) return;
3857
+ const overlay = getOverlay();
3858
+ if (!overlay) return;
3859
+ if (isCanvas) syncOverlay();
3860
+ const ctx = getContext();
3861
+ const data = getData();
3862
+ const { area, xScale } = ctx;
3863
+ const chConfig = getCrosshairConfig();
3864
+ if (!chConfig) return;
3865
+ const mode = chConfig.mode ?? "vertical";
3866
+ const label = data.labels[pointIndex];
3867
+ const xPos = xScale.map(label);
3868
+ if (mode === "vertical" || mode === "both") {
3869
+ const vline = ensureCrosshairVLine();
3870
+ vline.setAttribute("x1", String(xPos));
3871
+ vline.setAttribute("y1", String(area.y));
3872
+ vline.setAttribute("x2", String(xPos));
3873
+ vline.setAttribute("y2", String(area.y + area.height));
3874
+ vline.style.opacity = "0.6";
3875
+ if (!vline.parentElement) overlay.appendChild(vline);
3876
+ }
3877
+ if (mode === "horizontal" || mode === "both") {
3878
+ const hline = ensureCrosshairHLine();
3879
+ hline.setAttribute("x1", String(area.x));
3880
+ hline.setAttribute("y1", String(svgY));
3881
+ hline.setAttribute("x2", String(area.x + area.width));
3882
+ hline.setAttribute("y2", String(svgY));
3883
+ hline.style.opacity = "0.6";
3884
+ if (!hline.parentElement) overlay.appendChild(hline);
3885
+ const yVal = ctx.yScale.invert(svgY);
3886
+ const yTip = ensureCrosshairYTip();
3887
+ yTip.textContent = ctx.options.yFormat(yVal);
3888
+ if (!yTip.parentElement) {
3889
+ container.style.position = "relative";
3890
+ container.appendChild(yTip);
3891
+ }
3892
+ yTip.style.left = "0px";
3893
+ yTip.style.top = `${mouseY - 10}px`;
3894
+ yTip.style.opacity = "1";
3895
+ }
3896
+ if (mode === "vertical" || mode === "both") {
3897
+ clearCrosshairDots();
3898
+ for (const series of data.series) {
3899
+ const val = series.values[pointIndex];
3900
+ if (val == null || isNaN(val)) continue;
3901
+ const yPos = ctx.yScale.map(val);
3902
+ const dot = document.createElementNS(SVG_NS, "circle");
3903
+ dot.setAttribute("cx", String(xPos));
3904
+ dot.setAttribute("cy", String(yPos));
3905
+ dot.setAttribute("r", "5");
3906
+ dot.setAttribute("fill", series.color);
3907
+ dot.setAttribute("stroke", theme.tooltipBackground);
3908
+ dot.setAttribute("stroke-width", "2");
3909
+ dot.style.pointerEvents = "none";
3910
+ dot.style.transition = "cx 0.1s ease, cy 0.1s ease";
3911
+ overlay.appendChild(dot);
3912
+ crosshairDots.push(dot);
3913
+ }
3914
+ }
3915
+ const tip = ensureCrosshairTip();
3916
+ let html = `<div style="font-weight:600;margin-bottom:6px;letter-spacing:-0.01em;">${formatLabel(label)}</div>`;
3917
+ for (const series of data.series) {
3918
+ const val = series.values[pointIndex];
3919
+ if (val == null || isNaN(val)) continue;
3920
+ html += `<div style="display:flex;align-items:center;gap:8px;margin-top:3px;">
3921
+ <span style="width:8px;height:8px;border-radius:50%;background:${series.color};display:inline-block;box-shadow:0 0 6px ${series.color};flex-shrink:0;"></span>
3922
+ <span style="flex:1;color:var(${CSS_PREFIX}-text-muted, #6b7280);">${series.name}</span>
3923
+ <span style="font-weight:700;font-variant-numeric:tabular-nums;margin-left:12px;">${ctx.options.yFormat(val)}</span>
3924
+ </div>`;
3925
+ }
3926
+ tip.innerHTML = html;
3927
+ if (!tip.parentElement) {
3928
+ container.style.position = "relative";
3929
+ container.appendChild(tip);
3930
+ }
3931
+ requestAnimationFrame(() => {
3932
+ if (!tip.parentElement || !container) return;
3933
+ const tipRect = tip.getBoundingClientRect();
3934
+ const containerRect = container.getBoundingClientRect();
3935
+ let left = mouseX + 16;
3936
+ let top = mouseY - tipRect.height / 2;
3937
+ if (left + tipRect.width > containerRect.width) left = mouseX - tipRect.width - 16;
3938
+ if (top < 0) top = 4;
3939
+ if (top + tipRect.height > containerRect.height) top = containerRect.height - tipRect.height - 4;
3940
+ tip.style.left = `${left}px`;
3941
+ tip.style.top = `${top}px`;
3942
+ tip.style.opacity = "1";
3943
+ tip.style.transform = "translateY(0)";
3944
+ });
3945
+ activeCrosshairIndex = pointIndex;
3946
+ bus.emit("crosshair:move", { x: xPos, label });
3947
+ }
3948
+ function hideCrosshair() {
3949
+ if (crosshairVLineEl) crosshairVLineEl.style.opacity = "0";
3950
+ if (crosshairHLineEl) crosshairHLineEl.style.opacity = "0";
3951
+ if (crosshairTipEl) {
3952
+ crosshairTipEl.style.opacity = "0";
3953
+ crosshairTipEl.style.transform = "translateY(4px)";
3954
+ }
3955
+ if (crosshairYTipEl) {
3956
+ crosshairYTipEl.style.opacity = "0";
3957
+ }
3958
+ clearCrosshairDots();
3959
+ activeCrosshairIndex = -1;
3960
+ bus.emit("crosshair:hide", void 0);
3961
+ }
3962
+ function formatLabel(label) {
3963
+ try {
3964
+ return getContext().options.xFormat(label);
3965
+ } catch {
3966
+ return String(label);
3967
+ }
3968
+ }
3969
+ function nearestLabelIndex(svgX) {
3970
+ const data = getData();
3971
+ const ctx = getContext();
3972
+ let bestIdx = 0;
3973
+ let bestDist = Infinity;
3974
+ for (let i = 0; i < data.labels.length; i++) {
3975
+ const pos = ctx.xScale.map(data.labels[i]);
3976
+ const dist = Math.abs(svgX - pos);
3977
+ if (dist < bestDist) {
3978
+ bestDist = dist;
3979
+ bestIdx = i;
3980
+ }
3981
+ }
3982
+ return bestIdx;
3983
+ }
3984
+ function toChartCoords(el2, clientX, clientY) {
3985
+ const r = el2.getBoundingClientRect();
3986
+ const x = clientX - r.left;
3987
+ const y = clientY - r.top;
3988
+ if (el2 instanceof HTMLCanvasElement) {
3989
+ return { x, y, svgX: x, svgY: y };
3990
+ }
3991
+ const viewBox = el2.getAttribute("viewBox")?.split(" ").map(Number) ?? [0, 0, r.width, r.height];
3992
+ const scaleX = viewBox[2] / r.width;
3993
+ const scaleY = viewBox[3] / r.height;
3994
+ return { x, y, svgX: x * scaleX, svgY: y * scaleY };
3995
+ }
3996
+ function onMouseMove(e) {
3997
+ if (!targetEl || !container) return;
3998
+ if (interactionState?.isPanning) return;
3999
+ const { x, y, svgX, svgY } = toChartCoords(targetEl, e.clientX, e.clientY);
4000
+ const ctx = getContext();
4001
+ const chConfig = getCrosshairConfig();
4002
+ if (chConfig) {
4003
+ const { area } = ctx;
4004
+ if (svgX >= area.x && svgX <= area.x + area.width && svgY >= area.y && svgY <= area.y + area.height) {
4005
+ const idx = nearestLabelIndex(svgX);
4006
+ if (idx !== activeCrosshairIndex) {
4007
+ showCrosshair(idx, x, y, svgY);
4008
+ } else {
4009
+ if (chConfig.mode === "horizontal" || chConfig.mode === "both") {
4010
+ if (crosshairHLineEl) {
4011
+ crosshairHLineEl.setAttribute("y1", String(svgY));
4012
+ crosshairHLineEl.setAttribute("y2", String(svgY));
4013
+ }
4014
+ if (crosshairYTipEl) {
4015
+ const yVal = ctx.yScale.invert(svgY);
4016
+ crosshairYTipEl.textContent = ctx.options.yFormat(yVal);
4017
+ crosshairYTipEl.style.top = `${y - 10}px`;
4018
+ }
4019
+ }
4020
+ if (crosshairTipEl) {
4021
+ const tipRect = crosshairTipEl.getBoundingClientRect();
4022
+ const containerRect = container.getBoundingClientRect();
4023
+ let left = x + 16;
4024
+ let top = y - tipRect.height / 2;
4025
+ if (left + tipRect.width > containerRect.width) left = x - tipRect.width - 16;
4026
+ if (top < 0) top = 4;
4027
+ if (top + tipRect.height > containerRect.height) top = containerRect.height - tipRect.height - 4;
4028
+ crosshairTipEl.style.left = `${left}px`;
4029
+ crosshairTipEl.style.top = `${top}px`;
4030
+ }
4031
+ }
4032
+ } else {
4033
+ hideCrosshair();
4034
+ }
4035
+ return;
4036
+ }
4037
+ const hit = chartType.hitTest(ctx, svgX, svgY);
4038
+ if (hit) {
4039
+ const data = getData();
4040
+ const series = data.series[hit.seriesIndex];
4041
+ const point = {
4042
+ label: data.labels[hit.pointIndex],
4043
+ value: series.values[hit.pointIndex],
4044
+ index: hit.pointIndex,
4045
+ seriesIndex: hit.seriesIndex,
4046
+ seriesName: series.name
4047
+ };
4048
+ if (!activePoint || activePoint.seriesIndex !== hit.seriesIndex || activePoint.pointIndex !== hit.pointIndex) {
4049
+ clearHighlights();
4050
+ highlightPoint(hit.seriesIndex, hit.pointIndex, svgX, svgY);
4051
+ activePoint = { seriesIndex: hit.seriesIndex, pointIndex: hit.pointIndex };
4052
+ bus.emit("point:enter", { point, event: e });
4053
+ onHover?.(point, e);
4054
+ }
4055
+ if (tooltip) {
4056
+ tooltip.show(point, series.color, x, y, container);
4057
+ bus.emit("tooltip:show", { point, x: svgX, y: svgY });
4058
+ }
4059
+ } else {
4060
+ if (activePoint) {
4061
+ clearHighlights();
4062
+ activePoint = null;
4063
+ bus.emit("point:leave", { event: e });
4064
+ onHover?.(null, e);
4065
+ }
4066
+ if (tooltip) {
4067
+ tooltip.hide();
4068
+ bus.emit("tooltip:hide", void 0);
4069
+ }
4070
+ }
4071
+ }
4072
+ function onMouseLeave(e) {
4073
+ clearHighlights();
4074
+ activePoint = null;
4075
+ tooltip?.hide();
4076
+ hideCrosshair();
4077
+ bus.emit("point:leave", { event: e });
4078
+ onHover?.(null, e);
4079
+ }
4080
+ function onClickHandler(e) {
4081
+ if (!targetEl || !onClick) return;
4082
+ const { svgX, svgY } = toChartCoords(targetEl, e.clientX, e.clientY);
4083
+ const ctx = getContext();
4084
+ const hit = chartType.hitTest(ctx, svgX, svgY);
4085
+ if (hit) {
4086
+ const data = getData();
4087
+ const series = data.series[hit.seriesIndex];
4088
+ const point = {
4089
+ label: data.labels[hit.pointIndex],
4090
+ value: series.values[hit.pointIndex],
4091
+ index: hit.pointIndex,
4092
+ seriesIndex: hit.seriesIndex,
4093
+ seriesName: series.name
4094
+ };
4095
+ onClick(point, e);
4096
+ bus.emit("point:click", { point, event: e });
4097
+ }
4098
+ }
4099
+ function setupLinkedCrosshair() {
4100
+ const unsub1 = bus.on("crosshair:move", ((payload) => {
4101
+ if (!targetEl || !container) return;
4102
+ const data = getData();
4103
+ const idx = data.labels.findIndex((l) => String(l) === String(payload.label));
4104
+ if (idx < 0) return;
4105
+ if (idx === activeCrosshairIndex) return;
4106
+ const ctx = getContext();
4107
+ const xPos = ctx.xScale.map(data.labels[idx]);
4108
+ const rect2 = targetEl.getBoundingClientRect();
4109
+ showCrosshair(idx, xPos, rect2.height / 2, ctx.area.y + ctx.area.height / 2);
4110
+ }));
4111
+ const unsub2 = bus.on("crosshair:hide", (() => {
4112
+ hideCrosshair();
4113
+ }));
4114
+ linkedUnsubs.push(unsub1, unsub2);
4115
+ }
4116
+ function highlightPoint(seriesIndex, pointIndex, hitX, hitY) {
4117
+ const overlay = getOverlay();
4118
+ if (!overlay) return;
4119
+ if (isCanvas) {
4120
+ if (hitX == null || hitY == null) return;
4121
+ syncOverlay();
4122
+ const data = getData();
4123
+ const series = data.series[seriesIndex];
4124
+ if (!series) return;
4125
+ const glow = document.createElementNS(SVG_NS, "circle");
4126
+ glow.setAttribute("cx", String(hitX));
4127
+ glow.setAttribute("cy", String(hitY));
4128
+ glow.setAttribute("r", "12");
4129
+ glow.setAttribute("fill", "none");
4130
+ glow.setAttribute("stroke", series.color);
4131
+ glow.setAttribute("stroke-width", "2");
4132
+ glow.setAttribute("opacity", "0.3");
4133
+ glow.setAttribute("class", "chartts-canvas-highlight");
4134
+ glow.style.pointerEvents = "none";
4135
+ overlay.appendChild(glow);
4136
+ const dot = document.createElementNS(SVG_NS, "circle");
4137
+ dot.setAttribute("cx", String(hitX));
4138
+ dot.setAttribute("cy", String(hitY));
4139
+ dot.setAttribute("r", "5");
4140
+ dot.setAttribute("fill", series.color);
4141
+ dot.setAttribute("stroke", "#fff");
4142
+ dot.setAttribute("stroke-width", "2");
4143
+ dot.setAttribute("class", "chartts-canvas-highlight");
4144
+ dot.style.pointerEvents = "none";
4145
+ overlay.appendChild(dot);
4146
+ return;
4147
+ }
4148
+ const svg = targetEl;
4149
+ const target = svg.querySelector(
4150
+ `[data-series="${seriesIndex}"][data-index="${pointIndex}"]`
4151
+ );
4152
+ if (target?.classList.contains("chartts-point")) {
4153
+ target.style.transition = "r 0.15s ease, stroke-width 0.15s ease";
4154
+ target.setAttribute("r", "6");
4155
+ target.setAttribute("stroke-width", "3");
4156
+ }
4157
+ const targetGroup = target?.closest(".chartts-series");
4158
+ svg.querySelectorAll(".chartts-series").forEach((el2) => {
4159
+ if (el2 !== targetGroup) {
4160
+ el2.style.opacity = "0.3";
4161
+ el2.style.transition = "opacity 0.15s ease";
4162
+ }
4163
+ });
4164
+ }
4165
+ function clearHighlights() {
4166
+ if (isCanvas) {
4167
+ overlayEl?.querySelectorAll(".chartts-canvas-highlight").forEach((el2) => el2.remove());
4168
+ return;
4169
+ }
4170
+ const svg = targetEl;
4171
+ if (!svg) return;
4172
+ svg.querySelectorAll(".chartts-point").forEach((p) => {
4173
+ const el2 = p;
4174
+ el2.setAttribute("r", el2.getAttribute("data-original-r") || "3.5");
4175
+ el2.setAttribute("stroke-width", "2");
4176
+ });
4177
+ svg.querySelectorAll(".chartts-series").forEach((el2) => {
4178
+ el2.style.opacity = "";
4179
+ });
4180
+ }
4181
+ return {
4182
+ attach(svgEl, containerEl) {
4183
+ targetEl = svgEl;
4184
+ container = containerEl;
4185
+ isCanvas = svgEl instanceof HTMLCanvasElement;
4186
+ if (isCanvas) {
4187
+ containerEl.style.position = "relative";
4188
+ overlayEl = document.createElementNS(SVG_NS, "svg");
4189
+ const w = svgEl.clientWidth || parseInt(svgEl.style.width) || 400;
4190
+ const h = svgEl.clientHeight || parseInt(svgEl.style.height) || 300;
4191
+ overlayEl.setAttribute("viewBox", `0 0 ${w} ${h}`);
4192
+ overlayEl.setAttribute("width", String(w));
4193
+ overlayEl.setAttribute("height", String(h));
4194
+ overlayEl.style.cssText = "position:absolute;top:0;left:0;pointer-events:none;overflow:visible;";
4195
+ containerEl.appendChild(overlayEl);
4196
+ }
4197
+ svgEl.style.cursor = "crosshair";
4198
+ const el2 = svgEl;
4199
+ el2.addEventListener("mousemove", onMouseMove);
4200
+ el2.addEventListener("mouseleave", onMouseLeave);
4201
+ el2.addEventListener("click", onClickHandler);
4202
+ setupLinkedCrosshair();
4203
+ },
4204
+ detach() {
4205
+ if (!targetEl) return;
4206
+ const el2 = targetEl;
4207
+ el2.removeEventListener("mousemove", onMouseMove);
4208
+ el2.removeEventListener("mouseleave", onMouseLeave);
4209
+ el2.removeEventListener("click", onClickHandler);
4210
+ for (const unsub of linkedUnsubs) unsub();
4211
+ linkedUnsubs = [];
4212
+ },
4213
+ destroy() {
4214
+ this.detach();
4215
+ tooltip?.destroy();
4216
+ crosshairVLineEl?.remove();
4217
+ crosshairHLineEl?.remove();
4218
+ crosshairTipEl?.remove();
4219
+ crosshairYTipEl?.remove();
4220
+ clearCrosshairDots();
4221
+ overlayEl?.remove();
4222
+ overlayEl = null;
4223
+ targetEl = null;
4224
+ container = null;
4225
+ }
4226
+ };
4227
+ }
4228
+
4229
+ // src/render/states.ts
4230
+ function renderEmptyState(width, height, theme, message = "No data to display") {
4231
+ const cx = width / 2;
4232
+ const cy = height / 2;
4233
+ return [group([
4234
+ // Empty chart icon — simple bar chart outline
4235
+ ...emptyBars(cx, cy - 16, theme),
4236
+ // Message
4237
+ text(cx, cy + 28, message, {
4238
+ class: "chartts-state-text",
4239
+ fill: theme.textMuted,
4240
+ textAnchor: "middle",
4241
+ dominantBaseline: "middle",
4242
+ fontSize: theme.fontSize,
4243
+ fontFamily: theme.fontFamily
4244
+ })
4245
+ ], { class: "chartts-state chartts-state-empty" })];
4246
+ }
4247
+ function renderLoadingState(width, height, theme) {
4248
+ const barCount = 5;
4249
+ const padding = width * 0.15;
4250
+ const barAreaW = width - padding * 2;
4251
+ const barW = barAreaW / barCount * 0.6;
4252
+ const gap = barAreaW / barCount * 0.4;
4253
+ const maxH = height * 0.6;
4254
+ const baseY = height * 0.8;
4255
+ const bars = [];
4256
+ const heights = [0.6, 0.85, 0.45, 0.95, 0.7];
4257
+ for (let i = 0; i < barCount; i++) {
4258
+ const x = padding + i * (barW + gap);
4259
+ const h = maxH * heights[i];
4260
+ const y = baseY - h;
4261
+ bars.push(rect(x, y, barW, h, {
4262
+ class: "chartts-skeleton-bar",
4263
+ fill: theme.gridColor,
4264
+ rx: 3,
4265
+ ry: 3,
4266
+ opacity: 0.6
4267
+ }));
4268
+ }
4269
+ bars.push(rect(padding, baseY, barAreaW, 1, {
4270
+ fill: theme.axisColor,
4271
+ opacity: 0.4
4272
+ }));
4273
+ return [group(bars, { class: "chartts-state chartts-state-loading" })];
4274
+ }
4275
+ function renderErrorState(width, height, theme, message = "Failed to render chart") {
4276
+ const cx = width / 2;
4277
+ const cy = height / 2;
4278
+ return [group([
4279
+ // Error icon — circle with exclamation
4280
+ ...errorIcon(cx, cy - 18, theme),
4281
+ // Message
4282
+ text(cx, cy + 24, message, {
4283
+ class: "chartts-state-text",
4284
+ fill: theme.textMuted,
4285
+ textAnchor: "middle",
4286
+ dominantBaseline: "middle",
4287
+ fontSize: theme.fontSize,
4288
+ fontFamily: theme.fontFamily
4289
+ })
4290
+ ], { class: "chartts-state chartts-state-error" })];
4291
+ }
4292
+ function emptyBars(cx, cy, theme) {
4293
+ const barW = 6;
4294
+ const gap = 4;
4295
+ const heights = [14, 22, 10, 18];
4296
+ const totalW = heights.length * barW + (heights.length - 1) * gap;
4297
+ const startX = cx - totalW / 2;
4298
+ return heights.map(
4299
+ (h, i) => rect(startX + i * (barW + gap), cy - h / 2, barW, h, {
4300
+ fill: theme.gridColor,
4301
+ rx: 1,
4302
+ ry: 1
4303
+ })
4304
+ );
4305
+ }
4306
+ function errorIcon(cx, cy, theme) {
4307
+ return [
4308
+ // Circle outline
4309
+ rect(cx - 14, cy - 14, 28, 28, {
4310
+ fill: "none",
4311
+ stroke: theme.textMuted,
4312
+ strokeWidth: 1.5,
4313
+ rx: 14,
4314
+ ry: 14,
4315
+ opacity: 0.6
4316
+ }),
4317
+ // Exclamation line
4318
+ rect(cx - 1, cy - 7, 2, 10, {
4319
+ fill: theme.textMuted,
4320
+ opacity: 0.6
4321
+ }),
4322
+ // Exclamation dot
4323
+ rect(cx - 1, cy + 5, 2, 2, {
4324
+ fill: theme.textMuted,
4325
+ opacity: 0.6
4326
+ })
4327
+ ];
4328
+ }
4329
+
4330
+ // src/api/create.ts
4331
+ function createChart(target, chartType, data, options = {}) {
4332
+ const container = typeof target === "string" ? document.querySelector(target) : target;
4333
+ if (!container) {
4334
+ throw new Error(`[chartts] Target element not found: ${target}`);
4335
+ }
4336
+ let currentData = data;
4337
+ let currentOptions = resolveOptions(options, data.series.length);
4338
+ let currentTheme = resolveTheme(currentOptions.theme);
4339
+ let width = currentOptions.width || container.clientWidth || 400;
4340
+ let height = currentOptions.height || container.clientHeight || 300;
4341
+ let lastCtx = null;
4342
+ let lastPrepared = null;
4343
+ let chartState = "ready";
4344
+ let stateMessage;
4345
+ let resolvedRenderer = currentOptions.renderer;
4346
+ if (resolvedRenderer === "auto") {
4347
+ const totalPoints = data.series.reduce((sum, s) => sum + s.values.length, 0);
4348
+ resolvedRenderer = totalPoints > 1e5 ? "webgl" : totalPoints > 5e3 ? "canvas" : "svg";
4349
+ }
4350
+ const useCanvas = resolvedRenderer === "canvas" || resolvedRenderer === "webgl";
4351
+ const bus = createEventBus();
4352
+ const renderer = resolvedRenderer === "webgl" ? createWebGLRenderer(currentTheme) : resolvedRenderer === "canvas" ? createCanvasRenderer(currentTheme) : createSVGRenderer();
4353
+ const root = renderer.createRoot(container, width, height, {
4354
+ class: `chartts ${currentOptions.className}`.trim(),
4355
+ role: "img",
4356
+ ariaLabel: currentOptions.ariaLabel
4357
+ });
4358
+ if (!useCanvas) {
4359
+ applyTheme(root.element, currentTheme);
4360
+ } else {
4361
+ container.style.position = "relative";
4362
+ container.style.background = currentTheme.background;
4363
+ }
4364
+ const interactionState = { isPanning: false };
4365
+ const interaction = createInteractionLayer(
4366
+ chartType,
4367
+ () => lastCtx,
4368
+ () => lastPrepared,
4369
+ bus,
4370
+ currentOptions.tooltip,
4371
+ currentTheme,
4372
+ currentOptions.onClick,
4373
+ currentOptions.onHover,
4374
+ interactionState
4375
+ );
4376
+ if (useCanvas) {
4377
+ interaction.attach(root.element, container);
4378
+ } else {
4379
+ interaction.attach(root.element, container);
4380
+ }
4381
+ let zoomPan = null;
4382
+ if (currentOptions.zoom || currentOptions.pan) {
4383
+ zoomPan = createZoomPan(
4384
+ {
4385
+ x: true,
4386
+ y: false,
4387
+ wheel: currentOptions.zoom,
4388
+ drag: currentOptions.pan,
4389
+ pinch: currentOptions.zoom
4390
+ },
4391
+ () => {
4392
+ render();
4393
+ const state = zoomPan.getState();
4394
+ bus.emit("zoom:change", state);
4395
+ },
4396
+ interactionState
4397
+ );
4398
+ zoomPan.attach(
4399
+ root.element,
4400
+ () => lastCtx.area,
4401
+ () => ({ xScale: lastCtx.xScale, yScale: lastCtx.yScale })
4402
+ );
4403
+ }
4404
+ let brush = null;
4405
+ if (currentOptions.brush) {
4406
+ brush = createBrush(
4407
+ {},
4408
+ bus,
4409
+ root.element,
4410
+ () => lastCtx.area,
4411
+ () => lastCtx.xScale,
4412
+ () => lastPrepared,
4413
+ !!currentOptions.pan
4414
+ );
4415
+ }
4416
+ let debug = null;
4417
+ if (options.debug) {
4418
+ debug = createDebugPanel();
4419
+ debug.attach(container, root.element);
4420
+ }
4421
+ const stopResize = currentOptions.width && currentOptions.height ? () => {
4422
+ } : observeResize(container, (w, h) => {
4423
+ width = w;
4424
+ height = h;
4425
+ render();
4426
+ bus.emit("resize", { width: w, height: h });
4427
+ });
4428
+ const stopThemeWatch = currentOptions.theme === "auto" ? watchScheme(() => {
4429
+ currentTheme = resolveTheme("auto");
4430
+ applyTheme(root.element, currentTheme);
4431
+ render();
4432
+ }) : () => {
4433
+ };
4434
+ render();
4435
+ function render() {
4436
+ if (useCanvas) {
4437
+ const canvas = root.element;
4438
+ const dpr = window.devicePixelRatio || 1;
4439
+ canvas.width = Math.round(width * dpr);
4440
+ canvas.height = Math.round(height * dpr);
4441
+ canvas.style.width = `${width}px`;
4442
+ canvas.style.height = `${height}px`;
4443
+ const ctx2d = canvas.getContext("2d");
4444
+ ctx2d.setTransform(dpr, 0, 0, dpr, 0, 0);
4445
+ } else {
4446
+ root.element.setAttribute("viewBox", `0 0 ${width} ${height}`);
4447
+ }
4448
+ if (chartState === "loading") {
4449
+ renderer.render(root, renderLoadingState(width, height, currentTheme));
4450
+ return;
4451
+ }
4452
+ if (chartState === "error") {
4453
+ renderer.render(root, renderErrorState(width, height, currentTheme, stateMessage));
4454
+ return;
4455
+ }
4456
+ const isEmpty = !currentData.series.length || currentData.series.every((s) => s.values.length === 0);
4457
+ if (chartState === "empty" || isEmpty) {
4458
+ renderer.render(root, renderEmptyState(width, height, currentTheme, stateMessage));
4459
+ return;
4460
+ }
4461
+ const decimateOpt = options.decimate;
4462
+ const dataForRender = decimateOpt ? decimateData(currentData, typeof decimateOpt === "object" ? decimateOpt : { threshold: Math.max(width * 2, 500) }) : currentData;
4463
+ const prepared = chartType.prepareData(dataForRender, currentOptions);
4464
+ lastPrepared = prepared;
4465
+ const suppressAxes = NO_AXES_TYPES.has(chartType.type);
4466
+ const layoutOpts = suppressAxes ? { ...currentOptions, xAxis: false, yAxis: false, xLabel: "", yLabel: "", legend: false, padding: [4, 4, 4, 4] } : currentOptions;
4467
+ const { area } = computeLayout(width, height, layoutOpts, prepared);
4468
+ const useBand = BAND_SCALE_TYPES.has(chartType.type);
4469
+ const xScale = createCategoricalScale({
4470
+ categories: prepared.labels,
4471
+ range: [area.x, area.x + area.width],
4472
+ format: currentOptions.xFormat,
4473
+ band: useBand
4474
+ });
4475
+ const yScale = createLinearScale({
4476
+ domain: [prepared.bounds.yMin, prepared.bounds.yMax],
4477
+ range: [area.y + area.height, area.y],
4478
+ nice: true,
4479
+ format: currentOptions.yFormat
4480
+ });
4481
+ if (zoomPan) {
4482
+ zoomPan.applyToScales(xScale, yScale, area);
4483
+ }
4484
+ const ctx = {
4485
+ data: prepared,
4486
+ options: currentOptions,
4487
+ area,
4488
+ xScale,
4489
+ yScale,
4490
+ theme: currentTheme
4491
+ };
4492
+ lastCtx = ctx;
4493
+ const clipId = "chartts-clip";
4494
+ const nodes = [];
4495
+ nodes.push(defs([
4496
+ clipPathDef(clipId, [rect(area.x, area.y, area.width, area.height)])
4497
+ ]));
4498
+ if (!suppressAxes) {
4499
+ nodes.push(renderGrid(xScale, yScale, area, currentOptions, currentTheme));
4500
+ nodes.push(renderXAxis(xScale, area, currentOptions, currentTheme));
4501
+ nodes.push(renderYAxis(yScale, area, currentOptions, currentTheme));
4502
+ }
4503
+ const chartNodes = chartType.render(ctx);
4504
+ nodes.push(group(chartNodes, { class: "chartts-content", clipPath: clipId }));
4505
+ if (!suppressAxes) {
4506
+ const legend = renderLegend(prepared, area, currentOptions, currentTheme);
4507
+ if (legend) nodes.push(legend);
4508
+ }
4509
+ renderer.render(root, nodes);
4510
+ if (!useCanvas) {
4511
+ const NS2 = "http://www.w3.org/2000/svg";
4512
+ let fxDefs = root.element.querySelector("defs.chartts-fx");
4513
+ if (!fxDefs) {
4514
+ fxDefs = document.createElementNS(NS2, "defs");
4515
+ fxDefs.classList.add("chartts-fx");
4516
+ root.element.insertBefore(fxDefs, root.element.firstChild);
4517
+ }
4518
+ fxDefs.innerHTML = createEffectDefs(currentOptions.colors);
4519
+ }
4520
+ debug?.update(ctx, nodes);
4521
+ }
4522
+ const instance = {
4523
+ setData(newData) {
4524
+ const prev = currentData;
4525
+ currentData = newData;
4526
+ currentOptions = resolveOptions(options, newData.series.length);
4527
+ chartState = "ready";
4528
+ stateMessage = void 0;
4529
+ render();
4530
+ bus.emit("data:change", { previous: prev, current: newData });
4531
+ },
4532
+ setOptions(newOpts) {
4533
+ Object.assign(options, newOpts);
4534
+ currentOptions = resolveOptions(options, currentData.series.length);
4535
+ currentTheme = resolveTheme(currentOptions.theme);
4536
+ applyTheme(root.element, currentTheme);
4537
+ render();
4538
+ },
4539
+ getData() {
4540
+ return currentData;
4541
+ },
4542
+ getOptions() {
4543
+ return currentOptions;
4544
+ },
4545
+ setLoading(loading = true) {
4546
+ chartState = loading ? "loading" : "ready";
4547
+ stateMessage = void 0;
4548
+ render();
4549
+ },
4550
+ setError(message) {
4551
+ chartState = "error";
4552
+ stateMessage = message;
4553
+ render();
4554
+ },
4555
+ setEmpty(message) {
4556
+ chartState = "empty";
4557
+ stateMessage = message;
4558
+ render();
4559
+ },
4560
+ toSVG() {
4561
+ if (useCanvas) throw new Error("[chartts] toSVG() is not available with canvas renderer");
4562
+ return root.element.outerHTML;
4563
+ },
4564
+ async toPNG(opts) {
4565
+ if (useCanvas) {
4566
+ const canvas2 = root.element;
4567
+ return new Promise((resolve, reject) => {
4568
+ canvas2.toBlob((b) => b ? resolve(b) : reject(new Error("PNG export failed")), "image/png");
4569
+ });
4570
+ }
4571
+ const scale = opts?.scale ?? 2;
4572
+ const canvas = document.createElement("canvas");
4573
+ canvas.width = width * scale;
4574
+ canvas.height = height * scale;
4575
+ const canvasCtx = canvas.getContext("2d");
4576
+ const svgStr = this.toSVG();
4577
+ const blob = new Blob([svgStr], { type: "image/svg+xml;charset=utf-8" });
4578
+ const url = URL.createObjectURL(blob);
4579
+ const img = new Image();
4580
+ img.width = width * scale;
4581
+ img.height = height * scale;
4582
+ return new Promise((resolve, reject) => {
4583
+ img.onload = () => {
4584
+ canvasCtx.drawImage(img, 0, 0, width * scale, height * scale);
4585
+ URL.revokeObjectURL(url);
4586
+ canvas.toBlob((b) => b ? resolve(b) : reject(new Error("PNG export failed")), "image/png");
4587
+ };
4588
+ img.onerror = reject;
4589
+ img.src = url;
4590
+ });
4591
+ },
4592
+ async toClipboard() {
4593
+ const blob = await this.toPNG();
4594
+ await navigator.clipboard.write([
4595
+ new ClipboardItem({ "image/png": blob })
4596
+ ]);
4597
+ },
4598
+ on(event, handler) {
4599
+ return bus.on(event, handler);
4600
+ },
4601
+ resize(w, h) {
4602
+ width = w;
4603
+ height = h;
4604
+ render();
4605
+ },
4606
+ resetZoom() {
4607
+ if (zoomPan) {
4608
+ zoomPan.reset();
4609
+ bus.emit("zoom:reset", void 0);
4610
+ }
4611
+ },
4612
+ destroy() {
4613
+ stopResize();
4614
+ stopThemeWatch();
4615
+ interaction.destroy();
4616
+ zoomPan?.destroy();
4617
+ brush?.destroy();
4618
+ debug?.destroy();
4619
+ bus.emit("destroy", void 0);
4620
+ bus.destroy();
4621
+ renderer.destroy(root);
4622
+ },
4623
+ get element() {
4624
+ return root.element;
4625
+ },
4626
+ get _bus() {
4627
+ return bus;
4628
+ }
4629
+ };
4630
+ return instance;
4631
+ }
4632
+
4633
+ // src/render/string.ts
4634
+ function renderToString(chartType, data, options = {}) {
4635
+ const width = options.width ?? 600;
4636
+ const height = options.height ?? 400;
4637
+ const resolved = resolveOptions({ ...options, width, height }, data.series.length);
4638
+ const theme = resolveTheme(resolved.theme);
4639
+ const prepared = chartType.prepareData(data, resolved);
4640
+ const suppressAxes = NO_AXES_TYPES.has(chartType.type);
4641
+ const layoutOpts = suppressAxes ? { ...resolved, xAxis: false, yAxis: false, xLabel: "", yLabel: "", legend: false, padding: [4, 4, 4, 4] } : resolved;
4642
+ const { area } = computeLayout(width, height, layoutOpts, prepared);
4643
+ const useBand = BAND_SCALE_TYPES.has(chartType.type);
4644
+ const xScale = createCategoricalScale({
4645
+ categories: prepared.labels,
4646
+ range: [area.x, area.x + area.width],
4647
+ format: resolved.xFormat,
4648
+ band: useBand
4649
+ });
4650
+ const yScale = createLinearScale({
4651
+ domain: [prepared.bounds.yMin, prepared.bounds.yMax],
4652
+ range: [area.y + area.height, area.y],
4653
+ nice: true,
4654
+ format: resolved.yFormat
4655
+ });
4656
+ const ctx = { data: prepared, options: resolved, area, xScale, yScale, theme };
4657
+ const clipId = "chartts-clip";
4658
+ const nodes = [];
4659
+ nodes.push(defs([
4660
+ clipPathDef(clipId, [rect(area.x, area.y, area.width, area.height)])
4661
+ ]));
4662
+ if (!suppressAxes) {
4663
+ nodes.push(renderGrid(xScale, yScale, area, resolved, theme));
4664
+ nodes.push(renderXAxis(xScale, area, resolved, theme));
4665
+ nodes.push(renderYAxis(yScale, area, resolved, theme));
4666
+ }
4667
+ const chartNodes = chartType.render(ctx);
4668
+ nodes.push(group(chartNodes, { class: "chartts-content", clipPath: clipId }));
4669
+ if (!suppressAxes) {
4670
+ const legend = renderLegend(prepared, area, resolved, theme);
4671
+ if (legend) nodes.push(legend);
4672
+ }
4673
+ const childrenStr = nodes.map(nodeToString).join("");
4674
+ const styleStr = `<style>${CHART_CSS}</style>`;
4675
+ const fxDefsStr = `<defs class="chartts-fx">${createEffectDefs(resolved.colors)}</defs>`;
4676
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}" preserveAspectRatio="xMidYMid meet" role="img" aria-label="${escapeAttr(resolved.ariaLabel)}" class="chartts">${styleStr}${fxDefsStr}${childrenStr}</svg>`;
4677
+ }
4678
+ function nodeToString(node) {
4679
+ switch (node.type) {
4680
+ case "group": {
4681
+ const attrs = renderAttrs(node.attrs);
4682
+ const children = node.children.map(nodeToString).join("");
4683
+ return `<g${attrs}>${children}</g>`;
4684
+ }
4685
+ case "path": {
4686
+ const attrs = renderAttrs(node.attrs);
4687
+ const fill = node.attrs?.fill ? "" : ' fill="none"';
4688
+ return `<path d="${escapeAttr(node.d)}"${fill}${attrs}/>`;
4689
+ }
4690
+ case "rect": {
4691
+ const attrs = renderAttrs(node.attrs);
4692
+ const rx = node.rx != null ? ` rx="${node.rx}"` : "";
4693
+ const ry = node.ry != null ? ` ry="${node.ry}"` : "";
4694
+ return `<rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}"${rx}${ry}${attrs}/>`;
4695
+ }
4696
+ case "circle": {
4697
+ const attrs = renderAttrs(node.attrs);
4698
+ return `<circle cx="${node.cx}" cy="${node.cy}" r="${node.r}"${attrs}/>`;
4699
+ }
4700
+ case "line": {
4701
+ const attrs = renderAttrs(node.attrs);
4702
+ return `<line x1="${node.x1}" y1="${node.y1}" x2="${node.x2}" y2="${node.y2}"${attrs}/>`;
4703
+ }
4704
+ case "text": {
4705
+ const a = node.attrs;
4706
+ let attrs = renderAttrs(a);
4707
+ if (a) {
4708
+ if ("textAnchor" in a && a.textAnchor) attrs += ` text-anchor="${a.textAnchor}"`;
4709
+ if ("dominantBaseline" in a && a.dominantBaseline) attrs += ` dominant-baseline="${a.dominantBaseline}"`;
4710
+ if ("fontSize" in a && a.fontSize) attrs += ` font-size="${a.fontSize}"`;
4711
+ if ("fontFamily" in a && a.fontFamily) attrs += ` font-family="${escapeAttr(a.fontFamily)}"`;
4712
+ if ("fontWeight" in a && a.fontWeight) attrs += ` font-weight="${a.fontWeight}"`;
4713
+ }
4714
+ return `<text x="${node.x}" y="${node.y}"${attrs}>${escapeXml(node.content)}</text>`;
4715
+ }
4716
+ case "clipPath": {
4717
+ const children = node.children.map(nodeToString).join("");
4718
+ return `<clipPath id="${escapeAttr(node.id)}">${children}</clipPath>`;
4719
+ }
4720
+ case "defs": {
4721
+ const children = node.children.map(nodeToString).join("");
4722
+ return `<defs>${children}</defs>`;
4723
+ }
4724
+ }
4725
+ }
4726
+ var ATTR_MAP = {
4727
+ class: "class",
4728
+ style: "style",
4729
+ stroke: "stroke",
4730
+ strokeWidth: "stroke-width",
4731
+ strokeDasharray: "stroke-dasharray",
4732
+ fill: "fill",
4733
+ fillOpacity: "fill-opacity",
4734
+ opacity: "opacity",
4735
+ transform: "transform",
4736
+ role: "role",
4737
+ ariaLabel: "aria-label",
4738
+ tabindex: "tabindex"
4739
+ };
4740
+ var SKIP_KEYS = /* @__PURE__ */ new Set([
4741
+ "textAnchor",
4742
+ "dominantBaseline",
4743
+ "fontSize",
4744
+ "fontFamily",
4745
+ "fontWeight",
4746
+ "rx",
4747
+ "ry"
4748
+ ]);
4749
+ function renderAttrs(attrs) {
4750
+ if (!attrs) return "";
4751
+ let result = "";
4752
+ for (const [key, value] of Object.entries(attrs)) {
4753
+ if (value == null) continue;
4754
+ if (SKIP_KEYS.has(key)) continue;
4755
+ if (key === "clipPath") {
4756
+ result += ` clip-path="url(#${escapeAttr(String(value))})"`;
4757
+ } else if (key.startsWith("data-")) {
4758
+ result += ` ${key}="${escapeAttr(String(value))}"`;
4759
+ } else if (ATTR_MAP[key]) {
4760
+ result += ` ${ATTR_MAP[key]}="${escapeAttr(String(value))}"`;
4761
+ }
4762
+ }
4763
+ return result;
4764
+ }
4765
+ function escapeAttr(s) {
4766
+ return s.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4767
+ }
4768
+ function escapeXml(s) {
4769
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
4770
+ }
4771
+
4772
+ // src/data/validate.ts
4773
+ var CharttsError = class extends Error {
4774
+ constructor(message) {
4775
+ super(`[chartts] ${message}`);
4776
+ this.name = "CharttsError";
4777
+ }
4778
+ };
4779
+ function validateData(data) {
4780
+ if (!data) {
4781
+ throw new CharttsError("No data provided. Pass { series: [{ name, values }] }");
4782
+ }
4783
+ if (!Array.isArray(data.series) || data.series.length === 0) {
4784
+ throw new CharttsError("data.series must be a non-empty array.");
4785
+ }
4786
+ const len = data.series[0].values.length;
4787
+ for (let i = 0; i < data.series.length; i++) {
4788
+ const s = data.series[i];
4789
+ if (!s.name || typeof s.name !== "string") {
4790
+ throw new CharttsError(`series[${i}].name must be a non-empty string.`);
4791
+ }
4792
+ if (!Array.isArray(s.values)) {
4793
+ throw new CharttsError(`series[${i}] ("${s.name}").values must be an array.`);
4794
+ }
4795
+ if (s.values.length !== len) {
4796
+ throw new CharttsError(
4797
+ `Series length mismatch: "${data.series[0].name}" has ${len} values but "${s.name}" has ${s.values.length}. All series must have equal length.`
4798
+ );
4799
+ }
4800
+ for (let j = 0; j < s.values.length; j++) {
4801
+ const v = s.values[j];
4802
+ if (typeof v !== "number" || !Number.isFinite(v) && !Number.isNaN(v)) {
4803
+ throw new CharttsError(
4804
+ `series[${i}] ("${s.name}").values[${j}] must be a finite number (or NaN for missing data). Got: ${JSON.stringify(v)}`
4805
+ );
4806
+ }
4807
+ }
4808
+ }
4809
+ if (data.labels && data.labels.length > 0 && data.labels.length !== len) {
4810
+ throw new CharttsError(
4811
+ `labels has ${data.labels.length} entries but series have ${len} values. They must match.`
4812
+ );
4813
+ }
4814
+ }
4815
+
4816
+ // src/data/prepare.ts
4817
+ function prepareData(data, options) {
4818
+ validateData(data);
4819
+ const count = data.series[0].values.length;
4820
+ const labels = data.labels ?? Array.from({ length: count }, (_, i) => i);
4821
+ const series = data.series.map((s, i) => ({
4822
+ name: s.name,
4823
+ values: s.values,
4824
+ color: s.color ?? options.colors[i % options.colors.length],
4825
+ style: s.style ?? "solid",
4826
+ fill: s.fill ?? false,
4827
+ fillOpacity: s.fillOpacity ?? 0.15,
4828
+ showPoints: s.showPoints ?? true,
4829
+ index: i
4830
+ }));
4831
+ const bounds = computeBounds(series, options);
4832
+ return { labels, series, bounds };
4833
+ }
4834
+ function computeBounds(series, options) {
4835
+ let yMin = Infinity;
4836
+ let yMax = -Infinity;
4837
+ for (const s of series) {
4838
+ for (const v of s.values) {
4839
+ if (isNaN(v)) continue;
4840
+ if (v < yMin) yMin = v;
4841
+ if (v > yMax) yMax = v;
4842
+ }
4843
+ }
4844
+ if (yMin === yMax) {
4845
+ yMin = yMin === 0 ? 0 : yMin - Math.abs(yMin) * 0.1;
4846
+ yMax = yMax === 0 ? 1 : yMax + Math.abs(yMax) * 0.1;
4847
+ }
4848
+ if (options.yMin !== void 0) yMin = options.yMin;
4849
+ if (options.yMax !== void 0) yMax = options.yMax;
4850
+ const count = series[0]?.values.length ?? 0;
4851
+ return {
4852
+ xMin: 0,
4853
+ xMax: Math.max(0, count - 1),
4854
+ yMin,
4855
+ yMax
4856
+ };
4857
+ }
4858
+
4859
+ exports.CORPORATE_THEME = CORPORATE_THEME;
4860
+ exports.CSS_PREFIX = CSS_PREFIX;
4861
+ exports.DARK_THEME = DARK_THEME;
4862
+ exports.EDITORIAL_THEME = EDITORIAL_THEME;
4863
+ exports.LIGHT_THEME = LIGHT_THEME;
4864
+ exports.OCEAN_THEME = OCEAN_THEME;
4865
+ exports.PALETTE = PALETTE;
4866
+ exports.PathBuilder = PathBuilder;
4867
+ exports.SAAS_THEME = SAAS_THEME;
4868
+ exports.STARTUP_THEME = STARTUP_THEME;
4869
+ exports.THEME_PRESETS = THEME_PRESETS;
4870
+ exports.applyTheme = applyTheme;
4871
+ exports.circle = circle;
4872
+ exports.createBrush = createBrush;
4873
+ exports.createCanvasRenderer = createCanvasRenderer;
4874
+ exports.createCategoricalScale = createCategoricalScale;
4875
+ exports.createChart = createChart;
4876
+ exports.createDebugPanel = createDebugPanel;
4877
+ exports.createEventBus = createEventBus;
4878
+ exports.createLinearScale = createLinearScale;
4879
+ exports.createSVGRenderer = createSVGRenderer;
4880
+ exports.createWebGLRenderer = createWebGLRenderer;
4881
+ exports.createZoomPan = createZoomPan;
4882
+ exports.decimateData = decimateData;
4883
+ exports.group = group;
4884
+ exports.line = line;
4885
+ exports.path = path;
4886
+ exports.prepareData = prepareData;
4887
+ exports.rect = rect;
4888
+ exports.renderToString = renderToString;
4889
+ exports.resolveTheme = resolveTheme;
4890
+ exports.text = text;
4891
+ //# sourceMappingURL=chunk-XHQWWR5M.cjs.map
4892
+ //# sourceMappingURL=chunk-XHQWWR5M.cjs.map