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