@icen.ai/ui 0.8.1 → 0.10.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 (335) hide show
  1. package/AGENTS.md +176 -75
  2. package/README.md +66 -88
  3. package/dist/base.css +208 -179
  4. package/dist/behaviors/accordion.mjs +8 -8
  5. package/dist/behaviors/ai-branch.d.mts +19 -0
  6. package/dist/behaviors/ai-branch.mjs +1133 -0
  7. package/dist/behaviors/ai-chat.d.mts +12 -10
  8. package/dist/behaviors/ai-chat.mjs +1125 -155
  9. package/dist/behaviors/ai-composer.d.mts +44 -12
  10. package/dist/behaviors/ai-composer.mjs +2084 -523
  11. package/dist/behaviors/ai-core.d.mts +1 -1
  12. package/dist/behaviors/ai-core.mjs +1016 -107
  13. package/dist/behaviors/ai-diff.mjs +1019 -127
  14. package/dist/behaviors/ai-feedback.d.mts +26 -0
  15. package/dist/behaviors/ai-feedback.mjs +1212 -0
  16. package/dist/behaviors/ai-panel.d.mts +21 -7
  17. package/dist/behaviors/ai-panel.mjs +1308 -264
  18. package/dist/behaviors/ai-provider.mjs +1003 -101
  19. package/dist/behaviors/ai-threads.d.mts +38 -0
  20. package/dist/behaviors/ai-threads.mjs +1233 -0
  21. package/dist/behaviors/ai-tool.d.mts +2 -2
  22. package/dist/behaviors/ai-tool.mjs +2042 -396
  23. package/dist/behaviors/ai-tools.d.mts +89 -16
  24. package/dist/behaviors/ai-tools.mjs +4453 -372
  25. package/dist/behaviors/autocomplete.d.mts +53 -0
  26. package/dist/behaviors/autocomplete.mjs +1600 -0
  27. package/dist/behaviors/back-top.d.mts +1 -1
  28. package/dist/behaviors/back-top.mjs +951 -14
  29. package/dist/behaviors/carousel.d.mts +1 -1
  30. package/dist/behaviors/carousel.mjs +906 -11
  31. package/dist/behaviors/cascader.d.mts +48 -0
  32. package/dist/behaviors/cascader.mjs +1853 -0
  33. package/dist/behaviors/charts.d.mts +174 -8
  34. package/dist/behaviors/charts.mjs +2632 -192
  35. package/dist/behaviors/code-block.d.mts +36 -0
  36. package/dist/behaviors/code-block.mjs +1611 -0
  37. package/dist/behaviors/command-palette.d.mts +9 -6
  38. package/dist/behaviors/command-palette.mjs +161 -59
  39. package/dist/behaviors/context-menu.d.mts +9 -8
  40. package/dist/behaviors/context-menu.mjs +1112 -44
  41. package/dist/behaviors/controls.d.mts +13 -13
  42. package/dist/behaviors/controls.mjs +989 -130
  43. package/dist/behaviors/copy.d.mts +21 -2
  44. package/dist/behaviors/copy.mjs +1206 -42
  45. package/dist/behaviors/datatable.d.mts +53 -3
  46. package/dist/behaviors/datatable.mjs +1963 -207
  47. package/dist/behaviors/date-picker.d.mts +0 -1
  48. package/dist/behaviors/date-picker.mjs +1340 -293
  49. package/dist/behaviors/design.d.mts +54 -0
  50. package/dist/behaviors/design.mjs +109 -0
  51. package/dist/behaviors/dropdown.d.mts +5 -6
  52. package/dist/behaviors/dropdown.mjs +262 -110
  53. package/dist/behaviors/events.d.mts +313 -3
  54. package/dist/behaviors/events.mjs +47 -2
  55. package/dist/behaviors/feedback.d.mts +30 -0
  56. package/dist/behaviors/feedback.mjs +296 -0
  57. package/dist/behaviors/focus.d.mts +8 -0
  58. package/dist/behaviors/focus.mjs +24 -0
  59. package/dist/behaviors/form-validate.d.mts +102 -0
  60. package/dist/behaviors/form-validate.mjs +6091 -0
  61. package/dist/behaviors/i18n.d.mts +789 -0
  62. package/dist/behaviors/i18n.mjs +1711 -0
  63. package/dist/behaviors/icons.d.mts +93 -0
  64. package/dist/behaviors/icons.mjs +113 -0
  65. package/dist/behaviors/input.d.mts +9 -1
  66. package/dist/behaviors/input.mjs +1056 -60
  67. package/dist/behaviors/kb-access.d.mts +34 -0
  68. package/dist/behaviors/kb-access.mjs +2459 -0
  69. package/dist/behaviors/kb-acl.d.mts +50 -0
  70. package/dist/behaviors/kb-acl.mjs +1345 -0
  71. package/dist/behaviors/kb-answer.d.mts +38 -0
  72. package/dist/behaviors/kb-answer.mjs +3707 -0
  73. package/dist/behaviors/kb-audit.d.mts +23 -0
  74. package/dist/behaviors/kb-audit.mjs +1297 -0
  75. package/dist/behaviors/kb-canvas.d.mts +44 -0
  76. package/dist/behaviors/kb-canvas.mjs +1512 -0
  77. package/dist/behaviors/kb-chain.d.mts +15 -0
  78. package/dist/behaviors/kb-chain.mjs +1192 -0
  79. package/dist/behaviors/kb-checkpoint.d.mts +16 -0
  80. package/dist/behaviors/kb-checkpoint.mjs +1492 -0
  81. package/dist/behaviors/kb-chunks.d.mts +31 -0
  82. package/dist/behaviors/kb-chunks.mjs +1414 -0
  83. package/dist/behaviors/kb-citation.d.mts +38 -0
  84. package/dist/behaviors/kb-citation.mjs +1699 -0
  85. package/dist/behaviors/kb-clarify.d.mts +20 -0
  86. package/dist/behaviors/kb-clarify.mjs +1176 -0
  87. package/dist/behaviors/kb-conflict.d.mts +22 -0
  88. package/dist/behaviors/kb-conflict.mjs +1206 -0
  89. package/dist/behaviors/kb-connector.d.mts +17 -0
  90. package/dist/behaviors/kb-connector.mjs +1303 -0
  91. package/dist/behaviors/kb-core.d.mts +534 -0
  92. package/dist/behaviors/kb-core.mjs +1805 -0
  93. package/dist/behaviors/kb-eval.d.mts +22 -0
  94. package/dist/behaviors/kb-eval.mjs +2235 -0
  95. package/dist/behaviors/kb-explain.d.mts +11 -0
  96. package/dist/behaviors/kb-explain.mjs +1165 -0
  97. package/dist/behaviors/kb-filter.d.mts +31 -0
  98. package/dist/behaviors/kb-filter.mjs +2417 -0
  99. package/dist/behaviors/kb-gap.d.mts +27 -0
  100. package/dist/behaviors/kb-gap.mjs +1195 -0
  101. package/dist/behaviors/kb-hittest.d.mts +34 -0
  102. package/dist/behaviors/kb-hittest.mjs +1352 -0
  103. package/dist/behaviors/kb-metadata.d.mts +29 -0
  104. package/dist/behaviors/kb-metadata.mjs +1248 -0
  105. package/dist/behaviors/kb-passage.d.mts +41 -0
  106. package/dist/behaviors/kb-passage.mjs +1469 -0
  107. package/dist/behaviors/kb-pipeline.d.mts +18 -0
  108. package/dist/behaviors/kb-pipeline.mjs +1301 -0
  109. package/dist/behaviors/kb-qa.d.mts +26 -0
  110. package/dist/behaviors/kb-qa.mjs +1260 -0
  111. package/dist/behaviors/kb-rerank.d.mts +20 -0
  112. package/dist/behaviors/kb-rerank.mjs +1248 -0
  113. package/dist/behaviors/kb-retrieval.d.mts +49 -0
  114. package/dist/behaviors/kb-retrieval.mjs +2585 -0
  115. package/dist/behaviors/kb-review.d.mts +44 -0
  116. package/dist/behaviors/kb-review.mjs +2282 -0
  117. package/dist/behaviors/kb-sandbox.d.mts +33 -0
  118. package/dist/behaviors/kb-sandbox.mjs +1247 -0
  119. package/dist/behaviors/kb-segment.d.mts +32 -0
  120. package/dist/behaviors/kb-segment.mjs +1431 -0
  121. package/dist/behaviors/kb-select.d.mts +24 -0
  122. package/dist/behaviors/kb-select.mjs +1989 -0
  123. package/dist/behaviors/kb-sources.d.mts +27 -0
  124. package/dist/behaviors/kb-sources.mjs +1287 -0
  125. package/dist/behaviors/kb-sql.d.mts +43 -0
  126. package/dist/behaviors/kb-sql.mjs +1865 -0
  127. package/dist/behaviors/kb-trace.d.mts +16 -0
  128. package/dist/behaviors/kb-trace.mjs +1328 -0
  129. package/dist/behaviors/kb-visibility.d.mts +44 -0
  130. package/dist/behaviors/kb-visibility.mjs +1331 -0
  131. package/dist/behaviors/kb-who-can.d.mts +32 -0
  132. package/dist/behaviors/kb-who-can.mjs +2216 -0
  133. package/dist/behaviors/mentions.d.mts +93 -0
  134. package/dist/behaviors/mentions.mjs +2152 -0
  135. package/dist/behaviors/modal.d.mts +4 -3
  136. package/dist/behaviors/modal.mjs +1055 -33
  137. package/dist/behaviors/nav.d.mts +1 -1
  138. package/dist/behaviors/nav.mjs +6 -6
  139. package/dist/behaviors/notification.d.mts +16 -8
  140. package/dist/behaviors/notification.mjs +1615 -265
  141. package/dist/behaviors/panel.d.mts +4 -0
  142. package/dist/behaviors/panel.mjs +994 -0
  143. package/dist/behaviors/popover.d.mts +28 -5
  144. package/dist/behaviors/popover.mjs +225 -24
  145. package/dist/behaviors/select.d.mts +5 -2
  146. package/dist/behaviors/select.mjs +1410 -117
  147. package/dist/behaviors/sidebar.d.mts +1 -1
  148. package/dist/behaviors/sidebar.mjs +13 -13
  149. package/dist/behaviors/slider.d.mts +1 -1
  150. package/dist/behaviors/slider.mjs +9 -9
  151. package/dist/behaviors/split-pane.d.mts +1 -1
  152. package/dist/behaviors/split-pane.mjs +7 -7
  153. package/dist/behaviors/suggestions.d.mts +27 -0
  154. package/dist/behaviors/suggestions.mjs +1089 -0
  155. package/dist/behaviors/svg-parse.d.mts +14 -0
  156. package/dist/behaviors/svg-parse.mjs +37 -0
  157. package/dist/behaviors/tabs.d.mts +2 -2
  158. package/dist/behaviors/tabs.mjs +25 -25
  159. package/dist/behaviors/tag-input.d.mts +23 -5
  160. package/dist/behaviors/tag-input.mjs +1359 -104
  161. package/dist/behaviors/theme-switcher.d.mts +24 -0
  162. package/dist/behaviors/theme-switcher.mjs +1981 -0
  163. package/dist/behaviors/theme.d.mts +15 -8
  164. package/dist/behaviors/theme.mjs +390 -28
  165. package/dist/behaviors/toast.d.mts +24 -25
  166. package/dist/behaviors/toast.mjs +1266 -88
  167. package/dist/behaviors/tooltip.d.mts +4 -0
  168. package/dist/behaviors/tooltip.mjs +378 -0
  169. package/dist/behaviors/tree-select.d.mts +69 -0
  170. package/dist/behaviors/tree-select.mjs +1907 -0
  171. package/dist/behaviors/tree.d.mts +1 -1
  172. package/dist/behaviors/tree.mjs +15 -15
  173. package/dist/behaviors/upload.d.mts +3 -2
  174. package/dist/behaviors/upload.mjs +1076 -66
  175. package/dist/cli.mjs +5 -6
  176. package/dist/components/ai-branch.mjs +3 -0
  177. package/dist/components/ai-chat.css +846 -458
  178. package/dist/components/ai-diff.css +153 -132
  179. package/dist/components/ai-feedback.mjs +3 -0
  180. package/dist/components/ai-panel.css +495 -270
  181. package/dist/components/ai-threads.mjs +3 -0
  182. package/dist/components/ai-tool.css +393 -238
  183. package/dist/components/autocomplete.css +61 -0
  184. package/dist/components/autocomplete.mjs +3 -0
  185. package/dist/components/back-top.css +34 -31
  186. package/dist/components/badge.css +44 -41
  187. package/dist/components/breadcrumb.css +67 -46
  188. package/dist/components/btn.css +80 -74
  189. package/dist/components/card.css +117 -106
  190. package/dist/components/cascader.css +311 -0
  191. package/dist/components/cascader.mjs +3 -0
  192. package/dist/components/chart-area.css +8 -2
  193. package/dist/components/chart-bar.css +95 -33
  194. package/dist/components/chart-gauge.css +14 -11
  195. package/dist/components/chart-graph.css +60 -0
  196. package/dist/components/chart-graph.mjs +4 -0
  197. package/dist/components/chart-heatmap.css +96 -90
  198. package/dist/components/chart-line.css +8 -3
  199. package/dist/components/chart-map.mjs +3 -0
  200. package/dist/components/chart-pie.css +9 -6
  201. package/dist/components/chart-radar.css +8 -5
  202. package/dist/components/chart-scatter.css +9 -6
  203. package/dist/components/chart-scatter.mjs +1 -1
  204. package/dist/components/chart-sparkline.css +5 -2
  205. package/dist/components/chart-stack.css +14 -11
  206. package/dist/components/charts.css +190 -67
  207. package/dist/components/charts.mjs +1 -0
  208. package/dist/components/code-block.css +418 -0
  209. package/dist/components/code-block.mjs +4 -0
  210. package/dist/components/command-palette.css +138 -117
  211. package/dist/components/content.css +257 -251
  212. package/dist/components/copy.css +72 -44
  213. package/dist/components/datatable.css +423 -181
  214. package/dist/components/date-picker.css +135 -104
  215. package/dist/components/empty.css +68 -65
  216. package/dist/components/feedback.css +201 -183
  217. package/dist/components/form-validate.css +120 -0
  218. package/dist/components/form-validate.mjs +3 -0
  219. package/dist/components/form.css +150 -127
  220. package/dist/components/icon.css +102 -0
  221. package/dist/components/icon.mjs +3 -0
  222. package/dist/components/input.css +159 -104
  223. package/dist/components/kb-access.mjs +4 -0
  224. package/dist/components/kb-acl.mjs +4 -0
  225. package/dist/components/kb-agent.css +496 -0
  226. package/dist/components/kb-answer.mjs +4 -0
  227. package/dist/components/kb-audit.mjs +4 -0
  228. package/dist/components/kb-canvas.mjs +4 -0
  229. package/dist/components/kb-chain.mjs +4 -0
  230. package/dist/components/kb-checkpoint.mjs +4 -0
  231. package/dist/components/kb-chunks.mjs +4 -0
  232. package/dist/components/kb-citation.mjs +4 -0
  233. package/dist/components/kb-clarify.mjs +4 -0
  234. package/dist/components/kb-conflict.mjs +4 -0
  235. package/dist/components/kb-connector.mjs +4 -0
  236. package/dist/components/kb-data.css +653 -0
  237. package/dist/components/kb-eval.mjs +4 -0
  238. package/dist/components/kb-explain.mjs +4 -0
  239. package/dist/components/kb-filter.mjs +4 -0
  240. package/dist/components/kb-gap.mjs +4 -0
  241. package/dist/components/kb-ground.css +578 -0
  242. package/dist/components/kb-hittest.mjs +4 -0
  243. package/dist/components/kb-ingest.css +882 -0
  244. package/dist/components/kb-metadata.mjs +4 -0
  245. package/dist/components/kb-ops.css +554 -0
  246. package/dist/components/kb-passage.mjs +4 -0
  247. package/dist/components/kb-perm.css +433 -0
  248. package/dist/components/kb-pipeline.mjs +4 -0
  249. package/dist/components/kb-qa.mjs +4 -0
  250. package/dist/components/kb-rerank.mjs +4 -0
  251. package/dist/components/kb-retrieval.mjs +4 -0
  252. package/dist/components/kb-review.mjs +4 -0
  253. package/dist/components/kb-sandbox.mjs +4 -0
  254. package/dist/components/kb-search.css +897 -0
  255. package/dist/components/kb-segment.mjs +4 -0
  256. package/dist/components/kb-sources.mjs +4 -0
  257. package/dist/components/kb-sql.mjs +4 -0
  258. package/dist/components/kb-trace.mjs +4 -0
  259. package/dist/components/kb-visibility.mjs +4 -0
  260. package/dist/components/kb-who-can.mjs +4 -0
  261. package/dist/components/kb.css +263 -0
  262. package/dist/components/kb.mjs +9 -0
  263. package/dist/components/layout.css +85 -82
  264. package/dist/components/material-chinese.css +73 -0
  265. package/dist/components/material-soviet.css +76 -0
  266. package/dist/components/material.css +275 -0
  267. package/dist/components/media.css +220 -166
  268. package/dist/components/mentions.css +189 -0
  269. package/dist/components/mentions.mjs +3 -0
  270. package/dist/components/menu.css +113 -105
  271. package/dist/components/modal.css +107 -112
  272. package/dist/components/nav.css +118 -115
  273. package/dist/components/notification.css +238 -174
  274. package/dist/components/pagination.css +56 -53
  275. package/dist/components/panel.css +66 -63
  276. package/dist/components/panel.mjs +1 -0
  277. package/dist/components/pill.css +81 -71
  278. package/dist/components/popover.css +67 -53
  279. package/dist/components/scroll-area.css +113 -110
  280. package/dist/components/segmented.css +146 -143
  281. package/dist/components/select.css +206 -186
  282. package/dist/components/sidebar.css +137 -127
  283. package/dist/components/slider.css +117 -114
  284. package/dist/components/split-pane.css +112 -109
  285. package/dist/components/stat.css +112 -106
  286. package/dist/components/steps.css +109 -103
  287. package/dist/components/switch.css +138 -134
  288. package/dist/components/table.css +131 -107
  289. package/dist/components/tabs.css +114 -108
  290. package/dist/components/tag-input.css +48 -133
  291. package/dist/components/tag.css +81 -71
  292. package/dist/components/theme-switcher.css +105 -0
  293. package/dist/components/theme-switcher.mjs +3 -0
  294. package/dist/components/toast.css +100 -86
  295. package/dist/components/toolbar.css +45 -42
  296. package/dist/components/tooltip.css +102 -43
  297. package/dist/components/tooltip.mjs +1 -0
  298. package/dist/components/tree-select.css +416 -0
  299. package/dist/components/tree-select.mjs +3 -0
  300. package/dist/components/upload.css +135 -92
  301. package/dist/fonts/LICENSES/ark-pixel/OFL.txt +96 -0
  302. package/dist/fonts/LICENSES/cubic-11/OFL.txt +108 -0
  303. package/dist/fonts/LICENSES/galmuri/LICENSE.txt +93 -0
  304. package/dist/fonts/OFL.txt +96 -0
  305. package/dist/fonts/README.md +14 -0
  306. package/dist/fonts/fusion-pixel-12px-zh-hans.woff2 +0 -0
  307. package/dist/index.d.mts +64 -15
  308. package/dist/index.mjs +28049 -8595
  309. package/dist/react.d.mts +91 -0
  310. package/dist/react.mjs +8208 -0
  311. package/dist/registry.json +454 -15
  312. package/dist/tokens/chinese.css +44 -0
  313. package/dist/tokens/colors.css +365 -1222
  314. package/dist/tokens/design.css +109 -0
  315. package/dist/tokens/dtcg.json +11198 -0
  316. package/dist/tokens/figma.variables.json +7017 -0
  317. package/dist/tokens/icon-masks.css +78 -0
  318. package/dist/tokens/pixel.css +50 -0
  319. package/dist/tokens/soviet.css +44 -0
  320. package/dist/tokens/style-profiles.css +36 -547
  321. package/dist/tokens/themes.json +308 -0
  322. package/dist/tokens/tokens-studio.json +6728 -0
  323. package/dist/tokens/typography.css +21 -18
  324. package/dist/tokens.css +1555 -1801
  325. package/dist/ui.css +16147 -8597
  326. package/dist/vue.d.mts +344 -0
  327. package/dist/vue.mjs +7781 -0
  328. package/docs/spec/ai-input-experience.md +75 -0
  329. package/docs/spec/ai-native.md +238 -147
  330. package/docs/spec/design-philosophy.md +189 -0
  331. package/docs/spec/design-primitives.md +196 -0
  332. package/docs/spec/kb-family.md +475 -0
  333. package/package.json +42 -7
  334. package/dist/retro-effects.css +0 -255
  335. package/dist/tokens/retro-effects.css +0 -255
@@ -5,6 +5,944 @@ function emitIcen(source, type, detail) {
5
5
  );
6
6
  }
7
7
 
8
+ // src/behaviors/icons.ts
9
+ var ICON_PATHS = {
10
+ file: "M6 2.5h5.5L16 7v9.5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-12a2 2 0 0 1 2-2ZM11.5 2.5V7H16",
11
+ folder: "M3 5a1.5 1.5 0 0 1 1.5-1.5H8l2 2h5.5A1.5 1.5 0 0 1 17 7v8a1.5 1.5 0 0 1-1.5 1.5h-11A1.5 1.5 0 0 1 3 15Z",
12
+ globe: "M17.5 10a7.5 7.5 0 1 1-15 0 7.5 7.5 0 0 1 15 0ZM2.5 10h15M10 2.5c-4 4-4 11 0 15 4-4 4-11 0-15Z",
13
+ document: "M6 2.5h5.5L16 7v9.5a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-12a2 2 0 0 1 2-2ZM11.5 2.5V7H16M7 10.5h6M7 13.5h6",
14
+ agent: "M10 6V3M8 3h2M5 6h10a2 2 0 0 1 2 2v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2ZM1.5 10h1.5M17 10h1.5M7 10v2M13 10v2M8 14h4",
15
+ paperclip: "M14 7 8.3 12.7a1.6 1.6 0 0 1-2.3-2.3l6.3-6.3a3.2 3.2 0 0 1 4.5 4.5l-7 7a4.7 4.7 0 0 1-6.7-6.7l7-7",
16
+ stop: "M6.5 5h7A1.5 1.5 0 0 1 15 6.5v7a1.5 1.5 0 0 1-1.5 1.5h-7A1.5 1.5 0 0 1 5 13.5v-7A1.5 1.5 0 0 1 6.5 5Z",
17
+ terminal: "M5 3.5h10a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2ZM6 7l3 3-3 3M11 13h3",
18
+ branch: "M6 6v8M14 6v2a4 4 0 0 1-4 4H6M8 4a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM8 16a2 2 0 1 1-4 0 2 2 0 0 1 4 0ZM16 4a2 2 0 1 1-4 0 2 2 0 0 1 4 0Z",
19
+ "line-numbers": "M3 5h2M3 10h2M3 15h2M8 5h9M8 10h9M8 15h9",
20
+ wrap: "M3 5h14M3 10h10a3 3 0 0 1 0 6h-3M12.5 13.5 10 16l2.5 2.5",
21
+ calendar: "M5.5 4h9A2.5 2.5 0 0 1 17 6.5v8a2.5 2.5 0 0 1-2.5 2.5h-9A2.5 2.5 0 0 1 3 14.5v-8A2.5 2.5 0 0 1 5.5 4ZM6.5 2.5v3M13.5 2.5v3M3 8h14M6.5 11h.1M10 11h.1M13.5 11h.1M6.5 14h.1M10 14h.1",
22
+ close: "M6 6l8 8M14 6l-8 8",
23
+ check: "M4.5 10.2l3.6 3.6 7.4-7.6",
24
+ "chevron-down": "M5.5 7.75 10 12.25l4.5-4.5",
25
+ "chevron-up": "M5.5 12.25 10 7.75l4.5 4.5",
26
+ "chevron-right": "M7.75 5.5 12.25 10l-4.5 4.5",
27
+ "chevron-left": "M12.25 5.5 7.75 10l4.5 4.5",
28
+ "arrow-right": "M3.5 10h13M11 4.5l5.5 5.5-5.5 5.5",
29
+ "arrow-left": "M16.5 10h-13M9 4.5 3.5 10 9 15.5",
30
+ "arrow-up": "M10 16.5v-13M4.5 9 10 3.5 15.5 9",
31
+ "arrow-down": "M10 3.5v13M4.5 11l5.5 5.5 5.5-5.5",
32
+ plus: "M10 4v12M4 10h12",
33
+ minus: "M4 10h12",
34
+ sort: "M6 15V4M3 7l3-3 3 3M14 5v11M11 13l3 3 3-3",
35
+ search: "M13.3 13.3 17 17M14.5 8.5a6 6 0 1 1-12 0 6 6 0 0 1 12 0",
36
+ sun: "M13.5 10a3.5 3.5 0 1 1-7 0 3.5 3.5 0 0 1 7 0M10 1.75v1.5M10 16.75v1.5M1.75 10h1.5M16.75 10h1.5M4.17 4.17l1.06 1.06M14.77 14.77l1.06 1.06M4.17 15.83l1.06-1.06M14.77 5.23l1.06-1.06",
37
+ moon: "M17.25 11.15A7.5 7.5 0 0 1 8.85 2.75a7.5 7.5 0 1 0 8.4 8.4Z",
38
+ modern: "M6.5 3h7A3.5 3.5 0 0 1 17 6.5v7a3.5 3.5 0 0 1-3.5 3.5h-7A3.5 3.5 0 0 1 3 13.5v-7A3.5 3.5 0 0 1 6.5 3ZM7 10l3-3 3 3-3 3Z",
39
+ pixel: "M3 3h5v5H3ZM12 3h5v5h-5ZM3 12h5v5H3ZM12 12h5v5h-5Z",
40
+ chinese: "M3 8c3 0 5-2 7-5 2 3 4 5 7 5M4 8h12M6 8v8m8-8v8M3 16h14M8 16v-5h4v5",
41
+ soviet: "M3 17h14M5 17V8h4v9m0-12h6v12M7 8V5h2m2 3h2m-2 3h2m-2 3h2M10 5V3h4v2",
42
+ more: "M4 9.9v.2M10 9.9v.2M16 9.9v.2",
43
+ copy: "M7 7h8a2 2 0 0 1 2 2v6a2 2 0 0 1-2 2H9a2 2 0 0 1-2-2V7ZM13 7V5a2 2 0 0 0-2-2H5a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h2",
44
+ edit: "M11.5 4.5l4 4M4 12l9-9a1.4 1.4 0 0 1 2 0l2 2a1.4 1.4 0 0 1 0 2l-9 9-5 1Z",
45
+ link: "M8 6l2-2a4.2 4.2 0 0 1 6 6l-2 2M12 14l-2 2a4.2 4.2 0 0 1-6-6l2-2M7 13l6-6",
46
+ trash: "M3.5 5.5h13M7 5.5V3h6v2.5M5 5.5l.7 11.5h8.6L15 5.5M8 9v5M12 9v5",
47
+ spinner: "M10 3a7 7 0 1 1-7 7",
48
+ "circle-check": "M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0ZM6 10l2.7 2.7 5.3-5.4",
49
+ "circle-error": "M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0ZM7.2 7.2l5.6 5.6M12.8 7.2l-5.6 5.6",
50
+ "circle-info": "M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0ZM10 9v5M10 6.1v.15",
51
+ "triangle-alert": "M8.7 3.5a1.5 1.5 0 0 1 2.6 0l6.4 11.3a1.5 1.5 0 0 1-1.3 2.2H3.6a1.5 1.5 0 0 1-1.3-2.2ZM10 7.2v4.4M10 14v.15",
52
+ refresh: "M16.5 7.2A7 7 0 0 0 4.3 5.9L2.5 8M2.5 3.5V8H7M3.5 12.8a7 7 0 0 0 12.2 1.3l1.8-2.1M13 12h4.5v4.5",
53
+ external: "M11.5 3H17v5.5M17 3l-8 8M8 3H5a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-3",
54
+ home: "M3.5 10.25 10 4l6.5 6.25M5.75 8.75v7a.75.75 0 0 0 .75.75h7.5a.75.75 0 0 0 .75-.75v-7",
55
+ settings: "M3.5 5.5h13M3.5 10h13M3.5 14.5h13M7 4v3M12.5 8.5v3M8 13v3",
56
+ star: "M10 2.5l2.4 5.1 5.6.4-4.2 3.7 1.3 5.5-5.1-3-5.1 3 1.3-5.5L1.8 8l5.6-.4Z",
57
+ eye: "M2.2 10s3-5.4 7.8-5.4S17.8 10 17.8 10s-3 5.4-7.8 5.4S2.2 10 2.2 10ZM10 12.4a2.4 2.4 0 1 0 0-4.8 2.4 2.4 0 0 0 0 4.8Z",
58
+ "eye-off": "M2.2 10s3-5.4 7.8-5.4S17.8 10 17.8 10s-3 5.4-7.8 5.4S2.2 10 2.2 10ZM10 12.4a2.4 2.4 0 1 0 0-4.8 2.4 2.4 0 0 0 0 4.8ZM3.5 3.5l13 13",
59
+ inbox: "M3.5 12.25h3.7l1.5 2.5h2.6l1.5-2.5h3.7M4.7 5.5l-1.2 6.75v3.25a1.5 1.5 0 0 0 1.5 1.5h10a1.5 1.5 0 0 0 1.5-1.5v-3.25L15.3 5.5a1.3 1.3 0 0 0-1.25-1H5.95a1.3 1.3 0 0 0-1.25 1Z",
60
+ bell: "M10 3.5a4.5 4.5 0 0 1 4.5 4.5c0 4.5 1.8 5.7 2.5 6.5H3c.7-.8 2.5-2 2.5-6.5A4.5 4.5 0 0 1 10 3.5ZM8.5 16.5a1.6 1.6 0 0 0 3 0",
61
+ users: "M13.8 16.5v-1.5a3.5 3.5 0 0 0-3.5-3.5H6.3a3.5 3.5 0 0 0-3.5 3.5v1.5M8.3 8.9a2.7 2.7 0 1 0 0-5.4 2.7 2.7 0 0 0 0 5.4ZM16.5 16.5v-1.3a3.3 3.3 0 0 0-2.3-3.1M12.3 3.7a2.7 2.7 0 0 1 0 5.2",
62
+ activity: "M17 10h-2.6l-2 5.8L8 4.2 6 10H3",
63
+ "trend-up": "M3.5 14.5l4.2-4.2 2.8 2.8 5.9-5.9M12.4 7.2h4.2v4.2",
64
+ "trend-down": "M3.5 5.5l4.2 4.2 2.8-2.8 5.9 5.9M12.4 12.8h4.2V8.6",
65
+ rocket: "M10 2.5c2 1.5 3.2 4 3.2 6.8 0 1.5-.3 2.9-.8 4.2H7.6c-.5-1.3-.8-2.7-.8-4.2 0-2.8 1.2-5.3 3.2-6.8ZM10 8.6a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3ZM7.6 12.5 5.6 15.4c-.9.3-1.6 1-2 2M12.4 12.5l2 2.9c.9.3 1.6 1 2 2M8.7 13.8c0 1.8.5 3.1 1.3 4.2.8-1.1 1.3-2.4 1.3-4.2",
66
+ bold: "M6.5 4h4.2a3 3 0 0 1 0 6H6.5M6.5 10h5a3 3 0 0 1 0 6H6.5M6.5 4v12",
67
+ italic: "M8.5 4.5h7M4.5 15.5h7M12.5 4.5l-5 11",
68
+ underline: "M6 4v5.5a4 4 0 0 0 8 0V4M4.5 16h11",
69
+ "align-left": "M3.5 4.5h13M3.5 10h8.5M3.5 15.5h11",
70
+ "align-center": "M3.5 4.5h13M5.75 10h8.5M4.5 15.5h11",
71
+ "align-right": "M3.5 4.5h13M8 10h8.5M5.5 15.5h11",
72
+ list: "M6.5 5h10M6.5 10h10M6.5 15h10M3.5 5h.01M3.5 10h.01M3.5 15h.01",
73
+ server: "M3 3h14a1.5 1.5 0 0 1 1.5 1.5v2A1.5 1.5 0 0 1 17 8H3a1.5 1.5 0 0 1-1.5-1.5v-2A1.5 1.5 0 0 1 3 3ZM3 12h14a1.5 1.5 0 0 1 1.5 1.5v2A1.5 1.5 0 0 1 17 17H3a1.5 1.5 0 0 1-1.5-1.5v-2A1.5 1.5 0 0 1 3 12ZM5 5.5h.01M5 14.5h.01",
74
+ sparkle: "M10 2.5l1.6 4.3 4.3 1.6-4.3 1.6L10 14.3l-1.6-4.3-4.3-1.6 4.3-1.6ZM15.5 12.5l.8 2.1 2.1.8-2.1.8-.8 2.1-.8-2.1-2.1-.8 2.1-.8Z",
75
+ play: "M6.5 4.8v10.4c0 .9 1 1.5 1.8 1l8.3-5.2a1.2 1.2 0 0 0 0-2L8.3 3.8a1.2 1.2 0 0 0-1.8 1Z",
76
+ "chart-line": "M3.5 3.5v13h13M6.5 12l2.8-2.8 2.2 2.2 4-4.4",
77
+ layers: "M10 3.2 3.6 6.5 10 9.8l6.4-3.3ZM4.5 10.4 10 13.2l5.5-2.8M4.5 13.6 10 16.4l5.5-2.8",
78
+ box: "M17 6 10 3.2 3 6v8l7 2.8 7-2.8ZM3 6l7 2.8L17 6M10 9v7.8",
79
+ filter: "M4 4.5h12l-4.4 5.1v5l-3.2-1.6V9.6Z",
80
+ columns: "M3.5 4h13v12h-13ZM8.2 4v12M12.8 4v12",
81
+ download: "M10 3.5v8M6.8 8.3 10 11.5l3.2-3.2M4 15v2.5a1.5 1.5 0 0 0 1.5 1.5h9a1.5 1.5 0 0 0 1.5-1.5V15"
82
+ };
83
+ var custom = /* @__PURE__ */ new Map();
84
+ function iconPath(name) {
85
+ return Object.hasOwn(ICON_PATHS, name) ? ICON_PATHS[name] : custom.get(name) ?? null;
86
+ }
87
+ function iconAttributes(o = {}) {
88
+ const size = Number.isFinite(o.size) && o.size > 0 ? o.size : 16;
89
+ return { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 20 20", width: String(size), height: String(size), fill: "none", stroke: "currentColor", "stroke-width": "1.6", "stroke-linecap": "round", "stroke-linejoin": "round", focusable: "false", class: ["icen-symbol", o.className].filter(Boolean).join(" "), ...o.label ? { role: "img", "aria-label": o.label } : { "aria-hidden": "true" } };
90
+ }
91
+ function createIcon(name, o = {}) {
92
+ if (typeof document === "undefined") return null;
93
+ const path = iconPath(name);
94
+ if (!path) return null;
95
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
96
+ for (const [key, value] of Object.entries(iconAttributes(o))) svg.setAttribute(key, value);
97
+ const line = document.createElementNS(svg.namespaceURI, "path");
98
+ line.setAttribute("d", path);
99
+ svg.append(line);
100
+ return svg;
101
+ }
102
+
103
+ // src/behaviors/i18n.ts
104
+ var ICEN_LOCALE_ZH_CN = {
105
+ /* ── aiBranch ── */
106
+ "aiBranch.nextVersion": "\u4E0B\u4E00\u7248\u672C",
107
+ "aiBranch.prevVersion": "\u4E0A\u4E00\u7248\u672C",
108
+ "aiBranch.version": "\u5206\u652F\u7248\u672C",
109
+ /* ── aiChat ── */
110
+ "aiChat.backToBottom": "\u56DE\u5230\u5E95\u90E8",
111
+ "aiChat.backToBottomCount": "\u56DE\u5230\u5E95\u90E8 \xB7 {p0} \u6761\u65B0\u6D88\u606F",
112
+ "aiChat.continue": "\u7EE7\u7EED\u751F\u6210",
113
+ "aiChat.download": "\u4E0B\u8F7D",
114
+ "aiChat.file": "\u6587\u4EF6",
115
+ "aiChat.roleSystem": "\u7CFB",
116
+ "aiChat.roleTool": "\u5DE5",
117
+ "aiChat.roleUser": "\u6211",
118
+ "aiChat.thinking": "\u601D\u8003 {p0}",
119
+ /* ── aiComposer ── */
120
+ "aiComposer.attachAgent": "\u4EE3\u7406",
121
+ "aiComposer.library": "\u6DFB\u52A0\u4E0A\u4E0B\u6587\u4E0E\u547D\u4EE4",
122
+ "aiComposer.searchLibrary": "\u641C\u7D22\u6280\u80FD\u3001\u5DE5\u5177\u3001\u6587\u4EF6\u4E0E\u547D\u4EE4",
123
+ "aiComposer.all": "\u5168\u90E8",
124
+ "aiComposer.skills": "\u6280\u80FD",
125
+ "aiComposer.tools": "\u5DE5\u5177",
126
+ "aiComposer.libraryHint": "\u4E0A\u4E0B\u6587\u968F\u6D88\u606F\u53D1\u9001 \xB7 \u547D\u4EE4\u5148\u586B\u5165\u8F93\u5165\u6846",
127
+ "aiComposer.commandHint": "\u2191 \u2193 \u6D4F\u89C8 \xB7 Enter \u6267\u884C \xB7 Esc \u8FD4\u56DE",
128
+ "aiComposer.refHint": "\u2191 \u2193 \u6D4F\u89C8 \xB7 Enter \u5F15\u7528 \xB7 Esc \u8FD4\u56DE",
129
+ "aiComposer.noLibraryMatch": "\u6CA1\u6709\u5339\u914D\u7684\u9879\u76EE\uFF0C\u8BD5\u8BD5\u5176\u4ED6\u5173\u952E\u8BCD",
130
+ "aiComposer.filesNeedAdapter": "\u9644\u4EF6\u5C1A\u672A\u63A5\u901A\u4E0A\u4F20\u3002\u8BF7\u914D\u7F6E prepareMessage \u540E\u91CD\u8BD5\uFF0C\u6587\u4EF6\u4ECD\u4FDD\u7559\u5728\u6B64\u8BF7\u6C42\u4E2D\u3002",
131
+ "aiComposer.attachDoc": "\u6587\u6863",
132
+ "aiComposer.attachFile": "\u6587\u4EF6",
133
+ "aiComposer.attachFolder": "\u6587\u4EF6\u5939",
134
+ "aiComposer.cacheInvalidHint": "\u5207\u6362\u6A21\u578B\u4F1A\u4F7F prompt \u7F13\u5B58\u5931\u6548",
135
+ "aiComposer.commands": "\u547D\u4EE4",
136
+ "aiComposer.currentTodos": "\u5F53\u524D\u5F85\u529E",
137
+ "aiComposer.done": "\u5B8C\u6210",
138
+ "aiComposer.justNow": "\u521A\u521A \xB7 {p0}",
139
+ "aiComposer.modelAria": "\u6A21\u578B {p0}\uFF08{p1}\uFF09",
140
+ "aiComposer.noCommandMatch": "\u65E0\u5339\u914D\u547D\u4EE4",
141
+ "aiComposer.noModelMatch": "\u65E0\u5339\u914D\u6A21\u578B",
142
+ "aiComposer.noRefMatch": "\u65E0\u5339\u914D\u5F15\u7528",
143
+ "aiComposer.placeholder": "\u8F93\u5165\u6D88\u606F\uFF0C/ \u547D\u4EE4 \xB7 @ \u5F15\u7528 \xB7 Enter \u53D1\u9001",
144
+ "aiComposer.refs": "\u5F15\u7528",
145
+ "aiComposer.removeAttachment": "\u79FB\u9664\u9644\u4EF6 {p0}",
146
+ "aiComposer.removeQueued": "\u79FB\u9664\u6392\u961F\u6D88\u606F {p0}",
147
+ "aiComposer.removeRef": "\u79FB\u9664\u5F15\u7528 {p0}",
148
+ "aiComposer.searchModels": "\u641C\u7D22\u6A21\u578B",
149
+ "aiComposer.selectModel": "\u9009\u62E9\u6A21\u578B",
150
+ "aiComposer.send": "\u53D1\u9001",
151
+ "aiComposer.stop": "\u505C\u6B62\u751F\u6210",
152
+ "aiComposer.stopped": "\u5DF2\u505C\u6B62",
153
+ "aiComposer.todoChip": "\u5F53\u524D\u5F85\u529E {p0}/{p1}{p2}",
154
+ "aiComposer.todoHasError": "\uFF08\u6709\u5931\u8D25\u9879\uFF09",
155
+ /* ── aiCore ── */
156
+ "aiCore.kindBrowser": "\u6D4F\u89C8",
157
+ "aiCore.kindChart": "\u56FE\u8868",
158
+ "aiCore.kindDelete": "\u5220\u9664",
159
+ "aiCore.kindEdit": "\u7F16\u8F91",
160
+ "aiCore.kindFetch": "\u8BF7\u6C42",
161
+ "aiCore.kindGlob": "\u5339\u914D",
162
+ "aiCore.kindGrep": "\u641C\u7D22",
163
+ "aiCore.kindNote": "\u7B14\u8BB0",
164
+ "aiCore.kindPlan": "\u8BA1\u5212",
165
+ "aiCore.kindRead": "\u8BFB\u53D6",
166
+ "aiCore.kindSearch": "\u68C0\u7D22",
167
+ "aiCore.kindShell": "\u547D\u4EE4",
168
+ "aiCore.kindSkill": "\u6280\u80FD",
169
+ "aiCore.kindSubagent": "\u5B50\u4EE3\u7406",
170
+ "aiCore.kindTodo": "\u5F85\u529E",
171
+ "aiCore.kindWrite": "\u5199\u5165",
172
+ "aiCore.statusApproval": "\u5F85\u786E\u8BA4",
173
+ "aiCore.statusCancelled": "\u5DF2\u53D6\u6D88",
174
+ "aiCore.statusDone": "\u5B8C\u6210",
175
+ "aiCore.statusError": "\u5931\u8D25",
176
+ "aiCore.statusPending": "\u7B49\u5F85",
177
+ "aiCore.statusRunning": "\u6267\u884C\u4E2D",
178
+ "aiCore.statusStreaming": "\u6D41\u5F0F\u4E2D",
179
+ "aiCore.todoProgress": "{p0}/{p1} \u5B8C\u6210",
180
+ /* ── aiDiff ── */
181
+ "aiDiff.accepted": "\u5DF2\u63A5\u53D7",
182
+ "aiDiff.binary": "\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u663E\u793A\u6587\u672C\u5DEE\u5F02",
183
+ "aiDiff.empty": "\u65E0\u5DEE\u5F02\u5185\u5BB9",
184
+ "aiDiff.rejected": "\u5DF2\u62D2\u7EDD",
185
+ /* ── aiFeedback ── */
186
+ "aiFeedback.helpful": "\u6709\u5E2E\u52A9",
187
+ "aiFeedback.notHelpful": "\u65E0\u5E2E\u52A9",
188
+ "aiFeedback.reasonCitationMismatch": "\u5F15\u7528\u4E0D\u7B26",
189
+ "aiFeedback.reasonOutdated": "\u5185\u5BB9\u8FC7\u65F6",
190
+ "aiFeedback.reasonUnauthorized": "\u8D8A\u6743\u4FE1\u606F",
191
+ "aiFeedback.reasonUnsourced": "\u65E0\u5F15\u7528\u4F9D\u636E",
192
+ "aiFeedback.reasonWrong": "\u7B54\u6848\u6709\u8BEF",
193
+ "aiFeedback.thanks": "\u611F\u8C22\u53CD\u9988\uFF0C\u5DF2\u5173\u8054\u5230\u8BE5\u6761\u56DE\u7B54\u7684\u8FFD\u8E2A\u8BB0\u5F55",
194
+ /* ── aiPanel ── */
195
+ "aiPanel.audit": "\u5BA1\u8BA1",
196
+ "aiPanel.cacheRead": "\u7F13\u5B58\u8BFB",
197
+ "aiPanel.cacheWrite": "\u7F13\u5B58\u5199",
198
+ "aiPanel.callsTokens": "{p0} \u6B21 \xB7 {p1} tok{p2}",
199
+ "aiPanel.clearAudit": "\u6E05\u9664\u5BA1\u8BA1",
200
+ "aiPanel.context": "\u4E0A\u4E0B\u6587",
201
+ "aiPanel.contextWindow": "\u4E0A\u4E0B\u6587\u7A97\u53E3",
202
+ "aiPanel.errorsCount": "{p0} \u5931\u8D25",
203
+ "aiPanel.files": "\u6587\u4EF6 ({p0})",
204
+ "aiPanel.itemStatus": "{p0}\uFF0C\u72B6\u6001\uFF1A{p1}",
205
+ "aiPanel.noAudit": "\u6682\u65E0\u5BA1\u8BA1\u8BB0\u5F55",
206
+ "aiPanel.noUsage": "\u6682\u65E0\u7528\u91CF",
207
+ "aiPanel.reasoning": "\u63A8\u7406",
208
+ "aiPanel.requestsCount": "{p0} \u6B21\u8BF7\u6C42",
209
+ "aiPanel.ringPercent": "\u4E0A\u4E0B\u6587\u7A97\u53E3\u5360\u7528 {p0}%\uFF0C\u70B9\u51FB\u67E5\u770B\u8BE6\u60C5",
210
+ "aiPanel.ringUsed": "\u5DF2\u7528 {p0}\uFF0C\u70B9\u51FB\u67E5\u770B\u8BE6\u60C5",
211
+ "aiPanel.taskProgress": "\u4EFB\u52A1\u8FDB\u5EA6",
212
+ "aiPanel.toolsCount": "{p0} \u5DE5\u5177",
213
+ "aiPanel.usage": "\u7528\u91CF",
214
+ "aiPanel.usageAria": "\u7528\u91CF\uFF1A{p0}",
215
+ /* ── aiProvider ── */
216
+ "aiProvider.audioUrlOnly": "\u4EC5 URL \u7684\u97F3\u9891\uFF08input_audio \u9700 base64\uFF0C\u8BF7\u5148\u53D6\u56DE\u5185\u8054\uFF09",
217
+ "aiProvider.audioUrlOnlyAnthropic": "\u4EC5 URL \u7684\u97F3\u9891\uFF08audio \u5757\u9700 base64 source\uFF09",
218
+ "aiProvider.base64File": "base64 \u6587\u4EF6\uFF08file \u90E8\u4EF6\u9700 Files API file_id\uFF0C\u8BF7\u7F6E\u4E8E url \u69FD\uFF09",
219
+ "aiProvider.corsHint": "\uFF1B\u6D4F\u89C8\u5668\u76F4\u8FDE {p0}\uFF08{p1}\uFF09\u5F88\u53EF\u80FD\u88AB CORS \u62E6\u622A\uFF0C\u5EFA\u8BAE\u7ECF\u4EE3\u7406/\u81EA\u5EFA\u7F51\u5173\u8F6C\u53D1",
220
+ "aiProvider.emptyResponse": "\u54CD\u5E94\u4E3A\u7A7A",
221
+ "aiProvider.glmLabel": "GLM\uFF08\u667A\u8C31\uFF09",
222
+ "aiProvider.invalidJson": "\u54CD\u5E94\u4E0D\u662F\u5408\u6CD5 JSON",
223
+ "aiProvider.kimiLabel": "Kimi\uFF08\u6708\u4E4B\u6697\u9762\uFF09",
224
+ "aiProvider.networkFailed": "\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25\uFF1A{p0}{p1}",
225
+ "aiProvider.nonPdfDocument": "\u975E PDF \u6587\u4EF6\u5757\uFF08document \u5757\u4E3A application/pdf\uFF0C\u6536\u5230 {p0}\uFF09",
226
+ "aiProvider.requestCancelled": "\u8BF7\u6C42\u5DF2\u53D6\u6D88",
227
+ "aiProvider.resourceRef": "[\u8D44\u6E90] {p0} ({p1})",
228
+ "aiProvider.streamError": "\u6D41\u5F0F\u54CD\u5E94\u9519\u8BEF",
229
+ "aiProvider.toolRoleNoCallId": "role 'tool' \u4E14\u7F3A toolCallId\uFF08openai \u65CF\u9700 tool_call_id\uFF09",
230
+ "aiProvider.toolRoleNoUseId": "role 'tool' \u4E14\u7F3A toolCallId\uFF08anthropic \u65CF\u6620\u5C04 tool_result \u9700 tool_use_id\uFF09",
231
+ "aiProvider.unsupported": "{p0} \u7EBF\u534F\u8BAE\u4E0D\u652F\u6301 {p1}",
232
+ "aiProvider.videoUnsupported": "video \u8F93\u5165\uFF08\u65E0\u4E3B\u6D41 chat API \u652F\u6301\uFF09",
233
+ "aiProvider.videoUnsupportedAnthropic": "video \u8F93\u5165",
234
+ /* ── aiThreads ── */
235
+ "aiThreads.actionsAria": "\u4F1A\u8BDD\u64CD\u4F5C",
236
+ "aiThreads.archived": "\u5DF2\u5F52\u6863",
237
+ /* ── aiTool ── */
238
+ "aiTool.allow": "\u5141\u8BB8",
239
+ "aiTool.reject": "\u62D2\u7EDD",
240
+ /* ── aiTools ── */
241
+ "aiTools.chart.area": "line \u591A\u7CFB\u5217\u586B\u5145\u9762\u79EF",
242
+ "aiTools.chart.data": "\u4EFB\u610F\u7EF4\u5EA6\u539F\u59CB\u8BB0\u5F55 + dims \u5B57\u6BB5\u6620\u5C04\uFF08\u81EA\u52A8\u900F\u89C6\u6210 \u7C7B\u76EE \xD7 \u7CFB\u5217\uFF09",
243
+ "aiTools.chart.dates": "\u8D21\u732E\u65E5\u5386 ISO \u65E5\u671F\uFF08\u4E0E values \u914D\u5BF9\uFF09",
244
+ "aiTools.chart.description": "\u5728\u7528\u6237\u754C\u9762\u6E32\u67D3\u4E00\u5F20\u56FE\u8868\u53EF\u89C6\u5316\u3002\u8F93\u5165\u4E00\u4EFD\u56FE\u8868\u89C4\u683C\uFF08\u7EAF JSON\uFF09\uFF1Alabels+values\uFF08\u7C7B\u76EE\u6570\u503C\uFF09\u3001series\uFF08\u591A\u7CFB\u5217\uFF09\u3001segments\uFF08\u5360\u6BD4\uFF09\u3001points\uFF08\u6563\u70B9\u6C14\u6CE1\uFF09\u3001dates\uFF08\u8D21\u732E\u65E5\u5386\uFF09\u4EFB\u7ED9\u5176\u4E00\uFF1Btype \u7F3A\u7701\u65F6\u6309\u6570\u636E\u5F62\u6001\u81EA\u52A8\u9009\u578B\uFF08\u65F6\u95F4\u5E8F\u5217\u2192\u6298\u7EBF\u3001\u5360\u6BD4\u2192\u73AF\u5F62\u3001\u65E5\u671F\u2192\u8D21\u732E\u65E5\u5386\u3001\u5750\u6807\u70B9\u2192\u6563\u70B9\uFF09\u3002\u56FE\u8868\u5E26\u4EA4\u4E92\uFF08\u60AC\u505C\u63D0\u793A/\u70B9\u51FB/\u53F3\u952E\uFF09\u4E0E\u53EF\u9690\u85CF\u56FE\u4F8B\u3002",
245
+ "aiTools.chart.dims": "data[] \u7684\u5B57\u6BB5\u6620\u5C04\uFF1A{label?, series?, value?, x?, y?}",
246
+ "aiTools.chart.format": "\u6570\u503C\u683C\u5F0F\u5316\uFF1A{notation?: compact|percent|ms, unit?}",
247
+ "aiTools.chart.labels": "\u7C7B\u76EE\u8F74\uFF08line/vbar/hbar\uFF09",
248
+ "aiTools.chart.legend": "false = \u521D\u59CB\u9690\u85CF\u56FE\u4F8B",
249
+ "aiTools.chart.lttb": "\u5927\u6570\u636E\u91CF\u964D\u91C7\u6837\uFF1Atrue=\u81EA\u52A8\u4E0A\u9650\uFF0C\u6570\u5B57=\u6700\u5927\u4FDD\u7559\u70B9\u6570\uFF08\u6E32\u67D3\u5C42\u5951\u7EA6\uFF0C\u968F\u56FE\u8868\u91CD\u5EA6\u573A\u666F\u843D\u5730\uFF09",
250
+ "aiTools.chart.marks": "\u6807\u8BB0\u5C42\uFF08\u9608\u503C\u7EBF/\u53C2\u8003\u533A\u95F4/\u5F3A\u8C03\u70B9\uFF09\uFF0C\u5143\u7D20\u4E3A\u7EAF JSON \u63CF\u8FF0\u5B50\uFF08\u6E32\u67D3\u5C42\u5951\u7EA6\uFF0C\u968F\u56FE\u8868\u91CD\u5EA6\u573A\u666F\u843D\u5730\uFF09",
251
+ "aiTools.chart.points": "\u6563\u70B9/\u6C14\u6CE1\uFF1A[{x,y,size?,label?}]",
252
+ "aiTools.chart.radarAxes": "\u96F7\u8FBE\u8F74\u540D",
253
+ "aiTools.chart.segments": "\u5360\u6BD4\u578B\uFF08donut/stack\uFF09\uFF1A[{label,value}]",
254
+ "aiTools.chart.series": "\u591A\u7CFB\u5217\uFF08line/vbar/radar\uFF09",
255
+ "aiTools.chart.stacked": "vbar \u591A\u7CFB\u5217\u5806\u53E0\uFF08\u9ED8\u8BA4\u5206\u7EC4\uFF09",
256
+ "aiTools.chart.title": "\u56FE\u8868\u6807\u9898\uFF08\u6E32\u67D3\u5728\u5934\u90E8\u884C\u5DE6\u4FA7\uFF09",
257
+ "aiTools.chart.tooltip": "false = \u5173\u95ED\u5185\u7F6E\u63D0\u793A",
258
+ "aiTools.chart.type": "\u56FE\u8868\u7C7B\u578B\uFF0C\u7F3A\u7701\u81EA\u52A8\u63A8\u65AD",
259
+ "aiTools.chart.values": "\u5355\u7CFB\u5217\u6570\u503C\uFF08\u4E0E labels \u914D\u5BF9\uFF09",
260
+ "aiTools.chart.zoom": "\u7F29\u653E\u4EA4\u4E92\uFF1A'x'=\u7C7B\u76EE\u8F74\u533A\u95F4\u7F29\u653E\uFF0C'brush'=\u5237\u9009\u7F29\u653E",
261
+ "aiTools.datatable.columns": "\u5217\u5B9A\u4E49\u6570\u7EC4\uFF1A\u6BCF\u9879 { key, title, sortable?, filterable? }\uFF08key \u5BF9\u5E94 rows \u5143\u7D20\u5B57\u6BB5\u540D\uFF09",
262
+ "aiTools.datatable.description": "\u5728\u7528\u6237\u754C\u9762\u6E32\u67D3\u4E00\u4E2A\u53EF\u641C\u7D22\u3001\u5206\u9875\u3001\u53EF\u5BFC\u51FA CSV \u7684\u6570\u636E\u8868\u683C\u3002\u8F93\u5165\u7EAF JSON\uFF1Acolumns \u4E3A\u5217\u5B9A\u4E49\uFF08key/title \u5FC5\u586B\uFF09\uFF0Crows \u4E3A\u884C\u5BF9\u8C61\u6570\u7EC4\uFF08\u5B57\u6BB5\u540D\u5BF9\u9F50\u5217 key\uFF09\u3002\u8FD4\u56DE { title, rows: \u884C\u6570 } \u6458\u8981\u3002",
263
+ "aiTools.datatable.rows": "\u884C\u6570\u636E\u6570\u7EC4\uFF1A\u6BCF\u9879\u4E3A\u4E00\u4E2A\u5BF9\u8C61\uFF0C\u5B57\u6BB5\u540D\u5BF9\u9F50 columns[].key",
264
+ "aiTools.datatable.title": "\u8868\u683C\u6807\u9898\uFF08\u6E32\u67D3\u5728\u5DE5\u5177\u680F\u5DE6\u4FA7\uFF09",
265
+ "aiTools.execFailed": "\u5DE5\u5177\u6267\u884C\u5931\u8D25\uFF1A{p0}",
266
+ "aiTools.execTimeout": "\u6267\u884C\u8D85\u65F6\uFF08{p0}ms\uFF09",
267
+ "aiTools.manifestHeader": "\u53EF\u7528 UI \u5DE5\u5177\uFF08\u8C03\u7528\u540E\u5728\u7528\u6237\u754C\u9762\u6302\u8F7D\u53EF\u89C6\u5316\u7ED3\u679C\uFF09\uFF1A",
268
+ "aiTools.missingRequired": "\u7F3A\u5C11\u5FC5\u586B\u5B57\u6BB5",
269
+ "aiTools.none": "\uFF08\u65E0\u5DF2\u6CE8\u518C\u7684 UI \u5DE5\u5177\uFF09",
270
+ "aiTools.notInEnum": "\u4E0D\u5728\u5141\u8BB8\u53D6\u503C\u5185\uFF08{p0}\uFF09",
271
+ "aiTools.numMax": "\u6570\u503C {p0} \u5927\u4E8E\u6700\u5927\u503C {p1}",
272
+ "aiTools.numMin": "\u6570\u503C {p0} \u5C0F\u4E8E\u6700\u5C0F\u503C {p1}",
273
+ "aiTools.removeMount": "\u79FB\u9664 {p0} \u6302\u8F7D",
274
+ "aiTools.strMax": "\u957F\u5EA6 {p0} \u8D85\u8FC7\u4E0A\u9650 {p1}",
275
+ "aiTools.strMin": "\u957F\u5EA6 {p0} \u5C0F\u4E8E\u4E0B\u9650 {p1}",
276
+ "aiTools.typeMismatch": "\u671F\u671B {p0}\uFF0C\u5B9E\u5F97 {p1}",
277
+ "aiTools.validationFailed": "\u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF08{p0} \u9879\uFF09",
278
+ /* ── autocomplete ── */
279
+ "suggestions.loading": "\u6B63\u5728\u67E5\u627E\u5EFA\u8BAE\u2026",
280
+ "suggestions.error": "\u6682\u65F6\u65E0\u6CD5\u52A0\u8F7D\u5EFA\u8BAE\uFF0C\u8BF7\u91CD\u8BD5\u3002",
281
+ "suggestions.options": "\u5EFA\u8BAE",
282
+ "suggestions.empty": "\u6CA1\u6709\u5339\u914D\u7684\u5EFA\u8BAE",
283
+ "autocomplete.empty": "\u65E0\u5339\u914D\u7ED3\u679C",
284
+ /* ── backTop ── */
285
+ "backTop.label": "\u56DE\u5230\u9876\u90E8",
286
+ /* ── carousel ── */
287
+ "carousel.item": "\u7B2C {p0} \u5F20",
288
+ "carousel.region": "\u8F6E\u64AD",
289
+ "carousel.slide": "\u5E7B\u706F\u7247",
290
+ "carousel.status": "\u7B2C {p0} / {p1} \u5F20",
291
+ /* ── charts ── */
292
+ "charts.calendarWeekdays": ["\u4E00", "", "\u4E09", "", "\u4E94", "", "\u65E5"],
293
+ "charts.few": "\u5C11",
294
+ "charts.hideLegend": "\u9690\u85CF\u56FE\u4F8B",
295
+ "charts.loading": "\u52A0\u8F7D\u4E2D",
296
+ "charts.many": "\u591A",
297
+ "charts.monthLabel": "{p0}\u6708",
298
+ "charts.noData": "\u6682\u65E0\u6570\u636E",
299
+ "charts.resetZoom": "\u91CD\u7F6E\u7F29\u653E",
300
+ "charts.sampled": "\u5DF2\u62BD\u6837 {p0}\u2192{p1} \u70B9",
301
+ "charts.type.area": "\u9762\u79EF\u56FE",
302
+ "charts.type.donut": "\u73AF\u5F62\u56FE",
303
+ "charts.type.gauge": "\u4EEA\u8868\u76D8",
304
+ "charts.type.graph": "\u77E5\u8BC6\u56FE\u8C31",
305
+ "charts.type.heatmap": "\u70ED\u529B\u56FE",
306
+ "charts.type.line": "\u6298\u7EBF\u56FE",
307
+ "charts.type.map": "\u5730\u56FE",
308
+ "charts.type.radar": "\u96F7\u8FBE\u56FE",
309
+ "charts.type.scatter": "\u6563\u70B9\u56FE",
310
+ "charts.type.sparkline": "\u8FF7\u4F60\u8D8B\u52BF",
311
+ "charts.seriesName": "\u7CFB\u5217{p0}",
312
+ "charts.showLegend": "\u663E\u793A\u56FE\u4F8B",
313
+ "charts.value": "\u6570\u503C",
314
+ /* ── themeSwitcher ── */
315
+ "themeSwitcher.appearance": "\u5916\u89C2",
316
+ "themeSwitcher.theme": "\u4E3B\u9898",
317
+ "themeSwitcher.primitive": "\u539F\u8BED",
318
+ "themeSwitcher.mode": "\u660E\u6697",
319
+ "themeSwitcher.light": "\u6D45\u8272",
320
+ "themeSwitcher.dark": "\u6DF1\u8272",
321
+ "themeSwitcher.modern": "\u73B0\u4EE3",
322
+ "themeSwitcher.dongyun": "\u4E1C\u97F5",
323
+ "themeSwitcher.jugou": "\u5DE8\u6784",
324
+ "themeSwitcher.piano": "\u94A2\u7434",
325
+ "themeSwitcher.monet": "\u83AB\u5948",
326
+ "themeSwitcher.vangogh": "\u68B5\u9AD8",
327
+ "themeSwitcher.chinese": "\u4E2D\u5F0F",
328
+ "themeSwitcher.soviet": "\u82CF\u5F0F",
329
+ "themeSwitcher.pixel": "\u50CF\u7D20",
330
+ /* ── codeBlock ── */
331
+ "codeBlock.collapse": "\u6536\u8D77",
332
+ "codeBlock.region": "\u4EE3\u7801\u5185\u5BB9",
333
+ "codeBlock.copyFailed": "\u672A\u80FD\u590D\u5236\uFF0C\u8BF7\u91CD\u8BD5\u3002",
334
+ "codeBlock.copyRetry": "\u91CD\u8BD5\u590D\u5236",
335
+ "codeBlock.copy": "\u590D\u5236",
336
+ "codeBlock.foldCollapse": "\u6298\u53E0\u4EE3\u7801",
337
+ "codeBlock.foldExpand": "\u5C55\u5F00\u4EE3\u7801",
338
+ "codeBlock.linesTotal": "\u5171 {p0} \u884C",
339
+ "codeBlock.numHide": "\u9690\u85CF\u884C\u53F7",
340
+ "codeBlock.numShow": "\u663E\u793A\u884C\u53F7",
341
+ "codeBlock.tailAria": "\u5C55\u5F00\u6216\u6536\u8D77\u4EE3\u7801",
342
+ "codeBlock.wrapOff": "\u5173\u95ED\u6362\u884C",
343
+ "codeBlock.wrapOn": "\u81EA\u52A8\u6362\u884C",
344
+ /* ── common ── */
345
+ "common.accept": "\u63A5\u53D7",
346
+ "common.add": "\u65B0\u589E",
347
+ "common.all": "\u5168\u90E8",
348
+ "common.approve": "\u6279\u51C6",
349
+ "common.cancel": "\u53D6\u6D88",
350
+ "common.actionFailed": "\u64CD\u4F5C\u672A\u5B8C\u6210\uFF0C\u8BF7\u91CD\u8BD5\u3002",
351
+ "common.close": "\u5173\u95ED",
352
+ "common.confirm": "\u786E\u8BA4",
353
+ "common.copied": "\u5DF2\u590D\u5236",
354
+ "common.copy": "\u590D\u5236",
355
+ "common.delete": "\u5220\u9664",
356
+ "common.enable": "\u542F\u7528",
357
+ "common.exportCsv": "\u5BFC\u51FA CSV",
358
+ "common.healthy": "\u6B63\u5E38",
359
+ "common.input": "\u8F93\u5165",
360
+ "common.loadingEllipsis": "\u52A0\u8F7D\u4E2D\u2026",
361
+ "common.official": "\u5B98\u65B9",
362
+ "common.output": "\u8F93\u51FA",
363
+ "common.page": "\u7B2C {p0} \u9875",
364
+ "common.reject": "\u62D2\u7EDD",
365
+ "common.remove": "\u79FB\u9664",
366
+ "common.requestAccess": "\u7533\u8BF7\u8BBF\u95EE",
367
+ "common.requestAccessFor": "\u7533\u8BF7\u8BBF\u95EE {p0}",
368
+ "common.restricted": "\u53D7\u9650",
369
+ "common.retry": "\u91CD\u8BD5",
370
+ "common.rows": "{p0} \u884C",
371
+ "common.run": "\u8FD0\u884C",
372
+ "common.steps": "{p0} \u6B65",
373
+ "common.submit": "\u63D0\u4EA4",
374
+ "common.unknown": "\u672A\u77E5",
375
+ /* ── contextMenu ── */
376
+ "contextMenu.back": "\u540E\u9000",
377
+ "contextMenu.copyPageLink": "\u590D\u5236\u9875\u9762\u94FE\u63A5",
378
+ "contextMenu.forward": "\u524D\u8FDB",
379
+ "contextMenu.refresh": "\u5237\u65B0",
380
+ /* ── copy ── */
381
+ "copy.copied": "\u5DF2\u590D\u5236",
382
+ "copy.failed": "\u672A\u80FD\u590D\u5236\uFF0C\u8BF7\u91CD\u8BD5\u3002",
383
+ "copy.denied": "clipboard API \u88AB\u62D2\uFF08{p0}\uFF09\u4E14\u56DE\u9000 execCommand \u5931\u8D25",
384
+ "copy.unavailable": "\u526A\u8D34\u677F\u4E0D\u53EF\u7528\uFF08\u65E0 clipboard API \u4E14 execCommand \u56DE\u9000\u5931\u8D25\uFF09",
385
+ "copy.unknown": "\u672A\u77E5\u9519\u8BEF",
386
+ /* ── datatable ── */
387
+ "datatable.clear": "\u6E05\u7A7A",
388
+ "datatable.collapseGroup": "\u6298\u53E0\u5206\u7EC4 {p0}",
389
+ "datatable.columnSettingsAria": "\u5217\u8BBE\u7F6E",
390
+ "datatable.countUnit": "{p0} \u6761",
391
+ "datatable.editHint": "\u7F16\u8F91\u300C{p0}\u300D\uFF1A\u53CC\u51FB\u6216\u6309 Enter",
392
+ "datatable.editHintWithValue": "{p0}\uFF08\u53CC\u51FB\u6216\u6309 Enter \u7F16\u8F91\u300C{p1}\u300D\uFF09",
393
+ "datatable.empty": "\u6682\u65E0\u6570\u636E",
394
+ "datatable.emptyValue": "\uFF08\u7A7A\uFF09",
395
+ "datatable.expandGroup": "\u5C55\u5F00\u5206\u7EC4 {p0}",
396
+ "datatable.expandRow": "\u5C55\u5F00\u884C",
397
+ "datatable.filterChipAria": "\u7B5B\u9009\u300C{p0}\u300D",
398
+ "datatable.pageSizeAria": "\u6BCF\u9875\u6761\u6570",
399
+ "datatable.paginationAria": "\u5206\u9875",
400
+ "datatable.reset": "\u91CD\u7F6E",
401
+ "datatable.resizeAria": "\u8C03\u6574\u300C{p0}\u300D\u5217\u5BBD",
402
+ "datatable.searchAria": "\u641C\u7D22\u8868\u683C",
403
+ "datatable.searchPlaceholder": "\u641C\u7D22\u2026",
404
+ "datatable.selectAll": "\u5168\u9009",
405
+ "datatable.selectRow": "\u9009\u62E9\u8BE5\u884C",
406
+ "datatable.selectedCount": "\u5DF2\u9009 {p0} \u9879",
407
+ "datatable.totalCount": "\u5171 {p0} \u6761",
408
+ "datatable.totalLabel": "\u5408\u8BA1",
409
+ /* ── datePicker ── */
410
+ "datePicker.noDates": "\u6CA1\u6709\u53EF\u9009\u65E5\u671F",
411
+ "datePicker.clear": "\u6E05\u9664",
412
+ "datePicker.nextMonth": "\u4E0B\u4E00\u6708",
413
+ "datePicker.prevMonth": "\u4E0A\u4E00\u6708",
414
+ "datePicker.selectDate": "\u9009\u62E9\u65E5\u671F",
415
+ "datePicker.today": "\u4ECA\u5929",
416
+ "datePicker.weekdays": ["\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D", "\u65E5"],
417
+ "datePicker.weekdaysSunFirst": ["\u65E5", "\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D"],
418
+ "datePicker.yearMonth": "{p0} \u5E74 {p1} \u6708",
419
+ /* ── formValidate ── */
420
+ "formValidate.email": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u90AE\u7BB1\u5730\u5740",
421
+ "formValidate.invalid": "\u8F93\u5165\u4E0D\u5408\u6CD5",
422
+ "formValidate.matches": "\u4E24\u6B21\u8F93\u5165\u4E0D\u4E00\u81F4",
423
+ "formValidate.max": "\u4E0D\u80FD\u5927\u4E8E {max}",
424
+ "formValidate.maxlength": "\u6700\u591A\u8F93\u5165 {max} \u4E2A\u5B57\u7B26",
425
+ "formValidate.min": "\u4E0D\u80FD\u5C0F\u4E8E {min}",
426
+ "formValidate.minlength": "\u81F3\u5C11\u8F93\u5165 {min} \u4E2A\u5B57\u7B26",
427
+ "formValidate.number": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u6570\u5B57",
428
+ "formValidate.pattern": "\u8F93\u5165\u683C\u5F0F\u4E0D\u6B63\u786E",
429
+ "formValidate.required": "\u6B64\u9879\u4E3A\u5FC5\u586B\u9879",
430
+ "formValidate.selectPlaceholder": "\u8BF7\u9009\u62E9",
431
+ "formValidate.step": "\u6570\u503C\u5FC5\u987B\u662F {step} \u7684\u500D\u6570",
432
+ "formValidate.summary": "\u8868\u5355\u6709 {p0} \u9879\u5185\u5BB9\u9700\u8981\u4FEE\u6B63\uFF0C\u8BF7\u4FEE\u6539\u540E\u91CD\u65B0\u63D0\u4EA4",
433
+ "formValidate.tool.description": "\u5411\u7528\u6237\u6E32\u67D3\u4E00\u4E2A\u7ED3\u6784\u5316\u8868\u5355\u6536\u96C6\u8F93\u5165",
434
+ "formValidate.tool.fields": "\u5B57\u6BB5\u5217\u8868",
435
+ "formValidate.tool.help": "\u5B57\u6BB5\u4E0B\u65B9\u7684\u5E2E\u52A9\u6587\u6848",
436
+ "formValidate.tool.label": "\u5B57\u6BB5\u6807\u7B7E",
437
+ "formValidate.tool.max": "\u6700\u5927\u503C\uFF08number \u7528\uFF09",
438
+ "formValidate.tool.min": "\u6700\u5C0F\u503C\uFF08number \u7528\uFF09",
439
+ "formValidate.tool.name": "\u5B57\u6BB5\u540D\uFF08\u63D0\u4EA4\u503C\u7684 key\uFF09",
440
+ "formValidate.tool.options": "select \u9009\u9879\uFF08\u5B57\u7B26\u4E32\u6216 {label,value}\uFF09",
441
+ "formValidate.tool.pattern": "\u6B63\u5219\u7EA6\u675F\uFF08HTML pattern \u8BED\u6CD5\uFF0C\u6574\u4E32\u5339\u914D\uFF09",
442
+ "formValidate.tool.required": "\u662F\u5426\u5FC5\u586B",
443
+ "formValidate.tool.submitText": "\u63D0\u4EA4\u6309\u94AE\u6587\u6848\uFF0C\u9ED8\u8BA4\u300C\u63D0\u4EA4\u300D",
444
+ "formValidate.tool.title": "\u8868\u5355\u6807\u9898\uFF08\u53EF\u9009\uFF09",
445
+ "formValidate.tool.type": "\u63A7\u4EF6\u7C7B\u578B\uFF0C\u9ED8\u8BA4 text",
446
+ "formValidate.url": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u94FE\u63A5\u5730\u5740",
447
+ /* ── input ── */
448
+ "input.clearSearch": "\u6E05\u7A7A\u641C\u7D22",
449
+ "input.hidePassword": "\u9690\u85CF\u5BC6\u7801",
450
+ "input.otpPosition": "\u7B2C {p0} \u4F4D\uFF0C\u5171 {p1} \u4F4D",
451
+ "panel.expand": "\u5C55\u5F00",
452
+ "panel.collapse": "\u6298\u53E0",
453
+ "input.showPassword": "\u663E\u793A\u5BC6\u7801",
454
+ /* ── kbAccess ── */
455
+ "kbAccess.approveTip": "\u6279\u51C6\u8BE5\u8BBF\u95EE\u7533\u8BF7",
456
+ "kbAccess.approvedBadge": "\u5DF2\u6279\u51C6 \xB7 {p0}",
457
+ "kbAccess.approverNotePlaceholder": "\u7ED9\u8BF7\u6C42\u4EBA\u7684\u7559\u8A00\uFF08\u53EF\u9009\uFF09",
458
+ "kbAccess.daysLeft": "{p0} \u5929\u540E",
459
+ "kbAccess.defaultSubject": "\u8BE5\u5185\u5BB9",
460
+ "kbAccess.deniedHint": "\u672A\u901A\u8FC7\u2014\u2014\u53EF\u8C03\u6574\u7406\u7531\u540E\u91CD\u65B0\u7533\u8BF7",
461
+ "kbAccess.denyNoteLabel": "\u62D2\u7EDD\u7559\u8A00\uFF08\u53EF\u9009\uFF09",
462
+ "kbAccess.duration30d": "30 \u5929",
463
+ "kbAccess.duration7d": "7 \u5929",
464
+ "kbAccess.durationForever": "\u6C38\u4E45",
465
+ "kbAccess.durationLabel": "\u8BBF\u95EE\u65F6\u957F",
466
+ "kbAccess.expired": "\u5DF2\u5230\u671F",
467
+ "kbAccess.expiresAt": "{p0}\u5230\u671F",
468
+ "kbAccess.grantAsPrefix": "\u6279\u51C6\u4E3A{p0}",
469
+ "kbAccess.grantRoleLabel": "\u6279\u51C6\u89D2\u8272",
470
+ "kbAccess.hoursLeft": "{p0} \u5C0F\u65F6\u540E",
471
+ "kbAccess.minutesLeft": "{p0} \u5206\u949F\u540E",
472
+ "kbAccess.ownerLabel": "\u5185\u5BB9\u8D1F\u8D23\u4EBA",
473
+ "kbAccess.ownerPrefix": "\u8D1F\u8D23\u4EBA\uFF1A{p0}",
474
+ "kbAccess.reapply": "\u91CD\u65B0\u7533\u8BF7",
475
+ "kbAccess.reasonLabel": "\u7533\u8BF7\u7406\u7531",
476
+ "kbAccess.reasonPlaceholder": "\u8BF4\u660E\u4E1A\u52A1\u539F\u56E0\uFF08\u5BA1\u6279\u4EBA\u53EF\u89C1\uFF09",
477
+ "kbAccess.rejectTip": "\u62D2\u7EDD\u8BE5\u8BBF\u95EE\u7533\u8BF7",
478
+ "kbAccess.requestRenewal": "\u7533\u8BF7\u7EED\u671F",
479
+ "kbAccess.requestToPrefix": "\u8BF7\u6C42\u5C06\u53D1\u9001\u7ED9\uFF1A{p0}",
480
+ "kbAccess.restrictedContent": "\u53D7\u9650\u5185\u5BB9",
481
+ "kbAccess.restrictedHint": "\u6B64\u5185\u5BB9\u4EC5\u5BF9\u6388\u6743\u8005\u5F00\u653E",
482
+ "kbAccess.revokedHint": "\u8BBF\u95EE\u5DF2\u88AB\u6536\u56DE",
483
+ "kbAccess.roleLabel": "\u7533\u8BF7\u89D2\u8272",
484
+ "kbAccess.submitRequest": "\u63D0\u4EA4\u7533\u8BF7",
485
+ "kbAccess.switchIdentity": "\u5207\u6362\u8EAB\u4EFD",
486
+ "kbAccess.switchIdentityTip": "\u5207\u6362\u8EAB\u4EFD\u540E\u91CD\u65B0\u68C0\u67E5\u6743\u9650",
487
+ "kbAccess.unknownRequester": "\u672A\u77E5\u8BF7\u6C42\u4EBA",
488
+ "kbAccess.waitingApproval": "\u7B49\u5F85 {p0} \u5BA1\u6279",
489
+ /* ── kbAcl ── */
490
+ "kbAcl.breakHint": "\u505C\u6B62\u4ECE\u7236\u7EA7\u7EE7\u627F\uFF0C\u6539\u4E3A\u72EC\u7ACB\u7BA1\u7406\u672C\u9879\u6743\u9650",
491
+ "kbAcl.breakInheritance": "\u505C\u6B62\u7EE7\u627F",
492
+ "kbAcl.brokenNotice": "\u5DF2\u505C\u6B62\u7EE7\u627F\u2014\u2014\u672C\u9879\u6743\u9650\u72EC\u7ACB\u4E8E\u7236\u7EA7",
493
+ "kbAcl.detachedChildren": "{p0}\uFF1A{p1} \u4E2A\u5B50\u9879\u5DF2\u8131\u79BB",
494
+ "kbAcl.directAccess": "\u76F4\u63A5\u8BBF\u95EE",
495
+ "kbAcl.exception": "\u4F8B\u5916",
496
+ "kbAcl.expiredOn": "\u5DF2\u8FC7\u671F\uFF08{p0}\uFF09",
497
+ "kbAcl.expiresIn": "{p0} \u5230\u671F",
498
+ "kbAcl.grantAccess": "\u6388\u4E88\u8BBF\u95EE",
499
+ "kbAcl.inheritedFrom": "\u7EE7\u627F\u81EA {p0}",
500
+ "kbAcl.inheritedFromLabel": "\u7EE7\u627F\u81EA",
501
+ "kbAcl.linkAccess": "\u94FE\u63A5\u8BBF\u95EE",
502
+ "kbAcl.noDirect": "\u6682\u65E0\u76F4\u63A5\u6388\u6743",
503
+ "kbAcl.noLinks": "\u65E0\u94FE\u63A5",
504
+ "kbAcl.removeEntry": "\u79FB\u9664 {p0}",
505
+ "kbAcl.restoreHint": "\u6062\u590D\u7EE7\u627F\u5C06\u4E22\u5F03\u672C\u9879\u81EA\u5B9A\u4E49\u7684\u6743\u9650",
506
+ "kbAcl.restoreInheritance": "\u6062\u590D\u7EE7\u627F",
507
+ "kbAcl.rootNotice": "\u6839\u7EA7\u8D44\u6E90\u2014\u2014\u65E0\u7236\u7EA7\u7EE7\u627F",
508
+ "kbAcl.systemBadge": "\u7CFB\u7EDF\u7BA1\u7406",
509
+ "kbAcl.systemNote": "\u7CFB\u7EDF\u81EA\u52A8\uFF0C\u4E0D\u53EF\u624B\u5DE5\u79FB\u9664",
510
+ "kbAcl.unnamedResource": "\u672A\u547D\u540D\u8D44\u6E90",
511
+ "kbAcl.visibleToCount": "\u5BF9 {p0} \u4EBA\u53EF\u89C1",
512
+ /* ── kbAnswer ── */
513
+ "kbAnswer.chartBar": "\u67F1\u72B6",
514
+ "kbAnswer.chartLine": "\u6298\u7EBF",
515
+ "kbAnswer.chartPie": "\u997C\u56FE",
516
+ "kbAnswer.exportNoun": "\u5BFC\u51FA {p0}",
517
+ "kbAnswer.filtered": "\u5DF2\u6309\u4F60\u7684\u6570\u636E\u6743\u9650\u8FC7\u6EE4",
518
+ "kbAnswer.filteredDetail": "\u5DF2\u6309\u4F60\u7684\u6570\u636E\u6743\u9650\u8FC7\u6EE4\uFF1A{p0}",
519
+ "kbAnswer.followups": "\u8FFD\u95EE\u5EFA\u8BAE",
520
+ "kbAnswer.hitAssets": "\u547D\u4E2D\u8D44\u4EA7\uFF1A{p0}",
521
+ "kbAnswer.kindCurated": "\u4EBA\u5DE5\u7B56\u5C55",
522
+ "kbAnswer.kindFunction": "\u51FD\u6570",
523
+ "kbAnswer.kindParameterized": "\u53C2\u6570\u5316\u67E5\u8BE2",
524
+ "kbAnswer.maskedPrefix": "\u5DF2\u8131\u654F\u5217\uFF1A",
525
+ "kbAnswer.rerunTip": "\u53C2\u6570\u53EF\u6539\u91CD\u8DD1",
526
+ "kbAnswer.truncated": "\u5DF2\u622A\u65AD\uFF1A\u663E\u793A {p0}/{p1} \u884C{p2}",
527
+ "kbAnswer.verifiedBadge": "\u8BA4\u8BC1\u7B54\u6848",
528
+ /* ── kbAudit ── */
529
+ "kbAudit.actionDetail": "\u5904\u7F6E\uFF1A{p0} \xB7 {p1}",
530
+ "kbAudit.actionLabel": "\u5904\u7F6E",
531
+ "kbAudit.alertedNote": "\u5DF2\u5B9E\u65F6\u544A\u8B66\u5B89\u5168\u56E2\u961F",
532
+ "kbAudit.channelFilterAria": "\u5BA1\u8BA1\u901A\u9053\u8FC7\u6EE4",
533
+ "kbAudit.channelPermission": "\u6743\u9650\u53D8\u66F4",
534
+ "kbAudit.channelRead": "\u8BFB\u53D6",
535
+ "kbAudit.channelRequest": "\u7533\u8BF7",
536
+ "kbAudit.channelSystem": "\u7CFB\u7EDF",
537
+ "kbAudit.empty": "\u6682\u65E0\u5BA1\u8BA1\u4E8B\u4EF6",
538
+ "kbAudit.exportTip": "\u5BFC\u51FA\u5F53\u524D\u8FC7\u6EE4\u89C6\u56FE\u4E3A CSV",
539
+ "kbAudit.hygieneLabel": "\u6743\u9650\u5F02\u5473",
540
+ "kbAudit.noHygiene": "\u672A\u53D1\u73B0\u6743\u9650\u5F02\u5473",
541
+ /* ── kbCanvas ── */
542
+ "kbCanvas.splitterAria": "\u8C03\u6574\u753B\u5E03\u53CC\u680F\u6BD4\u4F8B",
543
+ "kbCanvas.splitterTip": "\u62D6\u62FD\u8C03\u6574\u6BD4\u4F8B\uFF08\u2190/\u2192 \u5FAE\u8C03\uFF0C\u53CC\u51FB\u590D\u4F4D\uFF09",
544
+ /* ── kbCheckpoint ── */
545
+ "kbCheckpoint.chatAuditNote": "\u8BA8\u8BBA\u5386\u53F2\u6709\u5BA1\u8BA1\u4EF7\u503C",
546
+ "kbCheckpoint.label": "\u68C0\u67E5\u70B9",
547
+ "kbCheckpoint.restore": "\u6062\u590D\u68C0\u67E5\u70B9",
548
+ "kbCheckpoint.scopeBoth": "\u5185\u5BB9\u4E0E\u5BF9\u8BDD",
549
+ "kbCheckpoint.scopeChat": "\u4EC5\u5BF9\u8BDD",
550
+ "kbCheckpoint.scopeContent": "\u4EC5\u5185\u5BB9\uFF08\u4FDD\u7559\u5BF9\u8BDD\uFF09",
551
+ "kbCheckpoint.scopeMissing": "\u8BE5\u68C0\u67E5\u70B9\u672A\u5305\u542B\u6B64\u6062\u590D\u8303\u56F4",
552
+ /* ── kbChunks ── */
553
+ "kbChunks.addKeyword": "+ \u5173\u952E\u8BCD",
554
+ "kbChunks.chunkAddKeywordAria": "chunk {p0} \u65B0\u589E\u5173\u952E\u8BCD",
555
+ "kbChunks.chunkContentAria": "chunk {p0} \u5185\u5BB9",
556
+ "kbChunks.chunkEnableAria": "chunk {p0} \u542F\u7528",
557
+ "kbChunks.deleteChunkAria": "\u5220\u9664 chunk {p0}",
558
+ "kbChunks.disableNote": "\u5173\u95ED\u540E\u4E0D\u5220\u9664\uFF0C\u4EC5\u6392\u9664\u51FA\u68C0\u7D22",
559
+ "kbChunks.edited": "\u5DF2\u7F16\u8F91",
560
+ "kbChunks.empty": "\u6682\u65E0 chunk",
561
+ "kbChunks.modeFull": "\u5168\u6587",
562
+ "kbChunks.modeLabel": "\u641C\u7D22\u6A21\u5F0F",
563
+ "kbChunks.modeVector": "\u8BED\u4E49",
564
+ "kbChunks.removeKeyword": "\u79FB\u9664\u5173\u952E\u8BCD {p0}",
565
+ "kbChunks.searchAria": "\u641C\u7D22 chunk",
566
+ "kbChunks.searchFullPlaceholder": "\u5168\u6587\u641C\u7D22 chunk\u2026",
567
+ "kbChunks.searchVectorPlaceholder": "\u8BED\u4E49\u641C\u7D22 chunk\u2026",
568
+ /* ── kbCitation ── */
569
+ "kbCitation.sourceN": "\u6765\u6E90 {p0}",
570
+ "kbCitation.unavailable": "\u6765\u6E90\u5DF2\u4E0D\u53EF\u7528",
571
+ /* ── kbClarify ── */
572
+ "kbClarify.parsedPrefix": "\u5DF2\u89E3\u6790\uFF1A",
573
+ /* ── kbConflict ── */
574
+ "kbConflict.owner": "\u8D23\u4EFB\u4EBA {p0}",
575
+ /* ── kbConnector ── */
576
+ "kbConnector.changeRate": "\u53D8\u66F4\u7387",
577
+ "kbConnector.crawlRate": "\u6293\u53D6\u7387",
578
+ "kbConnector.credentials": "\u51ED\u8BC1",
579
+ "kbConnector.credentialsExpired": "\u51ED\u8BC1\u8FC7\u671F",
580
+ "kbConnector.editSchedule": "\u7F16\u8F91\u8BA1\u5212",
581
+ "kbConnector.empty": "\u6682\u65E0\u8FDE\u63A5\u5668",
582
+ "kbConnector.failing": "\u5F02\u5E38",
583
+ "kbConnector.lastSync": "\u6700\u8FD1\u540C\u6B65 {p0}",
584
+ "kbConnector.neverSynced": "\u4ECE\u672A\u540C\u6B65",
585
+ "kbConnector.notConfigured": "\u672A\u914D\u7F6E",
586
+ "kbConnector.off": "\u5DF2\u505C\u7528",
587
+ "kbConnector.perDay": "{p0}/\u5929",
588
+ "kbConnector.reauth": "\u91CD\u65B0\u6388\u6743",
589
+ "kbConnector.stale": "\u505C\u6EDE",
590
+ "kbConnector.syncNow": "\u7ACB\u5373\u540C\u6B65",
591
+ "kbConnector.synced": "\u5DF2\u540C\u6B65",
592
+ /* ── kbCore ── */
593
+ "kbCore.accessDenied": "\u672A\u901A\u8FC7",
594
+ "kbCore.accessExpired": "\u5DF2\u8FC7\u671F",
595
+ "kbCore.accessExpiring": "\u5373\u5C06\u5230\u671F",
596
+ "kbCore.accessGranted": "\u5DF2\u5F00\u901A",
597
+ "kbCore.accessNotRequested": "\u672A\u7533\u8BF7",
598
+ "kbCore.accessPending": "\u5F85\u5BA1\u6279",
599
+ "kbCore.accessRequested": "\u5DF2\u63D0\u4EA4",
600
+ "kbCore.accessRevoked": "\u5DF2\u6536\u56DE",
601
+ "kbCore.auditApprove": "\u6279\u51C6",
602
+ "kbCore.auditBreakGlass": "\u5E94\u6025\u8BBF\u95EE",
603
+ "kbCore.auditDelegation": "\u59D4\u6D3E",
604
+ "kbCore.auditDeny": "\u62D2\u7EDD",
605
+ "kbCore.auditGrant": "\u6388\u4E88",
606
+ "kbCore.auditPermissionChange": "\u6743\u9650\u53D8\u66F4",
607
+ "kbCore.auditPreview": "\u9884\u89C8",
608
+ "kbCore.auditRead": "\u8BFB\u53D6",
609
+ "kbCore.auditRequest": "\u7533\u8BF7",
610
+ "kbCore.auditRevoke": "\u6536\u56DE",
611
+ "kbCore.auditSync": "\u6743\u9650\u540C\u6B65",
612
+ "kbCore.daysAgo": "{p0} \u5929\u524D",
613
+ "kbCore.hoursAgo": "{p0} \u5C0F\u65F6\u524D",
614
+ "kbCore.hygieneBroadGroup": "\u8FC7\u5BBD\u7EC4\u6388\u6743",
615
+ "kbCore.hygieneBrokenInheritance": "\u65AD\u7EE7\u627F",
616
+ "kbCore.hygieneOrgWideLink": "\u7EC4\u7EC7\u7EA7\u94FE\u63A5",
617
+ "kbCore.hygieneOrphanedOwner": "\u8D1F\u8D23\u4EBA\u7F3A\u4F4D",
618
+ "kbCore.hygieneOverexposed": "\u8FC7\u5EA6\u66B4\u9732",
619
+ "kbCore.hygieneSensitiveMismatch": "\u654F\u611F\u5EA6\u9519\u914D",
620
+ "kbCore.justNow": "\u521A\u521A",
621
+ "kbCore.levelComment": "\u53EF\u8BC4\u8BBA",
622
+ "kbCore.levelEdit": "\u53EF\u7F16\u8F91",
623
+ "kbCore.levelNone": "\u65E0\u6743\u9650",
624
+ "kbCore.levelOwner": "\u8D1F\u8D23\u4EBA",
625
+ "kbCore.levelView": "\u53EF\u67E5\u770B",
626
+ "kbCore.minutesAgo": "{p0} \u5206\u949F\u524D",
627
+ "kbCore.runCancelled": "\u5DF2\u53D6\u6D88",
628
+ "kbCore.runDone": "\u5DF2\u5B8C\u6210",
629
+ "kbCore.runFailed": "\u5931\u8D25",
630
+ "kbCore.runQueued": "\u6392\u961F\u4E2D",
631
+ "kbCore.runRunning": "\u8FDB\u884C\u4E2D",
632
+ "kbCore.runUnstarted": "\u672A\u5F00\u59CB",
633
+ "kbCore.stageAgent": "\u4EE3\u7406",
634
+ "kbCore.stageChunk": "\u5207\u7247",
635
+ "kbCore.stageEmbed": "\u5411\u91CF\u5316",
636
+ "kbCore.stageGeneration": "\u751F\u6210",
637
+ "kbCore.stageGuard": "\u62A4\u680F",
638
+ "kbCore.stageQuery": "\u67E5\u8BE2",
639
+ "kbCore.stageRerank": "\u91CD\u6392",
640
+ "kbCore.stageRetrieval": "\u68C0\u7D22",
641
+ "kbCore.stageTool": "\u5DE5\u5177",
642
+ "kbCore.typeApi": "\u63A5\u53E3",
643
+ "kbCore.typeCode": "\u4EE3\u7801",
644
+ "kbCore.typeDatabase": "\u6570\u636E\u5E93",
645
+ "kbCore.typeDocument": "\u6587\u6863",
646
+ "kbCore.typeEmail": "\u90AE\u4EF6",
647
+ "kbCore.typeImage": "\u56FE\u7247",
648
+ "kbCore.typeKb": "\u77E5\u8BC6\u5E93",
649
+ "kbCore.typeTable": "\u8868\u683C",
650
+ "kbCore.typeTicket": "\u5DE5\u5355",
651
+ "kbCore.typeWebpage": "\u7F51\u9875",
652
+ "kbCore.unnamedSource": "\u672A\u547D\u540D\u6765\u6E90",
653
+ "kbCore.unnamedSubject": "\u672A\u547D\u540D\u4E3B\u4F53",
654
+ "kbCore.visHidden": "\u4E0D\u53EF\u89C1",
655
+ "kbCore.visMetadataOnly": "\u4EC5\u5143\u6570\u636E",
656
+ "kbCore.visSummaryOnly": "\u4EC5\u6458\u8981",
657
+ "kbCore.visVisible": "\u53EF\u89C1",
658
+ /* ── kbEval ── */
659
+ "kbEval.avgA": "A \u5747\u5206 {p0}",
660
+ "kbEval.avgB": "B \u5747\u5206 {p0}",
661
+ "kbEval.compare": "\u5BF9\u6BD4",
662
+ "kbEval.runSelectAria": "\u9009\u62E9\u5BF9\u6BD4\u8FD0\u884C",
663
+ "kbEval.expected": "\u671F\u671B",
664
+ "kbEval.gradeImprovement": "\u63D0\u5347",
665
+ "kbEval.gradeRegression": "\u56DE\u5F52",
666
+ "kbEval.gradeTie": "\u6301\u5E73",
667
+ "kbEval.gradeTradeoff": "\u6743\u8861",
668
+ "kbEval.noData": "\u6CA1\u6709\u5BF9\u6BD4\u6570\u636E",
669
+ "kbEval.onlyRegression": "\u4EC5\u770B\u56DE\u5F52",
670
+ "kbEval.outputA": "\u8F93\u51FA A",
671
+ "kbEval.outputB": "\u8F93\u51FA B",
672
+ "kbEval.outputN": "\u8F93\u51FA {p0}",
673
+ /* ── kbExplain ── */
674
+ "kbExplain.empty": "\u6682\u65E0\u53E3\u5F84\u4FE1\u606F",
675
+ "kbExplain.groupAgg": "\u805A\u5408",
676
+ "kbExplain.groupColumn": "\u5217",
677
+ "kbExplain.groupFilter": "\u7B5B\u9009",
678
+ "kbExplain.groupTable": "\u8868",
679
+ "kbExplain.trigger": "\u8FD9\u4E2A\u6570\u5B57\u662F\u600E\u4E48\u7B97\u51FA\u6765\u7684",
680
+ "kbExplain.viewDefinition": "\u67E5\u770B\u53E3\u5F84",
681
+ /* ── kbFilter ── */
682
+ "kbFilter.addGroup": "+ \u7EC4",
683
+ "kbFilter.addRule": "+ \u89C4\u5219",
684
+ "kbFilter.and": "\u4E14",
685
+ "kbFilter.combineMode": "\u7EC4\u5408\u65B9\u5F0F {p0}",
686
+ "kbFilter.copyDsl": "\u590D\u5236 DSL",
687
+ "kbFilter.deleteGroup": "\u5220\u9664\u7EC4",
688
+ "kbFilter.deleteRule": "\u5220\u9664\u89C4\u5219",
689
+ "kbFilter.fieldLabel": "\u5B57\u6BB5",
690
+ "kbFilter.fieldPlaceholder": "\u8FC7\u6EE4\u5B57\u6BB5",
691
+ "kbFilter.noValueNeeded": "\uFF08\u65E0\u9700\u503C\uFF09",
692
+ "kbFilter.none": "\uFF08\u65E0\u8FC7\u6EE4\uFF09",
693
+ "kbFilter.opContains": "\u5305\u542B",
694
+ "kbFilter.opEq": "\u7B49\u4E8E",
695
+ "kbFilter.opExists": "\u5B58\u5728",
696
+ "kbFilter.opGt": "\u5927\u4E8E",
697
+ "kbFilter.opGte": "\u5927\u4E8E\u7B49\u4E8E",
698
+ "kbFilter.opIn": "\u5C5E\u4E8E",
699
+ "kbFilter.opLt": "\u5C0F\u4E8E",
700
+ "kbFilter.opLte": "\u5C0F\u4E8E\u7B49\u4E8E",
701
+ "kbFilter.opNe": "\u4E0D\u7B49\u4E8E",
702
+ "kbFilter.operatorLabel": "\u64CD\u4F5C\u7B26",
703
+ "kbFilter.or": "\u6216",
704
+ "kbFilter.toggleAndOr": "\u5207\u6362 AND / OR",
705
+ "kbFilter.valueAria": "\u8FC7\u6EE4\u503C",
706
+ "kbFilter.valueLabel": "\u503C",
707
+ "kbFilter.valuePlaceholder": "\u503C 1, \u503C 2\uFF08\u9017\u53F7\u5206\u9694\uFF09",
708
+ /* ── kbGap ── */
709
+ "kbGap.addSynonym": "\u52A0\u540C\u4E49\u8BCD",
710
+ "kbGap.colCount": "\u6B21\u6570",
711
+ "kbGap.colLastSeen": "\u6700\u8FD1\u51FA\u73B0",
712
+ "kbGap.colQuery": "\u67E5\u8BE2",
713
+ "kbGap.colShare": "\u5360\u6BD4",
714
+ "kbGap.createDoc": "\u5EFA\u6587\u6863",
715
+ "kbGap.empty": "\u6CA1\u6709\u672A\u547D\u4E2D\u67E5\u8BE2\u2014\u2014\u8986\u76D6\u826F\u597D",
716
+ "kbGap.title": "\u672A\u547D\u4E2D\u67E5\u8BE2\u8D8B\u52BF",
717
+ "kbGap.zeroClickHint": "\u7528\u6237\u53EF\u80FD\u5DF2\u4ECE\u5F15\u7528\u7247\u6BB5\u5F97\u5230\u7B54\u6848\uFF1B\u96F6\u70B9\u51FB\u9700\u4EBA\u5DE5\u5224\u8BFB\uFF0C\u4E0D\u76F4\u63A5\u89C6\u4E3A\u5931\u8D25",
718
+ "kbGap.zeroClickRate": "\u96F6\u70B9\u51FB\u7387",
719
+ "kbGap.zeroClickTitle": "\u96F6\u70B9\u51FB \u2260 \u5931\u8D25",
720
+ /* ── kbHittest ── */
721
+ "kbHittest.addAria": "\u65B0\u589E\u6D4B\u8BD5\u95EE\u9898",
722
+ "kbHittest.addPlaceholder": "\u65B0\u589E\u95EE\u9898\uFF0CEnter \u6DFB\u52A0",
723
+ "kbHittest.avgScore": "\u5747\u5206",
724
+ "kbHittest.deleteQuestion": "\u5220\u9664\u95EE\u9898",
725
+ "kbHittest.empty": "\u6682\u65E0\u6D4B\u8BD5\u95EE\u9898\u2014\u2014\u6DFB\u52A0\u7B2C\u4E00\u6761\uFF0C\u5F00\u59CB\u53EC\u56DE\u56DE\u5F52",
726
+ "kbHittest.expectHit": "\u671F\u671B\u547D\u4E2D\u5728\u524D\u6392\u547D\u4E2D\u4E2D",
727
+ "kbHittest.expectMiss": "\u671F\u671B\u672A\u547D\u4E2D",
728
+ "kbHittest.hitRate": "\u547D\u4E2D\u7387",
729
+ "kbHittest.noExpectation": "\u672A\u8BBE\u671F\u671B",
730
+ "kbHittest.notRun": "\u672A\u8FD0\u884C",
731
+ "kbHittest.pass": "\u901A\u8FC7",
732
+ "kbHittest.pin": "\u7F6E\u9876\u56FA\u5B9A",
733
+ "kbHittest.runAll": "\u8FD0\u884C\u5168\u90E8",
734
+ "kbHittest.sessionOnly": "\u53C2\u6570\u4EC5\u672C\u6B21\u4F1A\u8BDD\u751F\u6548",
735
+ "kbHittest.sessionOnlyHint": "\u6D4B\u8BD5\u8FD0\u884C\u7684\u68C0\u7D22\u53C2\u6570\uFF08topK/\u9608\u503C/\u91CD\u6392\uFF09\u4E0D\u843D\u5E93\uFF0C\u4EC5\u672C\u6B21\u4F1A\u8BDD\u751F\u6548",
736
+ "kbHittest.unpin": "\u53D6\u6D88\u7F6E\u9876",
737
+ /* ── kbMetadata ── */
738
+ "kbMetadata.addField": "\u65B0\u589E\u5B57\u6BB5",
739
+ "kbMetadata.builtIn": "\u5185\u7F6E",
740
+ "kbMetadata.colField": "\u5B57\u6BB5",
741
+ "kbMetadata.colType": "\u7C7B\u578B",
742
+ "kbMetadata.colValue": "\u503C",
743
+ "kbMetadata.empty": "\u6682\u65E0\u5143\u6570\u636E\u5B57\u6BB5",
744
+ "kbMetadata.nameLabel": "\u65B0\u5B57\u6BB5\u540D",
745
+ "kbMetadata.namePlaceholder": "\u5B57\u6BB5\u540D\uFF0C\u5982 department",
746
+ "kbMetadata.removeField": "\u79FB\u9664\u5B57\u6BB5 {p0}",
747
+ "kbMetadata.typeLabel": "\u65B0\u5B57\u6BB5\u7C7B\u578B",
748
+ "kbMetadata.valueOf": "{p0} \u7684\u503C",
749
+ /* ── kbPassage ── */
750
+ "kbPassage.blockRange": "\u5757 {p0}\u2013{p1}",
751
+ "kbPassage.close": "\u5173\u95ED\u539F\u6587",
752
+ "kbPassage.expandContext": "\u5C55\u5F00\u4E0A\u4E0B\u6587",
753
+ "kbPassage.locate": "\u539F\u6587\u5B9A\u4F4D",
754
+ "kbPassage.none": "\uFF08\u65E0\u539F\u6587\uFF09",
755
+ "kbPassage.pageRange": "\u7B2C {p0}\u2013{p1} \u9875",
756
+ "kbPassage.restricted": "\u5185\u5BB9\u53D7\u9650",
757
+ "kbPassage.unitByte": "\u5B57\u8282",
758
+ "kbPassage.unitChar": "\u5B57\u7B26",
759
+ /* ── kbPipeline ── */
760
+ "kbPipeline.empty": "\u6682\u65E0\u89E3\u6790\u4EFB\u52A1",
761
+ "kbPipeline.progress": "\u89E3\u6790\u8FDB\u5EA6",
762
+ "kbPipeline.rerun": "\u91CD\u8DD1",
763
+ "kbPipeline.unknownStage": "\u672A\u77E5\u9636\u6BB5",
764
+ /* ── kbQa ── */
765
+ "kbQa.colAnswer": "\u7B54\u6848",
766
+ "kbQa.colQuestion": "\u95EE\u9898",
767
+ "kbQa.csvHint": "CSV \u6A21\u677F\uFF1Aquestion,answer\uFF08\u6BCF\u884C\u4E00\u5BF9\uFF0CUTF-8\uFF09",
768
+ "kbQa.deleteRow": "\u5220\u9664\u7B2C {p0} \u884C",
769
+ "kbQa.empty": "\u6682\u65E0\u8BA4\u8BC1\u95EE\u7B54\u5BF9",
770
+ "kbQa.importCsv": "\u5BFC\u5165 CSV",
771
+ /* ── kbRerank ── */
772
+ "kbRerank.after": "\u91CD\u6392\u540E",
773
+ "kbRerank.before": "\u91CD\u6392\u524D",
774
+ "kbRerank.compareTitle": "\u91CD\u6392\u5BF9\u6BD4",
775
+ "kbRerank.down": "\u4E0B\u964D {p0} \u4F4D",
776
+ "kbRerank.dropped": "\u843D\u9009",
777
+ "kbRerank.empty": "\u4E24\u4FA7\u5747\u4E3A\u7A7A\u2014\u2014\u8FD0\u884C\u68C0\u7D22\u540E\u518D\u5BF9\u6BD4",
778
+ "kbRerank.enableCompare": "\u542F\u7528\u91CD\u6392\u5BF9\u6BD4",
779
+ "kbRerank.newIn": "\u65B0\u8FDB",
780
+ "kbRerank.unmarked": "\u672A\u6807",
781
+ "kbRerank.up": "\u4E0A\u5347 {p0} \u4F4D",
782
+ /* ── kbRetrieval ── */
783
+ "kbRetrieval.breakdown": "\u62C6\u89E3",
784
+ "kbRetrieval.copyParams": "\u590D\u5236\u53C2\u6570 JSON",
785
+ "kbRetrieval.decreaseTopK": "\u51CF\u5C11 topK",
786
+ "kbRetrieval.empty": "\u8F93\u5165\u95EE\u9898\u5E76\u8FD0\u884C\uFF0C\u547D\u4E2D\u7ED3\u679C\u5C06\u51FA\u73B0\u5728\u8FD9\u91CC",
787
+ "kbRetrieval.failOpenNotice": "fail-open\uFF1A\u5F53\u524D\u67E5\u8BE2\u672A\u643A\u5E26\u8EAB\u4EFD\u8FC7\u6EE4\uFF0C\u5C06\u8FD4\u56DE\u5168\u90E8\u5019\u9009\uFF08\u4EC5\u9650\u8C03\u8BD5\uFF09",
788
+ "kbRetrieval.hybridWeight": "\u6DF7\u5408\u6743\u91CD\uFF08\u5DE6\u8BED\u4E49\u53F3\u5173\u952E\u8BCD\uFF09",
789
+ "kbRetrieval.identityLabel": "\u68C0\u7D22\u8EAB\u4EFD",
790
+ "kbRetrieval.identityShort": "\u8EAB\u4EFD",
791
+ "kbRetrieval.increaseTopK": "\u589E\u52A0 topK",
792
+ "kbRetrieval.keywordHint": "\u5173\u952E\u8BCD\uFF1A\u5927\u5E93\u5FEB\u901F\u7CBE\u786E",
793
+ "kbRetrieval.layerLabel": "\u8FC7\u6EE4\u5C42",
794
+ "kbRetrieval.layerNone": "none\uFF1Afail-open\uFF0C\u65E0\u8EAB\u4EFD\u8FC7\u6EE4\u8FD4\u56DE\u5168\u90E8\u2014\u2014\u5371\u9669\uFF0C\u4EC5\u6F14\u793A",
795
+ "kbRetrieval.layerPost": "post-filter\uFF1A\u5206\u6570\u5DF2\u53EF\u89C2\u5BDF\u2014\u2014\u7F3A\u9677\u5C42\uFF0C\u4EC5\u6F14\u793A",
796
+ "kbRetrieval.layerPre": "pre-filter\uFF1AACL \u5728\u68C0\u7D22\u6267\u884C\u4F53\u5185\u751F\u6548\u2014\u2014\u5B89\u5168\u57FA\u7EBF",
797
+ "kbRetrieval.leakNote": "\u5206\u6BCD\u672A\u8FC7\u6EE4\u524D\u8BA1\u6570\u5C5E\u6CC4\u9732\uFF0C\u4E0D\u663E\u793A",
798
+ "kbRetrieval.modeHybrid": "\u6DF7\u5408",
799
+ "kbRetrieval.modeKeyword": "\u5173\u952E\u8BCD",
800
+ "kbRetrieval.modeLabel": "\u68C0\u7D22\u6A21\u5F0F",
801
+ "kbRetrieval.modeVector": "\u5411\u91CF",
802
+ "kbRetrieval.postFilterNotice": "post-filter\uFF1A\u5206\u6570\u5DF2\u5728\u8FC7\u6EE4\u524D\u4EA7\u751F\uFF08\u7F3A\u9677\u5C42\u6F14\u793A\uFF09",
803
+ "kbRetrieval.queryLabel": "\u68C0\u7D22\u95EE\u9898",
804
+ "kbRetrieval.queryPlaceholder": "\u68C0\u7D22\u95EE\u9898\uFF0CEnter \u8FD0\u884C",
805
+ "kbRetrieval.rerankLabel": "\u91CD\u6392",
806
+ "kbRetrieval.rerankThresholdOff": "\u9608\u503C \u5173 \xB7 \u5B58\u6D3B {p0}",
807
+ "kbRetrieval.rerankThresholdOn": "\u9608\u503C {p0} \xB7 \u5B58\u6D3B {p1}",
808
+ "kbRetrieval.running": "\u8FD0\u884C\u4E2D\u2026",
809
+ "kbRetrieval.thresholdLabel": "\u5206\u6570\u9608\u503C",
810
+ "kbRetrieval.thresholdValue": "\u9608\u503C N/A",
811
+ "kbRetrieval.vectorHint": "\u8BED\u4E49\uFF1A\u8DE8\u8BED\u8A00/\u65E0\u7CBE\u786E\u8BCD",
812
+ "kbRetrieval.vectorPercent": "\u8BED\u4E49 {p0}",
813
+ "kbRetrieval.weightValue": "\u5411\u91CF {p0}% / \u5173\u952E\u8BCD {p1}%",
814
+ /* ── kbReview ── */
815
+ "kbReview.assignee": "\u5206\u914D\u4EBA",
816
+ "kbReview.doneNext": "\u5B8C\u6210\u5E76\u4E0B\u4E00\u6761",
817
+ "kbReview.empty": "\u6CA1\u6709\u5F85\u5BA1\u67E5\u6761\u76EE",
818
+ "kbReview.emptyAssignee": "\u8BE5\u5206\u914D\u4EBA\u4E0B\u6CA1\u6709\u6761\u76EE",
819
+ "kbReview.emptyQueue": "\u961F\u5217\u4E3A\u7A7A",
820
+ "kbReview.kbCloseHint": "\u5173\u95ED\u63D0\u793A",
821
+ "kbReview.kbSelectOption": "\u9009\u62E9\u9009\u9879",
822
+ "kbReview.kbSwitchItem": "\u5207\u6362\u6761\u76EE",
823
+ "kbReview.kbToggleHint": "\u663E\u793A/\u9690\u85CF\u672C\u63D0\u793A",
824
+ "kbReview.shortcuts": "? \u5FEB\u6377\u952E",
825
+ "kbReview.typeAnswer": "\u56DE\u7B54",
826
+ "kbReview.typeMessage": "\u6D88\u606F",
827
+ "kbReview.typeTrace": "\u8F68\u8FF9",
828
+ "kbReview.unassigned": "\u672A\u5206\u914D",
829
+ /* ── kbSandbox ── */
830
+ "kbSandbox.noResolver": "\u5BBF\u4E3B\u672A\u63D0\u4F9B ui:// \u89E3\u6790\u5668",
831
+ "kbSandbox.notReady": "kb-sandbox: \u5E94\u7528\u672A\u5C31\u7EEA",
832
+ "kbSandbox.resolveFailed": "ui:// \u89E3\u6790\u5931\u8D25\uFF1A{p0}",
833
+ "kbSandbox.timeout": "kb-sandbox: \u8C03\u7528\u8D85\u65F6\uFF08{p0}ms\uFF09",
834
+ "kbSandbox.title": "MCP App \u6C99\u7BB1",
835
+ /* ── kbSegment ── */
836
+ "kbSegment.avgLength": "\u5747\u957F {p0}",
837
+ "kbSegment.childNote": "\u5B50\u5206\u6BB5\u6309\u957F\u5EA6\u81EA\u52A8\u751F\u6210",
838
+ "kbSegment.cleanBlankLines": "\u5408\u5E76\u8FDE\u7EED\u7A7A\u884C",
839
+ "kbSegment.cleanEmails": "\u53BB\u9664\u90AE\u7BB1",
840
+ "kbSegment.cleanLinks": "\u53BB\u9664\u94FE\u63A5",
841
+ "kbSegment.empty": "\u6837\u672C\u4E3A\u7A7A\uFF0C\u6682\u65E0\u53EF\u9884\u89C8\u5206\u6BB5",
842
+ "kbSegment.maxLengthLabel": "\u6700\u5927\u957F\u5EA6",
843
+ "kbSegment.maxLengthPlaceholder": "\u5206\u6BB5\u6700\u5927\u957F\u5EA6",
844
+ "kbSegment.modeAuto": "\u81EA\u52A8",
845
+ "kbSegment.modeCustom": "\u81EA\u5B9A\u4E49",
846
+ "kbSegment.modeLabel": "\u5206\u6BB5\u6A21\u5F0F",
847
+ "kbSegment.modeNote": "\u5206\u6BB5\u6A21\u5F0F\u521B\u5EFA\u540E\u4E0D\u53EF\u66F4\u6539\uFF0C\u53C2\u6570\u53EF\u968F\u65F6\u8C03\u6574",
848
+ "kbSegment.modeParentChild": "\u7236\u5B50",
849
+ "kbSegment.overlapInvalid": "\u91CD\u53E0\u9700\u5C0F\u4E8E\u6700\u5927\u957F\u5EA6",
850
+ "kbSegment.overlapLabel": "\u91CD\u53E0",
851
+ "kbSegment.overlapPlaceholder": "\u5206\u6BB5\u91CD\u53E0",
852
+ "kbSegment.previewNote": "\u5171 {p0} \u6BB5\uFF0C\u4EC5\u9884\u89C8\u524D {p1} \u6BB5",
853
+ "kbSegment.segmentsCount": "{p0} \u6BB5",
854
+ "kbSegment.separatorLabel": "\u5206\u9694\u7B26",
855
+ "kbSegment.separatorPlaceholder": "\u5206\u6BB5\u5206\u9694\u7B26",
856
+ "kbSegment.toggleTip": "\u70B9\u51FB\u5C55\u5F00/\u6536\u8D77",
857
+ /* ── kbSources ── */
858
+ "kbSources.cited": "\u88AB\u5F15 \xD7{p0}",
859
+ "kbSources.empty": "\u4E0B\u4E00\u6761\u7B54\u6848\u5F15\u7528\u7684\u6BB5\u843D\u5C06\u51FA\u73B0\u5728\u8FD9\u91CC",
860
+ /* ── kbSql ── */
861
+ "kbSql.adminOnly": "\u4EC5\u7BA1\u7406\u5458\u53EF\u89C1",
862
+ "kbSql.applyFix": "\u5E94\u7528\u4FEE\u590D\u5E76\u91CD\u8DD1",
863
+ "kbSql.confirmRerun": "\u786E\u8BA4\u5E76\u91CD\u8DD1",
864
+ "kbSql.editRerun": "\u7F16\u8F91\u91CD\u8DD1",
865
+ "kbSql.fixReady": "\u68C0\u6D4B\u5230\u4E0A\u7248\u62A5\u9519\uFF0C\u4FEE\u590D\u5EFA\u8BAE\u5DF2\u5C31\u7EEA",
866
+ "kbSql.generated": "\u751F\u6210\u7684 SQL",
867
+ /* ── kbTrace ── */
868
+ "kbTrace.empty": "\u6682\u65E0\u8C03\u7528\u94FE\u6570\u636E",
869
+ "kbTrace.hits": "\u547D\u4E2D {p0}",
870
+ /* ── kbVisibility ── */
871
+ "kbVisibility.adminOnlyNote": "\u4EE5\u4E0A\u5BF9\u7167\u4EC5\u9650 admin \u5BA1\u8BA1",
872
+ "kbVisibility.adminWarn": "admin \u5BA1\u8BA1\u89C6\u56FE\u2014\u2014\u547D\u4E2D\u5DEE\u5F02\u5BF9\u7167\u5BF9\u666E\u901A\u7528\u6237\u6784\u6210\u6CC4\u9732\uFF0C\u52FF\u5D4C\u5165\u7EC8\u7AEF\u7528\u6237\u754C\u9762",
873
+ "kbVisibility.failOpenNote": "fail-open\uFF1AKendra \u65E0\u8EAB\u4EFD\u4E0A\u4E0B\u6587\u8FD4\u56DE\u5168\u90E8\u6587\u6863\u2014\u2014\u68C0\u7D22\u5FC5\u987B\u643A\u5E26\u670D\u52A1\u7AEF\u8EAB\u4EFD",
874
+ "kbVisibility.focusColumn": "\u805A\u7126\u8EAB\u4EFD {p0} \u7684\u5217",
875
+ "kbVisibility.layerValue": "\u8FC7\u6EE4\u5C42\uFF1A{p0}",
876
+ "kbVisibility.leakageNote": "\u8BA1\u6570\u5DEE\u5F02\u5373 volume leakage\uFF1A\u5BF9\u666E\u901A\u7528\u6237\u53EA\u53EF\u7ED9\u300C\u5DF2\u6309\u6743\u9650\u8FC7\u6EE4\u300D\u6052\u5B9A\u6587\u6848\uFF0C\u7F13\u89E3\u53EA\u6709 padding \u53D6\u6574",
877
+ "kbVisibility.postFilterNote": "post-filter\uFF1AtopK \u88AB\u65E0\u6743\u7ED3\u679C\u5360\u7528\u4E14\u76F8\u4F3C\u5EA6\u5206\u6570\u53EF\u88AB\u89C2\u5BDF",
878
+ "kbVisibility.queryLabel": "\u67E5\u8BE2\uFF1A{p0}",
879
+ "kbVisibility.safeCount": "\u5B89\u5168\u8BA1\u6570 {p0}",
880
+ "kbVisibility.sourceUnavailable": "\u539F\u6587\u4E0D\u53EF\u7528",
881
+ "kbVisibility.unnamedHit": "\u672A\u547D\u540D\u547D\u4E2D",
882
+ /* ── kbWhoCan ── */
883
+ "kbWhoCan.check": "\u68C0\u67E5",
884
+ "kbWhoCan.checkIdentity": "\u68C0\u67E5\u8EAB\u4EFD",
885
+ "kbWhoCan.daysUntil": "{p0} \u5929\u540E",
886
+ "kbWhoCan.deniedBy": "\u88AB\u7B56\u7565\u62D2\u7EDD\uFF1A{p0}",
887
+ "kbWhoCan.denyVeto": "\u88AB Deny \u4E00\u7968\u5426\u51B3",
888
+ "kbWhoCan.direct": "\u76F4\u63A5",
889
+ "kbWhoCan.expiresSoon": "\u6700\u8FD1\u5230\u671F {p0}",
890
+ "kbWhoCan.hoursUntil": "{p0} \u5C0F\u65F6\u540E",
891
+ "kbWhoCan.inherit": "\u7EE7\u627F",
892
+ "kbWhoCan.link": "\u94FE\u63A5",
893
+ "kbWhoCan.noDeny": "\u65E0 Deny \u751F\u6548",
894
+ "kbWhoCan.noIdentity": "\u672A\u63D0\u4F9B\u53EF\u68C0\u67E5\u7684\u8EAB\u4EFD",
895
+ "kbWhoCan.none": "\u672A\u547D\u4E2D\u4EFB\u4F55\u6388\u6743\u2014\u2014\u9ED8\u8BA4\u62D2\u7EDD",
896
+ "kbWhoCan.recheckTip": "\u91CD\u65B0\u68C0\u67E5\u5F53\u524D\u8EAB\u4EFD\u7684\u6709\u6548\u6743\u9650",
897
+ "kbWhoCan.stackRule": "\u53E0\u52A0\u89C4\u5219\uFF1A\u591A\u6765\u6E90\u5E76\u96C6 \u2192 {p0}\uFF1B{p1}",
898
+ /* ── mentions ── */
899
+ "mentions.create": "\u521B\u5EFA\u201C{p0}\u201D",
900
+ "mentions.customBadge": "\u81EA\u5B9A\u4E49",
901
+ "mentions.loading": "\u52A0\u8F7D\u4E2D\u2026",
902
+ /* ── notification ── */
903
+ "notification.groupBusy": "\u64CD\u4F5C\u7ED3\u675F\u540E\u53EF\u4EE5\u6536\u8D77\u8FD9\u7EC4\u901A\u77E5\u3002",
904
+ "notification.groupUnread": "{count} \u6761\u540C\u7EC4\u901A\u77E5\uFF0C{unread} \u6761\u672A\u8BFB",
905
+ "notification.groupCount": "{count} \u6761\u540C\u7EC4\u901A\u77E5",
906
+ "notification.groupExpanded": "\u6536\u8D77\u8FD9\u7EC4\u901A\u77E5",
907
+ "notification.ariaLabel": "\u901A\u77E5",
908
+ "notification.viewDetails": "\u67E5\u770B\u8BE6\u60C5",
909
+ /* ── pagination ── */
910
+ "pagination.next": "\u4E0B\u4E00\u9875",
911
+ "pagination.pageSize": "{p0} \u6761/\u9875",
912
+ "pagination.prev": "\u4E0A\u4E00\u9875",
913
+ /* ── select ── */
914
+ "select.fieldLabel": "\u9009\u62E9\u9009\u9879",
915
+ "select.noMatch": "\u65E0\u5339\u914D\u9879",
916
+ "select.remove": "\u79FB\u9664 {p0}",
917
+ "select.searchPlaceholder": "\u641C\u7D22\u2026",
918
+ /* ── cascader / tree-select ── */
919
+ "cascader.placeholder": "\u8BF7\u9009\u62E9",
920
+ "cascader.noMatch": "\u65E0\u5339\u914D",
921
+ "cascader.search": "\u641C\u7D22",
922
+ "treeSelect.placeholder": "\u8BF7\u9009\u62E9",
923
+ "treeSelect.noMatch": "\u65E0\u5339\u914D",
924
+ "treeSelect.search": "\u641C\u7D22",
925
+ /* ── tagInput ── */
926
+ "tagInput.ariaLabel": "\u6807\u7B7E\u8F93\u5165\uFF0C\u5171 {p0} \u4E2A",
927
+ "tagInput.remove": "\u79FB\u9664 {p0}",
928
+ "tagInput.field": "\u6DFB\u52A0\u6807\u7B7E",
929
+ "tagInput.added": "\u5DF2\u6DFB\u52A0 {p0} \u4E2A\u6807\u7B7E\u3002",
930
+ "tagInput.removed": "\u5DF2\u79FB\u9664 {p0}\u3002",
931
+ "tagInput.duplicate": "\u5DF2\u6709\u300C{p0}\u300D\uFF0C\u53EF\u4EE5\u7F16\u8F91\u540E\u91CD\u8BD5\u3002",
932
+ "tagInput.limit": "\u6700\u591A {p0} \u4E2A\u6807\u7B7E\uFF0C\u8BF7\u5148\u79FB\u9664\u4E00\u4E2A\u3002",
933
+ /* ── upload ── */
934
+ "upload.remove": "\u79FB\u9664 {p0}",
935
+ "upload.tooBig": "\u6587\u4EF6\u8D85\u8FC7 {p0}",
936
+ "upload.tooMany": "\u6700\u591A {p0} \u4E2A\u6587\u4EF6",
937
+ "upload.typeNotAllowed": "\u6587\u4EF6\u7C7B\u578B\u4E0D\u652F\u6301"
938
+ };
939
+ var current = { ...ICEN_LOCALE_ZH_CN };
940
+ function t(key, vars) {
941
+ const raw = current[key] ?? ICEN_LOCALE_ZH_CN[key];
942
+ if (vars == null || Array.isArray(raw)) return raw;
943
+ return raw.replace(/\{(\w+)\}/g, (m, name) => name in vars ? String(vars[name]) : m);
944
+ }
945
+
8
946
  // src/behaviors/charts.ts
9
947
  var SVG_NS = "http://www.w3.org/2000/svg";
10
948
  var REGISTERED_TONES = /* @__PURE__ */ new Map();
@@ -17,16 +955,16 @@ function toneVar(tone = "accent") {
17
955
  if (registered) return registered;
18
956
  switch (tone) {
19
957
  case "success":
20
- return "var(--token-success)";
958
+ return "var(--icen-token-success)";
21
959
  case "warning":
22
- return "var(--token-warning)";
960
+ return "var(--icen-token-warning)";
23
961
  case "error":
24
- return "var(--token-error)";
962
+ return "var(--icen-token-error)";
25
963
  case "muted":
26
- return "var(--token-text-faint)";
964
+ return "var(--icen-token-text-faint)";
27
965
  case "accent":
28
966
  default:
29
- return "var(--token-accent)";
967
+ return "var(--icen-token-accent)";
30
968
  }
31
969
  }
32
970
  function fmt(opts, value) {
@@ -37,13 +975,66 @@ function maxOf(values) {
37
975
  for (const v of values) if (v > max) max = v;
38
976
  return max;
39
977
  }
40
- function sampleLabels(labels, max = 7) {
41
- if (labels.length <= max) return labels;
42
- const out = [];
43
- for (let i = 0; i < max; i++) {
44
- out.push(labels[Math.round(i * (labels.length - 1) / (max - 1))] ?? "");
978
+ function pointLabelsRow(labels, width, pad) {
979
+ const row = document.createElement("div");
980
+ row.className = "icen-chart-labels icen-chart-labels--points";
981
+ const count = Math.min(labels.length, Math.max(2, Math.min(7, Math.floor(width / 64))));
982
+ const step = count > 1 ? (width - 2 * pad) / (count - 1) : width;
983
+ for (let tick = 0; tick < count; tick++) {
984
+ const index = count <= 1 ? 0 : Math.round(tick * (labels.length - 1) / (count - 1));
985
+ const x = labels.length <= 1 ? width / 2 : pad + index / (labels.length - 1) * (width - 2 * pad);
986
+ const span = document.createElement("span");
987
+ span.textContent = labels[index] ?? "";
988
+ span.title = span.textContent;
989
+ span.dir = "auto";
990
+ span.dataset.icenChartIndex = String(index);
991
+ span.style.left = `${x / width * 100}%`;
992
+ span.style.width = `${Math.max(0, Math.min(step * 0.9, 2 * x, 2 * (width - x)))}px`;
993
+ row.appendChild(span);
45
994
  }
46
- return out;
995
+ return row;
996
+ }
997
+ function clampNum(v, lo, hi) {
998
+ return Math.min(hi, Math.max(lo, v));
999
+ }
1000
+ function lttb(points, threshold, getX, getY) {
1001
+ const len = points.length;
1002
+ const n = Math.floor(threshold);
1003
+ if (!Number.isFinite(n) || n < 3 || n >= len || len <= 3) return points;
1004
+ const sampled = new Array(n);
1005
+ sampled[0] = points[0];
1006
+ sampled[n - 1] = points[len - 1];
1007
+ const every = (len - 2) / (n - 2);
1008
+ let a = 0;
1009
+ for (let i = 0; i < n - 2; i++) {
1010
+ const avgStart = Math.floor((i + 1) * every) + 1;
1011
+ const avgEnd = Math.min(Math.floor((i + 2) * every) + 1, len);
1012
+ let avgX = 0;
1013
+ let avgY = 0;
1014
+ for (let j = avgStart; j < avgEnd; j++) {
1015
+ avgX += getX(points[j]);
1016
+ avgY += getY(points[j]);
1017
+ }
1018
+ const avgLen = Math.max(1, avgEnd - avgStart);
1019
+ avgX /= avgLen;
1020
+ avgY /= avgLen;
1021
+ const rangeStart = Math.floor(i * every) + 1;
1022
+ const rangeEnd = Math.min(Math.floor((i + 1) * every) + 1, len);
1023
+ const ax = getX(points[a]);
1024
+ const ay = getY(points[a]);
1025
+ let maxArea = -1;
1026
+ let nextA = rangeStart;
1027
+ for (let j = rangeStart; j < rangeEnd; j++) {
1028
+ const area = Math.abs((ax - avgX) * (getY(points[j]) - ay) - (ax - getX(points[j])) * (avgY - ay)) * 0.5;
1029
+ if (area > maxArea) {
1030
+ maxArea = area;
1031
+ nextA = j;
1032
+ }
1033
+ }
1034
+ sampled[i + 1] = points[nextA];
1035
+ a = nextA;
1036
+ }
1037
+ return sampled;
47
1038
  }
48
1039
  function svgEl(tag, attrs) {
49
1040
  const el = document.createElementNS(SVG_NS, tag);
@@ -52,28 +1043,28 @@ function svgEl(tag, attrs) {
52
1043
  }
53
1044
  function renderEmpty(el, label) {
54
1045
  const box = document.createElement("div");
55
- box.className = "chart-empty";
56
- box.textContent = label ?? "\u6682\u65E0\u6570\u636E";
1046
+ box.className = "icen-chart-empty";
1047
+ box.textContent = label ?? t("charts.noData");
57
1048
  el.appendChild(box);
58
1049
  }
59
1050
  function legendItem(label, valueText, tone) {
60
1051
  const row = document.createElement("div");
61
- row.className = "chart-legend-item";
1052
+ row.className = "icen-chart-legend-item";
62
1053
  const dot = document.createElement("span");
63
- dot.className = "chart-legend-dot";
1054
+ dot.className = "icen-chart-legend-dot";
64
1055
  if (tone) dot.style.setProperty("--chart-tone", toneVar(tone));
65
1056
  const name = document.createElement("span");
66
- name.className = "chart-legend-label";
1057
+ name.className = "icen-chart-legend-label";
67
1058
  name.textContent = label;
68
1059
  const val = document.createElement("span");
69
- val.className = "chart-legend-value";
1060
+ val.className = "icen-chart-legend-value";
70
1061
  val.textContent = valueText;
71
1062
  row.append(dot, name, val);
72
1063
  return row;
73
1064
  }
74
1065
  function labelsRow(labels) {
75
1066
  const row = document.createElement("div");
76
- row.className = "chart-labels";
1067
+ row.className = "icen-chart-labels";
77
1068
  for (const label of labels) {
78
1069
  const span = document.createElement("span");
79
1070
  span.textContent = label;
@@ -88,85 +1079,90 @@ function normalizeSeriesInput(opts) {
88
1079
  function toneOf(tone, seriesIndex) {
89
1080
  if (typeof tone === "string") return toneVar(tone);
90
1081
  if (typeof tone === "number") return paletteVar(tone);
91
- return seriesIndex > 0 ? paletteVar((seriesIndex - 1) % 6 + 1) : void 0;
1082
+ return seriesIndex > 0 ? paletteVar(seriesIndex % 6 + 1) : void 0;
92
1083
  }
93
1084
  var PALETTE_VARS = [
94
- "var(--token-accent)",
95
- "var(--token-accent-cold)",
96
- "var(--token-success)",
97
- "var(--token-warning)",
98
- "var(--token-info)",
99
- "color-mix(in srgb, var(--token-text) 50%, transparent)"
1085
+ "var(--icen-token-accent)",
1086
+ "var(--icen-token-accent-cold)",
1087
+ "var(--icen-token-success)",
1088
+ "var(--icen-token-warning)",
1089
+ "var(--icen-token-info)",
1090
+ "color-mix(in srgb, var(--icen-token-text) 50%, transparent)"
100
1091
  ];
101
1092
  function paletteVar(index) {
102
1093
  const i = ((Math.floor(index) - 1) % 6 + 6) % 6;
103
1094
  return PALETTE_VARS[i];
104
1095
  }
105
1096
  function setMarkData(node, data) {
106
- node.setAttribute("data-chart-mark", "1");
107
- if (data.index != null) node.setAttribute("data-chart-index", String(data.index));
108
- if (data.seriesIndex != null) node.setAttribute("data-chart-series", String(data.seriesIndex));
109
- if (data.seriesName != null) node.setAttribute("data-chart-series-name", String(data.seriesName));
110
- if (data.label != null) node.setAttribute("data-chart-label", String(data.label));
111
- if (data.value != null) node.setAttribute("data-chart-value", String(data.value));
1097
+ node.setAttribute("data-icen-chart-mark", "1");
1098
+ if (data.index != null) node.setAttribute("data-icen-chart-index", String(data.index));
1099
+ if (data.seriesIndex != null) node.setAttribute("data-icen-chart-series", String(data.seriesIndex));
1100
+ if (data.seriesName != null) node.setAttribute("data-icen-chart-series-name", String(data.seriesName));
1101
+ if (data.label != null) node.setAttribute("data-icen-chart-label", String(data.label));
1102
+ if (data.value != null) node.setAttribute("data-icen-chart-value", String(data.value));
112
1103
  }
113
1104
  function copyMarkData(target, source) {
114
1105
  for (const attr of Array.from(source.attributes)) {
115
- if (attr.name.startsWith("data-chart-")) target.setAttribute(attr.name, attr.value);
1106
+ if (attr.name.startsWith("data-icen-chart-")) target.setAttribute(attr.name, attr.value);
116
1107
  }
117
1108
  }
118
- var CHART_EYE_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.06 12.35a1 1 0 0 1 0-.7 10.75 10.75 0 0 1 19.88 0 1 1 0 0 1 0 .7 10.75 10.75 0 0 1-19.88 0"/><circle cx="12" cy="12" r="3"/></svg>';
119
- var CHART_EYE_OFF_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.73 5.08A10.4 10.4 0 0 1 12 5c7 0 10 7 10 7a13.2 13.2 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.5 13.5 0 0 0 2 12s3 7 10 7a9.7 9.7 0 0 0 5.39-1.61"/><path d="M2 2l20 20"/><path d="M9.9 9.9a3 3 0 0 0 4.2 4.2"/></svg>';
120
- function parseChartSvg(svg) {
121
- try {
122
- const doc = new DOMParser().parseFromString(svg, "image/svg+xml");
123
- const el = doc.documentElement;
124
- return el && el.tagName.toLowerCase() === "svg" && !doc.querySelector("parsererror") ? document.importNode(el, true) : null;
125
- } catch {
126
- return null;
127
- }
128
- }
129
- function withChrome(el, opts, hasLegend, paint, state) {
130
- if (el.dataset.chartBody != null) {
1109
+ function withChrome(el, opts, hasLegend, paint, state, extras) {
1110
+ if (el.dataset.icenChartBody != null) {
131
1111
  el.textContent = "";
132
1112
  paint(el);
133
1113
  return;
134
1114
  }
135
1115
  el.textContent = "";
136
1116
  const body = document.createElement("div");
137
- body.className = "chart-body";
138
- body.dataset.chartBody = "";
139
- if (!opts.title && !hasLegend) {
1117
+ body.className = "icen-chart-body";
1118
+ body.dataset.icenChartBody = "";
1119
+ const note = extras?.note ?? null;
1120
+ const onZoomReset = extras?.onZoomReset ?? null;
1121
+ if (!opts.title && !hasLegend && note == null && !onZoomReset) {
140
1122
  el.appendChild(body);
141
1123
  paint(body);
142
1124
  return;
143
1125
  }
144
1126
  let hidden = state && state.hidden != null ? state.hidden : opts.legend === false;
145
1127
  if (state) state.hidden = hidden;
146
- el.setAttribute("data-legend-hidden", String(hidden));
1128
+ el.setAttribute("data-icen-legend-hidden", String(hidden));
147
1129
  const head = document.createElement("div");
148
- head.className = "chart-head";
1130
+ head.className = "icen-chart-head";
149
1131
  const title = document.createElement("div");
150
- title.className = "chart-title";
1132
+ title.className = "icen-chart-title";
151
1133
  title.textContent = opts.title ?? "";
152
1134
  if (!opts.title) title.setAttribute("aria-hidden", "true");
1135
+ if (note != null) {
1136
+ const noteEl = document.createElement("span");
1137
+ noteEl.className = "icen-chart-note";
1138
+ noteEl.textContent = note;
1139
+ head.appendChild(noteEl);
1140
+ }
1141
+ if (onZoomReset) {
1142
+ const chip = document.createElement("button");
1143
+ chip.type = "button";
1144
+ chip.className = "icen-chart-zoom-reset";
1145
+ chip.textContent = t("charts.resetZoom");
1146
+ chip.addEventListener("click", onZoomReset);
1147
+ head.appendChild(chip);
1148
+ }
153
1149
  const eye = document.createElement("button");
154
1150
  eye.type = "button";
155
- eye.className = "chart-legend-eye";
156
- eye.dataset.chartLegendEye = "";
1151
+ eye.className = "icen-chart-legend-eye";
1152
+ eye.dataset.icenChartLegendEye = "";
157
1153
  const syncEye = () => {
158
- const nowHidden = el.getAttribute("data-legend-hidden") === "true";
1154
+ const nowHidden = el.getAttribute("data-icen-legend-hidden") === "true";
159
1155
  eye.setAttribute("aria-pressed", String(!nowHidden));
160
- eye.setAttribute("aria-label", nowHidden ? "\u663E\u793A\u56FE\u4F8B" : "\u9690\u85CF\u56FE\u4F8B");
161
- eye.setAttribute("title", nowHidden ? "\u663E\u793A\u56FE\u4F8B" : "\u9690\u85CF\u56FE\u4F8B");
162
- const icon = parseChartSvg(nowHidden ? CHART_EYE_OFF_SVG : CHART_EYE_SVG);
1156
+ eye.setAttribute("aria-label", nowHidden ? t("charts.showLegend") : t("charts.hideLegend"));
1157
+ eye.setAttribute("title", nowHidden ? t("charts.showLegend") : t("charts.hideLegend"));
1158
+ const icon = createIcon(nowHidden ? "eye-off" : "eye");
163
1159
  if (icon) eye.replaceChildren(icon);
164
1160
  };
165
1161
  eye.addEventListener("click", () => {
166
- const nowHidden = el.getAttribute("data-legend-hidden") === "true";
1162
+ const nowHidden = el.getAttribute("data-icen-legend-hidden") === "true";
167
1163
  hidden = !nowHidden;
168
1164
  if (state) state.hidden = hidden;
169
- el.setAttribute("data-legend-hidden", String(hidden));
1165
+ el.setAttribute("data-icen-legend-hidden", String(hidden));
170
1166
  syncEye();
171
1167
  emitIcen(el, "icen:chart-legend-visibility", { visible: hidden ? false : true });
172
1168
  });
@@ -175,23 +1171,30 @@ function withChrome(el, opts, hasLegend, paint, state) {
175
1171
  el.append(head, body);
176
1172
  paint(body);
177
1173
  }
1174
+ var hiddenLegendKeys = /* @__PURE__ */ new WeakMap();
178
1175
  function toggleLegend(items, scope) {
179
1176
  const legend = document.createElement("div");
180
- legend.className = "chart-legend chart-legend--toggle";
1177
+ legend.className = "icen-chart-legend icen-chart-legend--toggle";
1178
+ const owner = scope instanceof Element ? scope.closest("[data-icen-chart-body]")?.parentElement ?? scope : null;
1179
+ const hidden = owner ? hiddenLegendKeys.get(owner) ?? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set();
1180
+ if (owner) hiddenLegendKeys.set(owner, hidden);
181
1181
  for (const item of items) {
182
1182
  const row = legendItem(item.label, "", item.tone);
183
- row.classList.add("chart-legend-toggle-item");
1183
+ row.classList.add("icen-chart-legend-toggle-item");
184
1184
  row.setAttribute("role", "button");
185
1185
  row.tabIndex = 0;
186
- row.setAttribute("aria-pressed", "true");
187
- row.dataset.chartKey = item.key;
188
- const dot = row.querySelector(".chart-legend-dot");
189
- const toneColor = toneOf(item.tone, item.si + 1);
1186
+ row.classList.toggle("is-off", hidden.has(item.key));
1187
+ row.setAttribute("aria-pressed", String(!hidden.has(item.key)));
1188
+ row.dataset.icenChartKey = item.key;
1189
+ const dot = row.querySelector(".icen-chart-legend-dot");
1190
+ const toneColor = toneOf(item.tone, item.si);
190
1191
  if (dot && toneColor) dot.style.setProperty("--chart-tone", toneColor);
191
1192
  const toggle = () => {
192
1193
  const off = row.classList.toggle("is-off");
1194
+ if (off) hidden.add(item.key);
1195
+ else hidden.delete(item.key);
193
1196
  row.setAttribute("aria-pressed", String(!off));
194
- scope.querySelectorAll(`[data-chart-key="${CSS.escape(item.key)}"]`).forEach((m) => {
1197
+ scope.querySelectorAll(`[data-icen-chart-key="${CSS.escape(item.key)}"]`).forEach((m) => {
195
1198
  m.classList.toggle("is-off", off);
196
1199
  });
197
1200
  emitIcen(legend, "icen:chart-legend-toggle", { key: item.key, seriesIndex: item.si, hidden: off });
@@ -206,8 +1209,9 @@ function toggleLegend(items, scope) {
206
1209
  legend.appendChild(row);
207
1210
  }
208
1211
  for (const item of items) {
209
- scope.querySelectorAll(`[data-chart-series="${item.si}"]`).forEach((m) => {
210
- if (!m.hasAttribute("data-chart-key")) m.setAttribute("data-chart-key", item.key);
1212
+ scope.querySelectorAll(`[data-icen-chart-series="${item.si}"]`).forEach((m) => {
1213
+ if (!m.hasAttribute("data-icen-chart-key")) m.setAttribute("data-icen-chart-key", item.key);
1214
+ m.classList.toggle("is-off", hidden.has(item.key));
211
1215
  });
212
1216
  }
213
1217
  return legend;
@@ -234,19 +1238,19 @@ function renderVBar(el, opts) {
234
1238
  for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
235
1239
  }
236
1240
  const wrap = document.createElement("div");
237
- wrap.className = "chart-vbar";
1241
+ wrap.className = "icen-chart-vbar";
238
1242
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
239
1243
  for (let i = 0; i < n; i++) {
240
1244
  const track = document.createElement("div");
241
- track.className = "chart-vbar-track";
1245
+ track.className = "icen-chart-vbar-track";
242
1246
  if (seriesList.length > 1) {
243
- track.classList.add("chart-vbar-track--multi");
244
- if (opts.stacked) track.classList.add("chart-vbar-track--stacked");
1247
+ track.classList.add("icen-chart-vbar-track--multi");
1248
+ if (opts.stacked) track.classList.add("icen-chart-vbar-track--stacked");
245
1249
  }
246
1250
  seriesList.forEach((s, si) => {
247
1251
  const value = s.values[i] ?? 0;
248
1252
  const bar = document.createElement("div");
249
- bar.className = "chart-vbar-bar";
1253
+ bar.className = "icen-chart-vbar-bar";
250
1254
  const height = value > 0 ? Math.max(6, value / max * 100) : 0;
251
1255
  bar.style.height = `${height}%`;
252
1256
  const toneColor = toneOf(s.tone, si);
@@ -264,7 +1268,7 @@ function renderVBar(el, opts) {
264
1268
  wrap.appendChild(track);
265
1269
  }
266
1270
  body.append(wrap, labelsRow(labelsUsed));
267
- if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
1271
+ if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone ?? (si === 0 ? opts.tone : void 0), si })), wrap));
268
1272
  });
269
1273
  attachChartInteraction(el, "vbar");
270
1274
  return el;
@@ -281,22 +1285,22 @@ function renderHBar(el, opts) {
281
1285
  const values = opts.values.slice(0, n);
282
1286
  const max = maxOf(values);
283
1287
  const wrap = document.createElement("div");
284
- wrap.className = "chart-hbar";
1288
+ wrap.className = "icen-chart-hbar";
285
1289
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
286
1290
  values.forEach((value, i) => {
287
1291
  const row = document.createElement("div");
288
- row.className = "chart-hbar-row";
1292
+ row.className = "icen-chart-hbar-row";
289
1293
  const label = document.createElement("span");
290
- label.className = "chart-hbar-label";
1294
+ label.className = "icen-chart-hbar-label";
291
1295
  label.textContent = labels[i] ?? "";
292
1296
  const track = document.createElement("div");
293
- track.className = "chart-hbar-track";
1297
+ track.className = "icen-chart-hbar-track";
294
1298
  const bar = document.createElement("div");
295
- bar.className = "chart-hbar-bar";
1299
+ bar.className = "icen-chart-hbar-bar";
296
1300
  bar.style.width = value > 0 ? `${Math.max(2, value / max * 100)}%` : "0%";
297
1301
  setMarkData(bar, { index: i, label: labels[i] ?? "", value });
298
1302
  const val = document.createElement("span");
299
- val.className = "chart-hbar-value";
1303
+ val.className = "icen-chart-hbar-value";
300
1304
  val.textContent = fmt(opts, value);
301
1305
  track.appendChild(bar);
302
1306
  row.append(label, track, val);
@@ -316,10 +1320,10 @@ function renderStack(el, opts) {
316
1320
  return;
317
1321
  }
318
1322
  const bar = document.createElement("div");
319
- bar.className = "chart-stack";
1323
+ bar.className = "icen-chart-stack";
320
1324
  for (const [segIndex, seg] of opts.segments.entries()) {
321
1325
  const s = document.createElement("div");
322
- s.className = "chart-stack-seg";
1326
+ s.className = "icen-chart-stack-seg";
323
1327
  s.style.width = `${seg.value / total * 100}%`;
324
1328
  if (seg.tone) s.style.setProperty("--chart-tone", toneVar(seg.tone));
325
1329
  s.setAttribute("title", `${seg.label}: ${fmt(opts, seg.value)}`);
@@ -327,7 +1331,7 @@ function renderStack(el, opts) {
327
1331
  bar.appendChild(s);
328
1332
  }
329
1333
  const legend = document.createElement("div");
330
- legend.className = "chart-legend";
1334
+ legend.className = "icen-chart-legend";
331
1335
  for (const seg of opts.segments) {
332
1336
  legend.appendChild(legendItem(seg.label, fmt(opts, seg.value), seg.tone));
333
1337
  }
@@ -347,15 +1351,15 @@ function renderDonut(el, opts) {
347
1351
  const radius = 42;
348
1352
  const circumference = 2 * Math.PI * radius;
349
1353
  const wrap = document.createElement("div");
350
- wrap.className = "chart-donut";
351
- const svg = svgEl("svg", { viewBox: "0 0 120 120", role: "img" });
1354
+ wrap.className = "icen-chart-donut";
1355
+ const svg = svgEl("svg", { viewBox: "0 0 120 120", role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.donut")) });
352
1356
  svg.style.transform = "rotate(-90deg)";
353
1357
  svg.appendChild(svgEl("circle", {
354
1358
  cx: "60",
355
1359
  cy: "60",
356
1360
  r: String(radius),
357
1361
  fill: "none",
358
- stroke: "var(--token-line-soft)",
1362
+ stroke: "var(--icen-token-line-soft)",
359
1363
  "stroke-width": "14"
360
1364
  }));
361
1365
  let offset = 0;
@@ -377,7 +1381,7 @@ function renderDonut(el, opts) {
377
1381
  offset += dash;
378
1382
  });
379
1383
  const legend = document.createElement("div");
380
- legend.className = "chart-legend";
1384
+ legend.className = "icen-chart-legend";
381
1385
  for (const seg of opts.segments) {
382
1386
  legend.appendChild(legendItem(seg.label, `${Math.round(seg.value / total * 100)}%`, seg.tone));
383
1387
  }
@@ -399,14 +1403,14 @@ function renderLine(el, opts) {
399
1403
  return;
400
1404
  }
401
1405
  const labelsUsed = labels.slice(0, n);
402
- const width = 360;
1406
+ const width = fluidWidth(body);
403
1407
  const height = 150;
404
1408
  const pad = 14;
405
1409
  let max = 1;
406
1410
  for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
407
1411
  const wrap = document.createElement("div");
408
- wrap.className = "chart-line";
409
- const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img" });
1412
+ wrap.className = "icen-chart-line";
1413
+ const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.line")) });
410
1414
  const defs = svgEl("defs", {});
411
1415
  for (let i = 0; i < 4; i++) {
412
1416
  const y = pad + i * (height - pad * 2) / 3;
@@ -415,7 +1419,7 @@ function renderLine(el, opts) {
415
1419
  x2: String(width - pad),
416
1420
  y1: String(y),
417
1421
  y2: String(y),
418
- stroke: "var(--token-line-soft)",
1422
+ stroke: "var(--icen-token-line-soft)",
419
1423
  "stroke-width": "1"
420
1424
  }));
421
1425
  }
@@ -456,7 +1460,7 @@ function renderLine(el, opts) {
456
1460
  "stroke-linejoin": "round"
457
1461
  });
458
1462
  const lineTitle = svgEl("title", {});
459
- lineTitle.textContent = s.name || "\u6570\u503C";
1463
+ lineTitle.textContent = s.name || t("charts.value");
460
1464
  linePath.appendChild(lineTitle);
461
1465
  setMarkData(linePath, { seriesIndex: si, seriesName: s.name, label: s.name, value: values[values.length - 1] ?? 0 });
462
1466
  svg.appendChild(linePath);
@@ -465,7 +1469,7 @@ function renderLine(el, opts) {
465
1469
  cx: p.x.toFixed(1),
466
1470
  cy: p.y.toFixed(1),
467
1471
  r: "3",
468
- fill: "var(--token-card)",
1472
+ fill: "var(--icen-token-card)",
469
1473
  stroke: tone,
470
1474
  "stroke-width": "2"
471
1475
  });
@@ -485,17 +1489,18 @@ function renderLine(el, opts) {
485
1489
  cy: p.y.toFixed(1),
486
1490
  r: "8",
487
1491
  fill: "transparent",
488
- "data-chart-hit": "1"
1492
+ "data-icen-chart-hit": "1"
489
1493
  });
490
1494
  copyMarkData(hit, dot);
491
1495
  svg.appendChild(hit);
492
1496
  }
493
1497
  });
494
1498
  if (defs.firstChild) svg.insertBefore(defs, svg.firstChild);
495
- wrap.append(svg, labelsRow(sampleLabels(labelsUsed)));
1499
+ wrap.append(svg, pointLabelsRow(labelsUsed, width, pad));
496
1500
  body.appendChild(wrap);
497
- if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
1501
+ if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone ?? (si === 0 ? opts.tone : void 0), si })), wrap));
498
1502
  });
1503
+ if (el.dataset.icenChartBody == null) observeFluidWidth(el, () => renderLine(el, opts));
499
1504
  attachChartInteraction(el, "line");
500
1505
  return el;
501
1506
  }
@@ -509,7 +1514,7 @@ function renderArea(el, opts) {
509
1514
  }
510
1515
  const labels = opts.labels.slice(0, n);
511
1516
  const values = opts.values.slice(0, n);
512
- const width = 360;
1517
+ const width = fluidWidth(body);
513
1518
  const height = 150;
514
1519
  const pad = 14;
515
1520
  const max = maxOf(values);
@@ -526,8 +1531,8 @@ function renderArea(el, opts) {
526
1531
  const area = `${line} L${last.x.toFixed(1)},${height - pad} L${first.x.toFixed(1)},${height - pad} Z`;
527
1532
  const gradientId = `icen-chart-area-${++lineGradientSeq}`;
528
1533
  const wrap = document.createElement("div");
529
- wrap.className = "chart-area";
530
- const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img" });
1534
+ wrap.className = "icen-chart-area";
1535
+ const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.area")) });
531
1536
  const defs = svgEl("defs", {});
532
1537
  const gradient = svgEl("linearGradient", { id: gradientId, x1: "0", x2: "0", y1: "0", y2: "1" });
533
1538
  gradient.appendChild(svgEl("stop", { offset: "0%", "stop-color": tone, "stop-opacity": "0.5" }));
@@ -541,7 +1546,7 @@ function renderArea(el, opts) {
541
1546
  x2: String(width - pad),
542
1547
  y1: String(y),
543
1548
  y2: String(y),
544
- stroke: "var(--token-line-soft)",
1549
+ stroke: "var(--icen-token-line-soft)",
545
1550
  "stroke-width": "1"
546
1551
  }));
547
1552
  }
@@ -566,9 +1571,10 @@ function renderArea(el, opts) {
566
1571
  inv.appendChild(title);
567
1572
  svg.appendChild(inv);
568
1573
  }
569
- wrap.append(svg, labelsRow(sampleLabels(labels)));
1574
+ wrap.append(svg, pointLabelsRow(labels, width, pad));
570
1575
  body.appendChild(wrap);
571
1576
  });
1577
+ if (el.dataset.icenChartBody == null) observeFluidWidth(el, () => renderArea(el, opts));
572
1578
  attachChartInteraction(el, "area");
573
1579
  return el;
574
1580
  }
@@ -600,8 +1606,8 @@ function renderRadar(el, opts) {
600
1606
  y: cy + r * Math.sin(angleOf(i))
601
1607
  });
602
1608
  const wrap = document.createElement("div");
603
- wrap.className = "chart-radar";
604
- const svg = svgEl("svg", { viewBox: `0 0 ${size} ${size}`, role: "img" });
1609
+ wrap.className = "icen-chart-radar";
1610
+ const svg = svgEl("svg", { viewBox: `0 0 ${size} ${size}`, role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.radar")) });
605
1611
  for (let lv = 1; lv <= levels; lv++) {
606
1612
  const r = radius * lv / levels;
607
1613
  const pts = Array.from({ length: n }, (_, i) => {
@@ -611,7 +1617,7 @@ function renderRadar(el, opts) {
611
1617
  svg.appendChild(svgEl("polygon", {
612
1618
  points: pts,
613
1619
  fill: "none",
614
- stroke: "var(--token-line-soft)",
1620
+ stroke: "var(--icen-token-line-soft)",
615
1621
  "stroke-width": "1",
616
1622
  "stroke-dasharray": lv === levels ? "0" : "3 2"
617
1623
  }));
@@ -623,7 +1629,7 @@ function renderRadar(el, opts) {
623
1629
  y1: String(cy),
624
1630
  x2: edge.x.toFixed(1),
625
1631
  y2: edge.y.toFixed(1),
626
- stroke: "var(--token-line-soft)",
1632
+ stroke: "var(--icen-token-line-soft)",
627
1633
  "stroke-width": "1"
628
1634
  }));
629
1635
  const labelP = pointAt(i, radius + 14);
@@ -633,14 +1639,14 @@ function renderRadar(el, opts) {
633
1639
  "text-anchor": "middle",
634
1640
  "dominant-baseline": "middle",
635
1641
  "font-size": "9",
636
- fill: "var(--token-text-muted)"
1642
+ fill: "var(--icen-token-text-muted)"
637
1643
  });
638
1644
  txt.textContent = axes[i] ?? "";
639
1645
  svg.appendChild(txt);
640
1646
  }
641
1647
  const legendItems = [];
642
1648
  opts.series.forEach((s, si) => {
643
- const tone = toneVar(s.tone);
1649
+ const tone = toneOf(s.tone, si) ?? toneVar(s.tone);
644
1650
  const pts = Array.from({ length: n }, (_, i) => {
645
1651
  const v = Math.max(0, Math.min(maxVal, s.values[i] ?? 0));
646
1652
  const p = pointAt(i, v / maxVal * radius);
@@ -666,18 +1672,19 @@ function renderRadar(el, opts) {
666
1672
  cx: p.x.toFixed(1),
667
1673
  cy: p.y.toFixed(1),
668
1674
  r: "2.5",
669
- fill: "var(--token-card)",
1675
+ fill: "var(--icen-token-card)",
670
1676
  stroke: tone,
671
1677
  "stroke-width": "1.5"
672
1678
  });
673
1679
  setMarkData(dot, { index: i, seriesIndex: si, seriesName: s.name || void 0, label: axes[i] ?? "", value: s.values[i] ?? 0 });
674
1680
  svg.appendChild(dot);
675
1681
  }
676
- legendItems.push({ key: s.name || `\u7CFB\u5217${si + 1}`, label: s.name || `\u7CFB\u5217${si + 1}`, tone: s.tone, si });
1682
+ legendItems.push({ key: s.name || t("charts.seriesName", { p0: si + 1 }), label: s.name || t("charts.seriesName", { p0: si + 1 }), tone: s.tone, si });
677
1683
  });
678
- const legend = opts.series.length > 1 ? toggleLegend(legendItems, wrap) : null;
679
- wrap.append(svg, ...legend ? [legend] : []);
1684
+ wrap.appendChild(svg);
680
1685
  body.appendChild(wrap);
1686
+ const legend = opts.series.length > 1 ? toggleLegend(legendItems, wrap) : null;
1687
+ if (legend) wrap.appendChild(legend);
681
1688
  });
682
1689
  attachChartInteraction(el, "radar");
683
1690
  return el;
@@ -710,23 +1717,23 @@ function padFirstWeek(grid, firstDate, weekStart) {
710
1717
  const pad = (first.getDay() + 7 - weekStart) % 7;
711
1718
  for (let i = 0; i < pad; i++) {
712
1719
  const blank = document.createElement("span");
713
- blank.className = "chart-heatmap-cell is-blank";
1720
+ blank.className = "icen-chart-heatmap-cell is-blank";
714
1721
  grid.appendChild(blank);
715
1722
  }
716
1723
  return pad;
717
1724
  }
718
1725
  function heatmapLegend(labels) {
719
1726
  const legend = document.createElement("div");
720
- legend.className = "chart-heatmap-legend";
1727
+ legend.className = "icen-chart-heatmap-legend";
721
1728
  const less = document.createElement("span");
722
- less.textContent = labels?.less ?? "\u5C11";
1729
+ less.textContent = labels?.less ?? t("charts.few");
723
1730
  const more = document.createElement("span");
724
- more.textContent = labels?.more ?? "\u591A";
1731
+ more.textContent = labels?.more ?? t("charts.many");
725
1732
  legend.appendChild(less);
726
1733
  for (let lv = 0; lv <= 4; lv++) {
727
1734
  const cell = document.createElement("span");
728
- cell.className = "chart-heatmap-cell";
729
- cell.dataset.level = String(lv);
1735
+ cell.className = "icen-chart-heatmap-cell";
1736
+ cell.dataset.icenLevel = String(lv);
730
1737
  legend.appendChild(cell);
731
1738
  }
732
1739
  legend.appendChild(more);
@@ -753,16 +1760,17 @@ function renderHeatmap(el, opts) {
753
1760
  if (entries.length > weeks * 7) entries = entries.slice(entries.length - weeks * 7);
754
1761
  const max = maxOf(entries.map((e) => e.value));
755
1762
  const wrap = document.createElement("div");
756
- wrap.className = "chart-heatmap";
1763
+ wrap.className = "icen-chart-heatmap";
757
1764
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
758
1765
  const grid = document.createElement("div");
759
- grid.className = "chart-heatmap-grid";
1766
+ grid.className = "icen-chart-heatmap-grid";
760
1767
  grid.setAttribute("role", "img");
1768
+ grid.setAttribute("aria-label", chartAriaLabel(opts.title, t("charts.type.heatmap")));
761
1769
  padFirstWeek(grid, entries[0].date, opts.weekStart === 0 ? 0 : 1);
762
1770
  for (const [i, entry] of entries.entries()) {
763
1771
  const cell = document.createElement("span");
764
- cell.className = "chart-heatmap-cell";
765
- cell.dataset.level = String(levelOf(entry.value, max));
1772
+ cell.className = "icen-chart-heatmap-cell";
1773
+ cell.dataset.icenLevel = String(levelOf(entry.value, max));
766
1774
  cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
767
1775
  setMarkData(cell, { index: i, label: entry.date, value: entry.value });
768
1776
  grid.appendChild(cell);
@@ -797,8 +1805,8 @@ function renderSparkline(el, opts) {
797
1805
  const area = `${line} L${last.x.toFixed(1)},${height - pad} L${first.x.toFixed(1)},${height - pad} Z`;
798
1806
  const gradientId = `icen-chart-line-${++lineGradientSeq}`;
799
1807
  const wrap = document.createElement("div");
800
- wrap.className = "chart-sparkline";
801
- const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img" });
1808
+ wrap.className = "icen-chart-sparkline";
1809
+ const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.sparkline")) });
802
1810
  const defs = svgEl("defs", {});
803
1811
  const gradient = svgEl("linearGradient", { id: gradientId, x1: "0", x2: "0", y1: "0", y2: "1" });
804
1812
  gradient.appendChild(svgEl("stop", { offset: "0%", "stop-color": tone, "stop-opacity": "0.28" }));
@@ -818,7 +1826,7 @@ function renderSparkline(el, opts) {
818
1826
  cx: last.x.toFixed(1),
819
1827
  cy: last.y.toFixed(1),
820
1828
  r: "2.4",
821
- fill: "var(--token-card)",
1829
+ fill: "var(--icen-token-card)",
822
1830
  stroke: tone,
823
1831
  "stroke-width": "1.6"
824
1832
  });
@@ -840,16 +1848,16 @@ function renderGauge(el, opts) {
840
1848
  const radius = 42;
841
1849
  const circumference = 2 * Math.PI * radius;
842
1850
  const wrap = document.createElement("div");
843
- wrap.className = "chart-gauge";
1851
+ wrap.className = "icen-chart-gauge";
844
1852
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
845
- const svg = svgEl("svg", { viewBox: "0 0 120 120", role: "img" });
1853
+ const svg = svgEl("svg", { viewBox: "0 0 120 120", role: "img", "aria-label": chartAriaLabel(opts.label, t("charts.type.gauge")) });
846
1854
  svg.style.transform = "rotate(-90deg)";
847
1855
  svg.appendChild(svgEl("circle", {
848
1856
  cx: "60",
849
1857
  cy: "60",
850
1858
  r: String(radius),
851
1859
  fill: "none",
852
- stroke: "var(--token-line-soft)",
1860
+ stroke: "var(--icen-token-line-soft)",
853
1861
  "stroke-width": "10"
854
1862
  }));
855
1863
  const dash = ratio * circumference;
@@ -864,14 +1872,14 @@ function renderGauge(el, opts) {
864
1872
  "stroke-linecap": "round"
865
1873
  }));
866
1874
  const center = document.createElement("div");
867
- center.className = "chart-gauge-center";
1875
+ center.className = "icen-chart-gauge-center";
868
1876
  const val = document.createElement("span");
869
- val.className = "chart-gauge-value";
1877
+ val.className = "icen-chart-gauge-value";
870
1878
  val.textContent = opts.formatValue ? opts.formatValue(opts.value) : `${Math.round(ratio * 100)}%`;
871
1879
  center.appendChild(val);
872
1880
  if (opts.label) {
873
1881
  const lab = document.createElement("span");
874
- lab.className = "chart-gauge-label";
1882
+ lab.className = "icen-chart-gauge-label";
875
1883
  lab.textContent = opts.label;
876
1884
  center.appendChild(lab);
877
1885
  }
@@ -893,19 +1901,20 @@ function renderCalendar(el, opts) {
893
1901
  return;
894
1902
  }
895
1903
  const wrap = document.createElement("div");
896
- wrap.className = "chart-calendar";
1904
+ wrap.className = "icen-chart-calendar";
897
1905
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
898
1906
  const weekStart = opts.weekStart === 0 ? 0 : 1;
899
- const weekdayDefaults = ["\u4E00", "", "\u4E09", "", "\u4E94", "", "\u65E5"];
1907
+ const weekdayDefaults = t("charts.calendarWeekdays");
900
1908
  const weekdayLabels = opts.labels?.weekdays ?? weekdayDefaults.map((_, i) => weekdayLabelAt(weekdayDefaults, (weekStart + i) % 7));
901
- const monthLabel = (m) => opts.labels?.months?.[m] ?? `${m + 1}\u6708`;
1909
+ const monthLabel = (m) => opts.labels?.months?.[m] ?? t("charts.monthLabel", { p0: m + 1 });
902
1910
  const grid = document.createElement("div");
903
- grid.className = "chart-heatmap-grid";
1911
+ grid.className = "icen-chart-heatmap-grid";
904
1912
  grid.setAttribute("role", "img");
1913
+ grid.setAttribute("aria-label", chartAriaLabel(opts.title, t("charts.type.heatmap")));
905
1914
  const months = document.createElement("div");
906
- months.className = "chart-calendar-months";
1915
+ months.className = "icen-chart-calendar-months";
907
1916
  const weekdayRow = document.createElement("div");
908
- weekdayRow.className = "chart-calendar-weekdays";
1917
+ weekdayRow.className = "icen-chart-calendar-weekdays";
909
1918
  weekdayLabels.forEach((w) => {
910
1919
  const s = document.createElement("span");
911
1920
  s.textContent = w;
@@ -920,14 +1929,14 @@ function renderCalendar(el, opts) {
920
1929
  if (d.getMonth() !== lastMonth) {
921
1930
  lastMonth = d.getMonth();
922
1931
  const label = document.createElement("span");
923
- label.className = "chart-calendar-month";
1932
+ label.className = "icen-chart-calendar-month";
924
1933
  label.textContent = monthLabel(d.getMonth());
925
1934
  label.style.setProperty("--col", String(col));
926
1935
  months.appendChild(label);
927
1936
  }
928
1937
  const cell = document.createElement("span");
929
- cell.className = "chart-heatmap-cell";
930
- cell.dataset.level = String(levelOf(entry.value, max));
1938
+ cell.className = "icen-chart-heatmap-cell";
1939
+ cell.dataset.icenLevel = String(levelOf(entry.value, max));
931
1940
  cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
932
1941
  setMarkData(cell, { index: i, label: entry.date, value: entry.value });
933
1942
  grid.appendChild(cell);
@@ -949,7 +1958,7 @@ function renderScatter(el, opts) {
949
1958
  renderEmpty(body, opts.emptyLabel);
950
1959
  return;
951
1960
  }
952
- const width = 360;
1961
+ const width = fluidWidth(body);
953
1962
  const height = 220;
954
1963
  const pad = 30;
955
1964
  let xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, sMax = 0;
@@ -964,9 +1973,9 @@ function renderScatter(el, opts) {
964
1973
  if (yMax === yMin) yMax = yMin + 1;
965
1974
  const rOf = (size) => size && sMax > 0 ? 3 + size / sMax * 7 : 4;
966
1975
  const wrap = document.createElement("div");
967
- wrap.className = "chart-scatter";
1976
+ wrap.className = "icen-chart-scatter";
968
1977
  const tone = toneVar(opts.tone);
969
- const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img" });
1978
+ const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.scatter")) });
970
1979
  const xAt = (x) => pad + (x - xMin) / (xMax - xMin) * (width - pad * 2);
971
1980
  const yAt = (y) => height - pad - (y - yMin) / (yMax - yMin) * (height - pad * 2);
972
1981
  for (let i = 0; i <= 3; i++) {
@@ -976,7 +1985,7 @@ function renderScatter(el, opts) {
976
1985
  x2: String(width - pad),
977
1986
  y1: String(y),
978
1987
  y2: String(y),
979
- stroke: "var(--token-line-soft)",
1988
+ stroke: "var(--icen-token-line-soft)",
980
1989
  "stroke-width": "1"
981
1990
  }));
982
1991
  const tv = yMin + (yMax - yMin) * (3 - i) / 3;
@@ -985,7 +1994,7 @@ function renderScatter(el, opts) {
985
1994
  y: String(y + 3),
986
1995
  "text-anchor": "end",
987
1996
  "font-size": "8",
988
- fill: "var(--token-text-faint)"
1997
+ fill: "var(--icen-token-text-faint)"
989
1998
  });
990
1999
  txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
991
2000
  svg.appendChild(txt);
@@ -997,7 +2006,7 @@ function renderScatter(el, opts) {
997
2006
  x2: String(x),
998
2007
  y1: String(pad),
999
2008
  y2: String(height - pad),
1000
- stroke: "var(--token-line-soft)",
2009
+ stroke: "var(--icen-token-line-soft)",
1001
2010
  "stroke-width": "1",
1002
2011
  "stroke-dasharray": "2 3"
1003
2012
  }));
@@ -1007,20 +2016,20 @@ function renderScatter(el, opts) {
1007
2016
  y: String(height - pad + 12),
1008
2017
  "text-anchor": "middle",
1009
2018
  "font-size": "8",
1010
- fill: "var(--token-text-faint)"
2019
+ fill: "var(--icen-token-text-faint)"
1011
2020
  });
1012
2021
  txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
1013
2022
  svg.appendChild(txt);
1014
2023
  }
1015
2024
  if (opts.yLabel) {
1016
- const t = svgEl("text", { x: "10", y: String(pad - 10), "font-size": "8", fill: "var(--token-text-faint)" });
1017
- t.textContent = opts.yLabel;
1018
- svg.appendChild(t);
2025
+ const t2 = svgEl("text", { x: "10", y: String(pad - 10), "font-size": "8", fill: "var(--icen-token-text-faint)" });
2026
+ t2.textContent = opts.yLabel;
2027
+ svg.appendChild(t2);
1019
2028
  }
1020
2029
  if (opts.xLabel) {
1021
- const t = svgEl("text", { x: String(width - pad), y: String(pad - 10), "text-anchor": "end", "font-size": "8", fill: "var(--token-text-faint)" });
1022
- t.textContent = opts.xLabel;
1023
- svg.appendChild(t);
2030
+ const t2 = svgEl("text", { x: String(width - pad), y: String(pad - 10), "text-anchor": "end", "font-size": "8", fill: "var(--icen-token-text-faint)" });
2031
+ t2.textContent = opts.xLabel;
2032
+ svg.appendChild(t2);
1024
2033
  }
1025
2034
  pts.forEach((p, i) => {
1026
2035
  const c = svgEl("circle", {
@@ -1042,6 +2051,7 @@ function renderScatter(el, opts) {
1042
2051
  wrap.appendChild(svg);
1043
2052
  body.appendChild(wrap);
1044
2053
  });
2054
+ if (el.dataset.icenChartBody == null) observeFluidWidth(el, () => renderScatter(el, opts));
1045
2055
  attachChartInteraction(el, "scatter");
1046
2056
  return el;
1047
2057
  }
@@ -1091,7 +2101,7 @@ function pivotData(spec) {
1091
2101
  const labels = [];
1092
2102
  const seenLabel = /* @__PURE__ */ new Set();
1093
2103
  const hasSeries = data.some((r) => r[seriesField] != null && r[seriesField] !== "");
1094
- const seriesNames = [];
2104
+ const seriesNames = hasSeries ? [] : [""];
1095
2105
  const seenSeries = /* @__PURE__ */ new Set();
1096
2106
  for (const r of data) {
1097
2107
  const l = str(r[labelField]);
@@ -1180,8 +2190,8 @@ function normalizeChartSpec(raw) {
1180
2190
  const s = src;
1181
2191
  const spec = {};
1182
2192
  if (typeof s.type === "string") {
1183
- const t = s.type.trim().toLowerCase();
1184
- spec.type = CHART_TYPE_ALIASES[t] ?? t;
2193
+ const t2 = s.type.trim().toLowerCase();
2194
+ spec.type = CHART_TYPE_ALIASES[t2] ?? t2;
1185
2195
  }
1186
2196
  for (const k of ["title", "tone", "emptyLabel"]) {
1187
2197
  if (typeof s[k] === "string") spec[k] = s[k];
@@ -1203,7 +2213,7 @@ function normalizeChartSpec(raw) {
1203
2213
  if (Array.isArray(s.series)) {
1204
2214
  const series = s.series.filter((v) => !!v && typeof v === "object").map((v, i) => {
1205
2215
  const values = Array.isArray(v.values) ? v.values.map(toNum).filter((n) => n != null) : [];
1206
- const sv = { name: String(v.name ?? `\u7CFB\u5217${i + 1}`), values };
2216
+ const sv = { name: String(v.name ?? t("charts.seriesName", { p0: i + 1 })), values };
1207
2217
  const tone = v.tone;
1208
2218
  if (typeof tone === "string" || typeof tone === "number") sv.tone = tone;
1209
2219
  return sv;
@@ -1238,6 +2248,40 @@ function normalizeChartSpec(raw) {
1238
2248
  }
1239
2249
  spec.format = format;
1240
2250
  }
2251
+ if (typeof s.downsample === "boolean") spec.downsample = s.downsample;
2252
+ else {
2253
+ const n = toNum(s.downsample);
2254
+ if (n != null) spec.downsample = n;
2255
+ }
2256
+ if (s.zoom === "x" || s.zoom === "brush") spec.zoom = s.zoom;
2257
+ if (typeof s.loading === "boolean") spec.loading = s.loading;
2258
+ if (Array.isArray(s.marks)) {
2259
+ const marks = [];
2260
+ for (const raw2 of s.marks) {
2261
+ if (!raw2 || typeof raw2 !== "object" || Array.isArray(raw2)) continue;
2262
+ const m = raw2;
2263
+ if (m.type === "line") {
2264
+ if (m.axis !== "x" && m.axis !== "y") continue;
2265
+ if (typeof m.value !== "number" && typeof m.value !== "string") continue;
2266
+ const mark = { type: "line", axis: m.axis, value: m.value };
2267
+ if (typeof m.label === "string" && m.label !== "") mark.label = m.label;
2268
+ if (m.tone === "success" || m.tone === "warning" || m.tone === "error" || m.tone === "info") {
2269
+ mark.tone = m.tone;
2270
+ }
2271
+ marks.push(mark);
2272
+ } else if (m.type === "point") {
2273
+ if (typeof m.label !== "string" || m.label === "") continue;
2274
+ const mark = { type: "point", label: m.label };
2275
+ const si = toNum(m.seriesIndex);
2276
+ if (si != null && si >= 0) mark.seriesIndex = Math.floor(si);
2277
+ const v = toNum(m.value);
2278
+ if (v != null) mark.value = v;
2279
+ if (typeof m.text === "string") mark.text = m.text;
2280
+ marks.push(mark);
2281
+ }
2282
+ }
2283
+ if (marks.length > 0) spec.marks = marks;
2284
+ }
1241
2285
  const isDateRecords = spec.data?.length && spec.data[0] && "date" in spec.data[0];
1242
2286
  if (spec.data && spec.data.length > 0 && !isDateRecords && !spec.series && !spec.points && !spec.values && !spec.segments) {
1243
2287
  Object.assign(spec, pivotData(spec));
@@ -1277,6 +2321,391 @@ function inferChartType(spec) {
1277
2321
  if (typeof spec.max === "number" && spec.values && spec.values.length === 1) return "gauge";
1278
2322
  return "vbar";
1279
2323
  }
2324
+ var DOWNSAMPLE_DEFAULT_THRESHOLD = 1e3;
2325
+ var CHART_ZOOM_DRAG_MIN_PX = 5;
2326
+ var CHART_LINE_W = 360;
2327
+ var CHART_LINE_H = 150;
2328
+ var CHART_LINE_PAD = 14;
2329
+ function isDownsampleableType(type) {
2330
+ return type === "line" || type === "area" || type === "scatter";
2331
+ }
2332
+ function isZoomableType(type) {
2333
+ return type === "line" || type === "area" || type === "vbar";
2334
+ }
2335
+ function isStreamableType(type) {
2336
+ return type === "line" || type === "area" || type === "vbar";
2337
+ }
2338
+ function downsampleView(view, type) {
2339
+ const raw = view.downsample;
2340
+ if (raw == null || raw === false) return { view, note: null };
2341
+ const threshold = raw === true ? DOWNSAMPLE_DEFAULT_THRESHOLD : Math.floor(raw);
2342
+ if (!Number.isFinite(threshold)) return { view, note: null };
2343
+ if (type === "scatter") {
2344
+ const pts = view.points ?? [];
2345
+ if (pts.length === 0) return { view, note: null };
2346
+ const sampled2 = lttb(pts, threshold, (p) => p.x, (p) => p.y);
2347
+ if (sampled2.length === pts.length) return { view, note: null };
2348
+ return { view: { ...view, points: sampled2 }, note: t("charts.sampled", { p0: pts.length, p1: sampled2.length }) };
2349
+ }
2350
+ const labels = view.labels ?? [];
2351
+ if (labels.length === 0) return { view, note: null };
2352
+ const guide = view.series && view.series.length > 0 ? view.series[0]?.values ?? [] : view.values ?? [];
2353
+ const count = Math.min(labels.length, guide.length);
2354
+ if (count === 0) return { view, note: null };
2355
+ const indexed = guide.slice(0, count).map((v, i) => ({ i, v }));
2356
+ const sampled = lttb(indexed, threshold, (p) => p.i, (p) => p.v);
2357
+ if (sampled.length >= count) return { view, note: null };
2358
+ const idx = sampled.map((p) => p.i);
2359
+ return {
2360
+ view: {
2361
+ ...view,
2362
+ labels: idx.map((i) => labels[i] ?? ""),
2363
+ values: view.values ? idx.map((i) => view.values?.[i] ?? 0) : void 0,
2364
+ series: view.series?.map((s) => ({ ...s, values: idx.map((i) => s.values[i] ?? 0) }))
2365
+ },
2366
+ note: t("charts.sampled", { p0: count, p1: idx.length })
2367
+ };
2368
+ }
2369
+ function renderChartSkeleton(body) {
2370
+ const box = document.createElement("div");
2371
+ box.className = "icen-chart-skeleton";
2372
+ box.setAttribute("role", "status");
2373
+ box.setAttribute("aria-busy", "true");
2374
+ box.setAttribute("aria-label", t("charts.loading"));
2375
+ const widths = [100, 84, 92, 68, 76, 52];
2376
+ for (const w of widths) {
2377
+ const bar = document.createElement("div");
2378
+ bar.className = "icen-chart-skeleton-bar";
2379
+ bar.style.width = `${w}%`;
2380
+ box.appendChild(bar);
2381
+ }
2382
+ body.appendChild(box);
2383
+ }
2384
+ var CHART_MARK_ACCENT = "var(--icen-token-accent)";
2385
+ function markToneVar(tone) {
2386
+ switch (tone) {
2387
+ case "success":
2388
+ return "var(--icen-token-success)";
2389
+ case "warning":
2390
+ return "var(--icen-token-warning)";
2391
+ case "error":
2392
+ return "var(--icen-token-error)";
2393
+ case "info":
2394
+ return "var(--icen-token-info)";
2395
+ default:
2396
+ return "var(--icen-token-text-faint)";
2397
+ }
2398
+ }
2399
+ function markWarnOnce(warned, mark, reason) {
2400
+ const key = JSON.stringify(mark);
2401
+ if (warned.has(key)) return;
2402
+ warned.add(key);
2403
+ console.warn(`[icen:chart] \u65E0\u6548\u6807\u8BB0\u5DF2\u8DF3\u8FC7\uFF08${reason}\uFF09`, mark);
2404
+ }
2405
+ function paintChartMarks(root, view, spec, formatValue, warned) {
2406
+ const marks = spec.marks;
2407
+ if (!marks || marks.length === 0) return;
2408
+ const type = spec.type;
2409
+ if (type === "line" || type === "area") paintSvgChartMarks(root, view, marks, formatValue, warned);
2410
+ else if (type === "vbar") paintVBarChartMarks(root, view, spec, marks, formatValue, warned);
2411
+ }
2412
+ function paintSvgChartMarks(root, view, marks, formatValue, warned) {
2413
+ const svg = root.querySelector(".icen-chart-line svg");
2414
+ if (!svg) return;
2415
+ const labels = view.labels ?? [];
2416
+ const n = labels.length;
2417
+ if (n === 0) return;
2418
+ const seriesList = view.series && view.series.length > 0 ? view.series : view.values ? [{ name: "", values: view.values }] : [];
2419
+ let max = 1;
2420
+ for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
2421
+ const dims = svgDimsOf(svg);
2422
+ const W = dims.w;
2423
+ const H = dims.h;
2424
+ const PAD = CHART_LINE_PAD;
2425
+ const xAt = (i) => n <= 1 ? W / 2 : PAD + i / (n - 1) * (W - PAD * 2);
2426
+ const yAt = (v) => H - PAD - Math.max(0, v) / max * (H - PAD * 2);
2427
+ for (const mark of marks) {
2428
+ if (mark.type === "line") {
2429
+ const color = markToneVar(mark.tone);
2430
+ if (mark.axis === "y") {
2431
+ const v = typeof mark.value === "number" ? mark.value : Number(mark.value);
2432
+ if (!Number.isFinite(v)) {
2433
+ markWarnOnce(warned, mark, "\u53C2\u8003\u7EBF\u6570\u503C NaN");
2434
+ continue;
2435
+ }
2436
+ const y = clampNum(yAt(v), CHART_LINE_PAD, H - PAD);
2437
+ svg.appendChild(svgEl("line", {
2438
+ class: "icen-chart-mark-line",
2439
+ x1: String(PAD),
2440
+ x2: String(W - PAD),
2441
+ y1: y.toFixed(1),
2442
+ y2: y.toFixed(1),
2443
+ stroke: color,
2444
+ "stroke-width": "1",
2445
+ "stroke-dasharray": "4 3"
2446
+ }));
2447
+ const label = svgEl("text", {
2448
+ class: "icen-chart-mark-label",
2449
+ x: String(W - PAD),
2450
+ y: String(clampNum(y - 4, 8, CHART_LINE_H - 4)),
2451
+ "text-anchor": "end",
2452
+ "font-size": "8",
2453
+ fill: color
2454
+ });
2455
+ label.textContent = mark.label ?? formatValue(v);
2456
+ svg.appendChild(label);
2457
+ } else {
2458
+ const idx = typeof mark.value === "number" ? Math.round(mark.value) : labels.indexOf(mark.value);
2459
+ if (idx < 0 || idx >= n) {
2460
+ markWarnOnce(warned, mark, "\u53C2\u8003\u7EBF\u7C7B\u76EE\u4E0D\u5B58\u5728");
2461
+ continue;
2462
+ }
2463
+ const x = xAt(idx);
2464
+ svg.appendChild(svgEl("line", {
2465
+ class: "icen-chart-mark-line",
2466
+ x1: x.toFixed(1),
2467
+ x2: x.toFixed(1),
2468
+ y1: String(PAD),
2469
+ y2: String(H - PAD),
2470
+ stroke: color,
2471
+ "stroke-width": "1",
2472
+ "stroke-dasharray": "4 3"
2473
+ }));
2474
+ const label = svgEl("text", {
2475
+ class: "icen-chart-mark-label",
2476
+ x: String(clampNum(x, 10, W - 10)),
2477
+ y: String(PAD + 8),
2478
+ "text-anchor": "middle",
2479
+ "font-size": "8",
2480
+ fill: color
2481
+ });
2482
+ label.textContent = mark.label ?? labels[idx] ?? "";
2483
+ svg.appendChild(label);
2484
+ }
2485
+ } else {
2486
+ const idx = labels.indexOf(mark.label);
2487
+ if (idx < 0 || idx >= n) {
2488
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u7C7B\u76EE\u4E0D\u5B58\u5728");
2489
+ continue;
2490
+ }
2491
+ const si = Math.max(0, Math.floor(mark.seriesIndex ?? 0));
2492
+ const s = seriesList[si];
2493
+ if (!s) {
2494
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u7CFB\u5217\u4E0D\u5B58\u5728");
2495
+ continue;
2496
+ }
2497
+ const v = mark.value ?? s.values[idx] ?? NaN;
2498
+ if (!Number.isFinite(v)) {
2499
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u6570\u503C NaN");
2500
+ continue;
2501
+ }
2502
+ const x = xAt(idx);
2503
+ const y = clampNum(yAt(v), CHART_LINE_PAD, H - PAD);
2504
+ const g = svgEl("g", { class: "icen-chart-mark-point" });
2505
+ g.appendChild(svgEl("circle", {
2506
+ cx: x.toFixed(1),
2507
+ cy: y.toFixed(1),
2508
+ r: "6",
2509
+ fill: "none",
2510
+ stroke: CHART_MARK_ACCENT,
2511
+ "stroke-width": "1.5"
2512
+ }));
2513
+ g.appendChild(svgEl("circle", { cx: x.toFixed(1), cy: y.toFixed(1), r: "2", fill: CHART_MARK_ACCENT }));
2514
+ const label = svgEl("text", {
2515
+ class: "icen-chart-mark-label",
2516
+ x: String(clampNum(x, 12, W - 12)),
2517
+ y: String(clampNum(y - 10, 8, CHART_LINE_H - 4)),
2518
+ "text-anchor": "middle",
2519
+ "font-size": "8",
2520
+ fill: CHART_MARK_ACCENT
2521
+ });
2522
+ label.textContent = mark.text ?? mark.label;
2523
+ g.appendChild(label);
2524
+ svg.appendChild(g);
2525
+ }
2526
+ }
2527
+ }
2528
+ function paintVBarChartMarks(root, view, spec, marks, formatValue, warned) {
2529
+ const wrap = root.querySelector(".icen-chart-vbar");
2530
+ if (!wrap) return;
2531
+ const seriesList = view.series && view.series.length > 0 ? view.series : view.values ? [{ name: "", values: view.values }] : [];
2532
+ const labels = view.labels ?? [];
2533
+ const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
2534
+ if (n === 0 || seriesList.length === 0) return;
2535
+ const multi = seriesList.length > 1;
2536
+ let max = 1;
2537
+ if (spec.stacked && multi) {
2538
+ for (let i = 0; i < n; i++) {
2539
+ let sum = 0;
2540
+ for (const s of seriesList) sum += Math.max(0, s.values[i] ?? 0);
2541
+ if (sum > max) max = sum;
2542
+ }
2543
+ } else {
2544
+ for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
2545
+ }
2546
+ const centerPct = (i) => (i + 0.5) / n * 100;
2547
+ for (const mark of marks) {
2548
+ if (mark.type === "line") {
2549
+ if (mark.axis === "y") {
2550
+ const v = typeof mark.value === "number" ? mark.value : Number(mark.value);
2551
+ if (!Number.isFinite(v)) {
2552
+ markWarnOnce(warned, mark, "\u53C2\u8003\u7EBF\u6570\u503C NaN");
2553
+ continue;
2554
+ }
2555
+ const line = document.createElement("div");
2556
+ line.className = "icen-chart-mark-line icen-chart-mark-line--h";
2557
+ line.style.top = `${(clampNum(1 - Math.max(0, v) / max, 0.02, 0.98) * 100).toFixed(2)}%`;
2558
+ line.style.setProperty("--chart-mark-tone", markToneVar(mark.tone));
2559
+ const lab = document.createElement("span");
2560
+ lab.className = "icen-chart-mark-label";
2561
+ lab.textContent = mark.label ?? formatValue(v);
2562
+ line.appendChild(lab);
2563
+ wrap.appendChild(line);
2564
+ } else {
2565
+ const idx = typeof mark.value === "number" ? Math.round(mark.value) : labels.indexOf(mark.value);
2566
+ if (idx < 0 || idx >= n) {
2567
+ markWarnOnce(warned, mark, "\u53C2\u8003\u7EBF\u7C7B\u76EE\u4E0D\u5B58\u5728");
2568
+ continue;
2569
+ }
2570
+ const line = document.createElement("div");
2571
+ line.className = "icen-chart-mark-line icen-chart-mark-line--v";
2572
+ line.style.left = `${centerPct(idx).toFixed(2)}%`;
2573
+ line.style.setProperty("--chart-mark-tone", markToneVar(mark.tone));
2574
+ const lab = document.createElement("span");
2575
+ lab.className = "icen-chart-mark-label";
2576
+ lab.textContent = mark.label ?? labels[idx] ?? "";
2577
+ line.appendChild(lab);
2578
+ wrap.appendChild(line);
2579
+ }
2580
+ } else {
2581
+ const idx = labels.indexOf(mark.label);
2582
+ if (idx < 0 || idx >= n) {
2583
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u7C7B\u76EE\u4E0D\u5B58\u5728");
2584
+ continue;
2585
+ }
2586
+ const si = Math.max(0, Math.floor(mark.seriesIndex ?? 0));
2587
+ const s = seriesList[si];
2588
+ if (!s) {
2589
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u7CFB\u5217\u4E0D\u5B58\u5728");
2590
+ continue;
2591
+ }
2592
+ const v = mark.value ?? s.values[idx] ?? NaN;
2593
+ if (!Number.isFinite(v)) {
2594
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u6570\u503C NaN");
2595
+ continue;
2596
+ }
2597
+ const dot = document.createElement("div");
2598
+ dot.className = "icen-chart-mark-point";
2599
+ dot.style.left = `${centerPct(idx).toFixed(2)}%`;
2600
+ dot.style.bottom = `${(clampNum(Math.max(0, v) / max, 0, 1) * 100).toFixed(2)}%`;
2601
+ const lab = document.createElement("span");
2602
+ lab.className = "icen-chart-mark-label";
2603
+ lab.textContent = mark.text ?? mark.label;
2604
+ dot.appendChild(lab);
2605
+ wrap.appendChild(dot);
2606
+ }
2607
+ }
2608
+ }
2609
+ function chartAriaLabel(title, typeName) {
2610
+ return title ? `${typeName}\uFF1A${title}` : typeName;
2611
+ }
2612
+ function svgDimsOf(svg) {
2613
+ const vb = (svg.getAttribute("viewBox") ?? "").split(/\s+/).map(Number);
2614
+ const w = Number.isFinite(vb[2]) && (vb[2] ?? 0) > 0 ? vb[2] : CHART_LINE_W;
2615
+ const h = Number.isFinite(vb[3]) && (vb[3] ?? 0) > 0 ? vb[3] : CHART_LINE_H;
2616
+ return { w, h };
2617
+ }
2618
+ var fluidObservers = /* @__PURE__ */ new WeakMap();
2619
+ function fluidWidth(host) {
2620
+ const w = Math.round(host.getBoundingClientRect().width);
2621
+ return w > 0 ? Math.max(29, w) : 360;
2622
+ }
2623
+ function observeFluidWidth(el, rerender) {
2624
+ if (typeof ResizeObserver === "undefined") return;
2625
+ fluidObservers.get(el)?.disconnect();
2626
+ let last = Math.round(el.getBoundingClientRect().width);
2627
+ const ro = new ResizeObserver(() => {
2628
+ if (!el.isConnected) {
2629
+ ro.disconnect();
2630
+ fluidObservers.delete(el);
2631
+ return;
2632
+ }
2633
+ const w = Math.round(el.getBoundingClientRect().width);
2634
+ if (Math.abs(w - last) < 2) return;
2635
+ last = w;
2636
+ rerender();
2637
+ });
2638
+ ro.observe(el);
2639
+ fluidObservers.set(el, ro);
2640
+ }
2641
+ function releaseFluidWidth(el) {
2642
+ fluidObservers.get(el)?.disconnect();
2643
+ fluidObservers.delete(el);
2644
+ }
2645
+ function createChartToDataURL(root) {
2646
+ const cache = /* @__PURE__ */ new Map();
2647
+ return (opts) => {
2648
+ if (typeof document === "undefined") return void 0;
2649
+ const scale = clampNum(toNum(opts?.scale) ?? 2, 0.5, 4);
2650
+ const svgs = Array.from(root.querySelectorAll("svg"));
2651
+ if (svgs.length === 0) return void 0;
2652
+ const parts = [];
2653
+ for (const svg of svgs) {
2654
+ const { w, h } = svgDimsOf(svg);
2655
+ const clone = svg.cloneNode(true);
2656
+ clone.setAttribute("xmlns", SVG_NS);
2657
+ clone.setAttribute("width", String(w));
2658
+ clone.setAttribute("height", String(h));
2659
+ let str;
2660
+ try {
2661
+ str = new XMLSerializer().serializeToString(clone);
2662
+ } catch {
2663
+ return void 0;
2664
+ }
2665
+ parts.push({ src: `data:image/svg+xml;charset=utf-8,${encodeURIComponent(str)}`, w, h });
2666
+ }
2667
+ const key = `${scale}|${parts.map((p) => p.src).join("")}`;
2668
+ const hit = cache.get(key);
2669
+ if (hit) return hit;
2670
+ const canvas = document.createElement("canvas");
2671
+ let ctx = null;
2672
+ try {
2673
+ ctx = canvas.getContext("2d");
2674
+ } catch {
2675
+ return void 0;
2676
+ }
2677
+ if (!ctx) return void 0;
2678
+ const context = ctx;
2679
+ const totalW = Math.max(...parts.map((p) => p.w));
2680
+ const totalH = parts.reduce((sum, p) => sum + p.h, 0);
2681
+ canvas.width = Math.max(1, Math.round(totalW * scale));
2682
+ canvas.height = Math.max(1, Math.round(totalH * scale));
2683
+ let loaded = 0;
2684
+ let failed = false;
2685
+ let offsetY = 0;
2686
+ for (const part of parts) {
2687
+ const dy = offsetY;
2688
+ offsetY += part.h;
2689
+ const img = new Image();
2690
+ img.onload = () => {
2691
+ if (failed) return;
2692
+ context.drawImage(img, 0, dy * scale, part.w * scale, part.h * scale);
2693
+ loaded += 1;
2694
+ if (loaded === parts.length) {
2695
+ try {
2696
+ cache.set(key, canvas.toDataURL("image/png"));
2697
+ } catch {
2698
+ }
2699
+ }
2700
+ };
2701
+ img.onerror = () => {
2702
+ failed = true;
2703
+ };
2704
+ img.src = part.src;
2705
+ }
2706
+ return cache.get(key);
2707
+ };
2708
+ }
1280
2709
  function dispatchChartEvent(root, type, mark, chart, phase, pointer) {
1281
2710
  const read = (name) => mark.getAttribute(name) ?? void 0;
1282
2711
  const numOr = (v) => v == null ? void 0 : Number(v);
@@ -1284,11 +2713,11 @@ function dispatchChartEvent(root, type, mark, chart, phase, pointer) {
1284
2713
  type,
1285
2714
  phase,
1286
2715
  chart,
1287
- index: numOr(read("data-chart-index")),
1288
- seriesIndex: numOr(read("data-chart-series")),
1289
- seriesName: read("data-chart-series-name"),
1290
- label: read("data-chart-label"),
1291
- value: numOr(read("data-chart-value")),
2716
+ index: numOr(read("data-icen-chart-index")),
2717
+ seriesIndex: numOr(read("data-icen-chart-series")),
2718
+ seriesName: read("data-icen-chart-series-name"),
2719
+ label: read("data-icen-chart-label"),
2720
+ value: numOr(read("data-icen-chart-value")),
1292
2721
  target: mark,
1293
2722
  pointerX: pointer?.x,
1294
2723
  pointerY: pointer?.y
@@ -1299,7 +2728,7 @@ var tooltipEl = null;
1299
2728
  function chartTooltip() {
1300
2729
  if (!tooltipEl) {
1301
2730
  tooltipEl = document.createElement("div");
1302
- tooltipEl.className = "chart-tooltip";
2731
+ tooltipEl.className = "icen-chart-tooltip";
1303
2732
  tooltipEl.setAttribute("role", "tooltip");
1304
2733
  tooltipEl.hidden = true;
1305
2734
  document.body.appendChild(tooltipEl);
@@ -1308,14 +2737,14 @@ function chartTooltip() {
1308
2737
  }
1309
2738
  function applyTooltipSizing(host) {
1310
2739
  const tip = chartTooltip();
1311
- const raw = host?.closest(".chart, [data-chart-root]")?.getAttribute("data-panel-max") ?? null;
2740
+ const raw = host?.closest(".icen-chart, [data-icen-chart-root]")?.getAttribute("data-icen-panel-max") ?? null;
1312
2741
  const n = raw != null ? Number(raw) : NaN;
1313
2742
  tip.style.maxWidth = Number.isFinite(n) && n > 0 ? `${n}px` : "";
1314
2743
  }
1315
2744
  function tooltipText(mark) {
1316
- const label = mark.getAttribute("data-chart-label") ?? "";
1317
- const series = mark.getAttribute("data-chart-series-name");
1318
- const value = mark.getAttribute("data-chart-value");
2745
+ const label = mark.getAttribute("data-icen-chart-label") ?? "";
2746
+ const series = mark.getAttribute("data-icen-chart-series-name");
2747
+ const value = mark.getAttribute("data-icen-chart-value");
1319
2748
  const head = series ? label ? `${series} \xB7 ${label}` : series : label;
1320
2749
  return value != null ? `${head}\uFF1A${value}` : head;
1321
2750
  }
@@ -1347,8 +2776,8 @@ function bindChartEvents(root) {
1347
2776
  const typeNow = () => chartDelegateMeta.get(root)?.type() ?? "vbar";
1348
2777
  const tipEnabled = () => chartDelegateMeta.get(root)?.tooltip() ?? true;
1349
2778
  const markOf = (e) => {
1350
- const t = e.target;
1351
- return t instanceof Element ? t.closest("[data-chart-mark]") : null;
2779
+ const t2 = e.target;
2780
+ return t2 instanceof Element ? t2.closest("[data-icen-chart-mark]") : null;
1352
2781
  };
1353
2782
  let hoverMark = null;
1354
2783
  const enterMark = (mark, x, y) => {
@@ -1445,70 +2874,277 @@ function hasLegendOf(spec) {
1445
2874
  function renderChart(el, raw) {
1446
2875
  const noop = () => void 0;
1447
2876
  if (typeof document === "undefined") {
1448
- return { el, spec: {}, update: noop, on: () => noop, destroy: noop };
2877
+ return {
2878
+ el,
2879
+ spec: {},
2880
+ update: noop,
2881
+ append: noop,
2882
+ toDataURL: () => void 0,
2883
+ on: () => noop,
2884
+ destroy: noop
2885
+ };
1449
2886
  }
1450
2887
  const root = el;
1451
- let current = normalizeChartSpec(raw);
1452
- const typeOf = () => current.type ?? "vbar";
2888
+ let current2 = normalizeChartSpec(raw);
2889
+ const typeOf = () => current2.type ?? "vbar";
1453
2890
  const legendState = { hidden: null };
2891
+ let zoomWindow = null;
2892
+ const warnedMarks = /* @__PURE__ */ new Set();
2893
+ let warnedAppend = false;
2894
+ let zoomDragCleanup = null;
2895
+ const fullLength = () => {
2896
+ if (current2.type === "scatter") return current2.points?.length ?? 0;
2897
+ if (current2.series && current2.series.length > 0) {
2898
+ return Math.max(current2.labels?.length ?? 0, ...current2.series.map((s) => s.values.length));
2899
+ }
2900
+ return Math.max(current2.labels?.length ?? 0, current2.values?.length ?? 0);
2901
+ };
2902
+ const viewOf = () => {
2903
+ if (!zoomWindow) return current2;
2904
+ const { from, to } = zoomWindow;
2905
+ return {
2906
+ ...current2,
2907
+ labels: current2.labels?.slice(from, to + 1),
2908
+ values: current2.values?.slice(from, to + 1),
2909
+ series: current2.series?.map((s) => ({ ...s, values: s.values.slice(from, to + 1) })),
2910
+ points: current2.points?.slice(from, to + 1)
2911
+ };
2912
+ };
2913
+ const applyZoomSelection = (x0, x1) => {
2914
+ const len = fullLength();
2915
+ if (len < 2) return;
2916
+ const base = zoomWindow?.from ?? 0;
2917
+ const viewLen = zoomWindow ? zoomWindow.to - zoomWindow.from + 1 : len;
2918
+ const lo = Math.min(x0, x1);
2919
+ const hi = Math.max(x0, x1);
2920
+ let i0;
2921
+ let i1;
2922
+ const vbar = root.querySelector(".icen-chart-vbar");
2923
+ if (vbar) {
2924
+ const rect = vbar.getBoundingClientRect();
2925
+ const rel0 = rect.width > 0 ? Math.floor((lo - rect.left) / rect.width * viewLen) : 0;
2926
+ const rel1 = rect.width > 0 ? Math.max(rel0, Math.floor((hi - rect.left) / rect.width * viewLen)) : viewLen - 1;
2927
+ i0 = clampNum(rel0 + base, 0, len - 1);
2928
+ i1 = clampNum(rel1 + base, 0, len - 1);
2929
+ } else {
2930
+ const svg = root.querySelector(".icen-chart-line svg");
2931
+ if (!svg) return;
2932
+ const dims = svgDimsOf(svg);
2933
+ const p0 = svgClientPoint(svg, lo, 0, dims.w, dims.h);
2934
+ const p1 = svgClientPoint(svg, hi, 0, dims.w, dims.h);
2935
+ const rel0 = Math.round((p0.x - CHART_LINE_PAD) / (dims.w - CHART_LINE_PAD * 2) * (viewLen - 1));
2936
+ const rel1 = Math.round((p1.x - CHART_LINE_PAD) / (dims.w - CHART_LINE_PAD * 2) * (viewLen - 1));
2937
+ i0 = clampNum(rel0 + base, 0, len - 1);
2938
+ i1 = clampNum(rel1 + base, 0, len - 1);
2939
+ }
2940
+ if (i0 > i1) [i0, i1] = [i1, i0];
2941
+ if (i0 <= 0 && i1 >= len - 1) zoomWindow = null;
2942
+ else zoomWindow = { from: i0, to: Math.max(i1, i0) };
2943
+ render();
2944
+ emitIcen(root, "icen:chart-zoom", { chart: String(current2.type ?? ""), from: i0, to: Math.max(i1, i0) });
2945
+ };
2946
+ const bindZoomGesture = () => {
2947
+ if (current2.zoom !== "x" && current2.zoom !== "brush") return;
2948
+ const t2 = typeOf();
2949
+ if (!isZoomableType(t2) || fullLength() < 2) return;
2950
+ const plot = t2 === "vbar" ? root.querySelector(".icen-chart-vbar") : root.querySelector(".icen-chart-line svg");
2951
+ if (!plot) return;
2952
+ const on = (type, fn) => {
2953
+ plot.addEventListener(type, fn);
2954
+ };
2955
+ let drag = null;
2956
+ const onMove = (e) => {
2957
+ if (!drag) return;
2958
+ drag.x1 = e.clientX;
2959
+ drag.y1 = e.clientY;
2960
+ if (!drag.active && Math.hypot(drag.x1 - drag.x0, drag.y1 - drag.y0) >= CHART_ZOOM_DRAG_MIN_PX) {
2961
+ drag.active = true;
2962
+ const rect = document.createElement("div");
2963
+ rect.className = "icen-chart-zoom-rect";
2964
+ document.body.appendChild(rect);
2965
+ drag.rect = rect;
2966
+ }
2967
+ if (drag.active && drag.rect) {
2968
+ drag.rect.style.left = `${Math.round(Math.min(drag.x0, drag.x1))}px`;
2969
+ drag.rect.style.width = `${Math.round(Math.abs(drag.x1 - drag.x0))}px`;
2970
+ if (current2.zoom === "brush") {
2971
+ drag.rect.style.top = `${Math.round(Math.min(drag.y0, drag.y1))}px`;
2972
+ drag.rect.style.height = `${Math.round(Math.abs(drag.y1 - drag.y0))}px`;
2973
+ } else {
2974
+ const pr = plot.getBoundingClientRect();
2975
+ drag.rect.style.top = `${Math.round(pr.top)}px`;
2976
+ drag.rect.style.height = `${Math.round(pr.height)}px`;
2977
+ }
2978
+ }
2979
+ };
2980
+ const onUp = () => {
2981
+ window.removeEventListener("pointermove", onMove);
2982
+ window.removeEventListener("pointerup", onUp);
2983
+ window.removeEventListener("pointercancel", onUp);
2984
+ zoomDragCleanup = null;
2985
+ if (!drag) return;
2986
+ const d = drag;
2987
+ drag = null;
2988
+ d.rect?.remove();
2989
+ if (!d.active) return;
2990
+ applyZoomSelection(d.x0, d.x1);
2991
+ };
2992
+ const onDown = (e) => {
2993
+ if (e.button !== 0) return;
2994
+ drag = { x0: e.clientX, y0: e.clientY, x1: e.clientX, y1: e.clientY, active: false, rect: null };
2995
+ window.addEventListener("pointermove", onMove);
2996
+ window.addEventListener("pointerup", onUp);
2997
+ window.addEventListener("pointercancel", onUp);
2998
+ zoomDragCleanup = onUp;
2999
+ };
3000
+ on("pointerdown", onDown);
3001
+ on("dblclick", () => {
3002
+ if (!zoomWindow) return;
3003
+ zoomWindow = null;
3004
+ render();
3005
+ });
3006
+ };
1454
3007
  const render = () => {
1455
- if (!current.type) current = { ...current, type: inferChartType(current) };
1456
- const spec = current;
3008
+ if (!current2.type) current2 = { ...current2, type: inferChartType(current2) };
3009
+ const spec = current2;
3010
+ const type = spec.type;
1457
3011
  const formatValue = chartFormatValue(spec.format);
1458
3012
  if (spec.legend !== void 0) legendState.hidden = spec.legend === false;
3013
+ const loading = spec.loading === true;
3014
+ root.classList.toggle("is-loading", loading);
3015
+ let view = viewOf();
3016
+ let note = null;
3017
+ if (!loading && isDownsampleableType(type)) {
3018
+ const r = downsampleView(view, type);
3019
+ view = r.view;
3020
+ note = r.note;
3021
+ }
3022
+ const zoomReset = zoomWindow && (spec.zoom === "x" || spec.zoom === "brush") ? () => {
3023
+ zoomWindow = null;
3024
+ render();
3025
+ } : null;
1459
3026
  withChrome(root, spec, hasLegendOf(spec), (body) => {
1460
- switch (spec.type) {
3027
+ if (loading) {
3028
+ renderChartSkeleton(body);
3029
+ return;
3030
+ }
3031
+ switch (type) {
1461
3032
  case "line":
1462
- renderLine(body, { labels: spec.labels ?? [], values: spec.values ?? [], series: spec.series, tone: spec.tone, fill: spec.fill, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3033
+ renderLine(body, { labels: view.labels ?? [], values: view.values ?? [], series: view.series, tone: view.tone, fill: view.fill, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1463
3034
  break;
1464
3035
  case "area":
1465
- renderLine(body, { labels: spec.labels ?? [], values: spec.values ?? [], series: spec.series, tone: spec.tone, fill: true, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3036
+ renderLine(body, { labels: view.labels ?? [], values: view.values ?? [], series: view.series, tone: view.tone, fill: true, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1466
3037
  break;
1467
3038
  case "vbar":
1468
- renderVBar(body, { labels: spec.labels ?? [], values: spec.values ?? [], series: spec.series, stacked: spec.stacked, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3039
+ renderVBar(body, { labels: view.labels ?? [], values: view.values ?? [], series: view.series, stacked: view.stacked, tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1469
3040
  break;
1470
3041
  case "hbar":
1471
- renderHBar(body, { labels: spec.labels ?? [], values: spec.values ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3042
+ renderHBar(body, { labels: view.labels ?? [], values: view.values ?? [], tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1472
3043
  break;
1473
3044
  case "stack":
1474
- renderStack(body, { segments: spec.segments ?? [], formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3045
+ renderStack(body, { segments: view.segments ?? [], formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1475
3046
  break;
1476
3047
  case "donut":
1477
- renderDonut(body, { segments: spec.segments ?? [], formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3048
+ renderDonut(body, { segments: view.segments ?? [], formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1478
3049
  break;
1479
3050
  case "radar": {
1480
- const series = spec.series ? spec.series.map((s) => ({ name: s.name, values: s.values, tone: typeof s.tone === "string" ? s.tone : void 0 })) : spec.values ? [{ name: "", values: spec.values }] : [];
1481
- renderRadar(body, { axes: spec.axes ?? spec.labels ?? [], series, max: spec.max, levels: spec.levels, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3051
+ const series = view.series ? view.series.map((s) => ({ name: s.name, values: s.values, tone: typeof s.tone === "string" ? s.tone : void 0 })) : view.values ? [{ name: "", values: view.values }] : [];
3052
+ renderRadar(body, { axes: view.axes ?? view.labels ?? [], series, max: view.max, levels: view.levels, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1482
3053
  break;
1483
3054
  }
1484
3055
  case "heatmap":
1485
- renderHeatmap(body, { values: spec.values, data: spec.data, weeks: spec.weeks, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend, weekStart: spec.i18n?.weekStart, labels: spec.i18n });
3056
+ renderHeatmap(body, { values: view.values, data: view.data, weeks: view.weeks, tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend, weekStart: view.i18n?.weekStart, labels: view.i18n });
1486
3057
  break;
1487
3058
  case "calendar":
1488
- renderCalendar(body, { dates: spec.dates, values: spec.values, data: spec.data, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend, weekStart: spec.i18n?.weekStart, labels: spec.i18n });
3059
+ renderCalendar(body, { dates: view.dates, values: view.values, data: view.data, tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend, weekStart: view.i18n?.weekStart, labels: view.i18n });
1489
3060
  break;
1490
3061
  case "sparkline":
1491
- renderSparkline(body, { labels: spec.labels ?? [], values: spec.values ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3062
+ renderSparkline(body, { labels: view.labels ?? [], values: view.values ?? [], tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1492
3063
  break;
1493
3064
  case "gauge":
1494
- renderGauge(body, { value: spec.values?.[0] ?? 0, max: spec.max, tone: spec.tone, label: spec.labels?.[0], formatValue });
3065
+ renderGauge(body, { value: view.values?.[0] ?? 0, max: view.max, tone: view.tone, label: view.labels?.[0], formatValue });
1495
3066
  break;
1496
3067
  case "scatter":
1497
- renderScatter(body, { points: spec.points ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3068
+ renderScatter(body, { points: view.points ?? [], tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1498
3069
  break;
1499
3070
  default:
1500
- renderEmpty(body, spec.emptyLabel);
3071
+ renderEmpty(body, view.emptyLabel);
1501
3072
  }
1502
- });
3073
+ }, legendState, { note, onZoomReset: zoomReset });
3074
+ if (!loading) {
3075
+ const graphic = root.querySelector('[role="img"]');
3076
+ if (graphic && !graphic.getAttribute("aria-label")) {
3077
+ graphic.setAttribute("aria-label", spec.title ? `${spec.title}\uFF08${type}\uFF09` : type);
3078
+ }
3079
+ }
3080
+ if (loading) return;
3081
+ paintChartMarks(root, view, spec, formatValue, warnedMarks);
3082
+ bindZoomGesture();
1503
3083
  };
1504
- chartDelegateMeta.set(root, { type: typeOf, tooltip: () => current.tooltip !== false });
3084
+ chartDelegateMeta.set(root, { type: typeOf, tooltip: () => current2.tooltip !== false });
1505
3085
  const unbindEvents = bindChartEvents(root);
1506
3086
  render();
3087
+ observeFluidWidth(root, render);
1507
3088
  return {
1508
3089
  el: root,
1509
- spec: current,
3090
+ /** 归一 + 推断后的当前规格(getter:update/append 换引用后永远读到最新) */
3091
+ get spec() {
3092
+ return current2;
3093
+ },
1510
3094
  update(next) {
1511
- current = normalizeChartSpec(next);
3095
+ const prev = current2;
3096
+ current2 = normalizeChartSpec(next);
3097
+ if (zoomWindow) {
3098
+ const sameLabels = JSON.stringify(prev.labels ?? null) === JSON.stringify(current2.labels ?? null);
3099
+ const t2 = current2.type ?? inferChartType(current2);
3100
+ const zoomable = isZoomableType(t2) && (current2.zoom === "x" || current2.zoom === "brush");
3101
+ if (!sameLabels || !zoomable) zoomWindow = null;
3102
+ else {
3103
+ const len = fullLength();
3104
+ if (len < 2) zoomWindow = null;
3105
+ else {
3106
+ const from = clampNum(zoomWindow.from, 0, len - 1);
3107
+ const to = clampNum(zoomWindow.to, 0, len - 1);
3108
+ zoomWindow = from <= to ? { from, to } : null;
3109
+ }
3110
+ }
3111
+ }
3112
+ render();
3113
+ },
3114
+ append(label, values, opts) {
3115
+ const t2 = current2.type ?? inferChartType(current2);
3116
+ if (!isStreamableType(t2)) {
3117
+ if (!warnedAppend) {
3118
+ warnedAppend = true;
3119
+ console.warn(`[icen:chart] append \u4EC5\u652F\u6301 line/area/vbar\uFF08\u5F53\u524D ${String(current2.type ?? t2)}\uFF09\uFF0C\u5DF2\u5FFD\u7565`);
3120
+ }
3121
+ return;
3122
+ }
3123
+ const labels = current2.labels ?? (current2.labels = []);
3124
+ labels.push(String(label ?? ""));
3125
+ const vals = Array.isArray(values) ? values : [];
3126
+ if (current2.series && current2.series.length > 0) {
3127
+ for (const [i, s] of current2.series.entries()) s.values.push(toNum(vals[i]) ?? 0);
3128
+ } else {
3129
+ const v = current2.values ?? (current2.values = []);
3130
+ v.push(toNum(vals[0]) ?? 0);
3131
+ }
3132
+ const maxPoints = opts?.maxPoints != null ? Math.max(1, Math.floor(toNum(opts.maxPoints) ?? 0)) : 0;
3133
+ let dropped = 0;
3134
+ if (maxPoints > 0) {
3135
+ while (labels.length > maxPoints) {
3136
+ labels.shift();
3137
+ if (current2.series) for (const s of current2.series) s.values.shift();
3138
+ else current2.values?.shift();
3139
+ dropped += 1;
3140
+ }
3141
+ }
3142
+ if (zoomWindow) {
3143
+ const len = Math.max(1, labels.length);
3144
+ const size = zoomWindow.to - zoomWindow.from + 1;
3145
+ const to = clampNum(zoomWindow.to - dropped + 1, 0, len - 1);
3146
+ zoomWindow = { from: Math.max(0, to - size + 1), to };
3147
+ }
1512
3148
  render();
1513
3149
  },
1514
3150
  on(name, listener) {
@@ -1518,17 +3154,819 @@ function renderChart(el, raw) {
1518
3154
  root.addEventListener(`icen:chart-${name}`, handler);
1519
3155
  return () => root.removeEventListener(`icen:chart-${name}`, handler);
1520
3156
  },
3157
+ toDataURL: createChartToDataURL(root),
1521
3158
  destroy() {
3159
+ releaseFluidWidth(root);
3160
+ hiddenLegendKeys.delete(root);
3161
+ zoomDragCleanup?.();
1522
3162
  root.textContent = "";
3163
+ root.classList.remove("is-loading");
1523
3164
  unbindEvents();
1524
3165
  chartTooltip().hidden = true;
1525
3166
  }
1526
3167
  };
1527
3168
  }
3169
+ var GRAPH_CHART_TYPE = "graph";
3170
+ var MAP_CHART_TYPE = "map";
3171
+ function buildLayoutState(nodes, edges, width, height) {
3172
+ const ids = [];
3173
+ const index = /* @__PURE__ */ new Map();
3174
+ for (const node of nodes) {
3175
+ const id = String(node?.id ?? "").trim();
3176
+ if (!id || index.has(id)) continue;
3177
+ index.set(id, ids.length);
3178
+ ids.push(id);
3179
+ }
3180
+ const pairs = [];
3181
+ for (const edge of edges) {
3182
+ const a = index.get(String(edge?.source ?? "").trim());
3183
+ const b = index.get(String(edge?.target ?? "").trim());
3184
+ if (a == null || b == null || a === b) continue;
3185
+ pairs.push([a, b]);
3186
+ }
3187
+ const n = ids.length;
3188
+ return {
3189
+ ids,
3190
+ index,
3191
+ x: new Float64Array(n),
3192
+ y: new Float64Array(n),
3193
+ pairs,
3194
+ width,
3195
+ height,
3196
+ k: n > 0 ? Math.sqrt(width * height / n) : 1
3197
+ };
3198
+ }
3199
+ function relaxLayout(state, rounds, pinned = -1, tempScale = 1) {
3200
+ const { x, y, pairs, width, height, k } = state;
3201
+ const n = x.length;
3202
+ if (n <= 1 || rounds <= 0) return;
3203
+ const cx = width / 2;
3204
+ const cy = height / 2;
3205
+ const gravity = 0.015;
3206
+ const tempStart = Math.min(width, height) / 6 * tempScale;
3207
+ const dx = new Float64Array(n);
3208
+ const dy = new Float64Array(n);
3209
+ for (let round = 0; round < rounds; round++) {
3210
+ dx.fill(0);
3211
+ dy.fill(0);
3212
+ const temp = Math.max(0.5, tempStart * (1 - round / rounds));
3213
+ for (let i = 0; i < n; i++) {
3214
+ for (let j = i + 1; j < n; j++) {
3215
+ let vx = x[j] - x[i];
3216
+ let vy = y[j] - y[i];
3217
+ let d2 = vx * vx + vy * vy;
3218
+ if (d2 < 0.01) {
3219
+ vx = 0.5 + (j - i) % 7 * 0.25;
3220
+ vy = (i * 3 + j) % 5 * 0.3 - 0.6;
3221
+ d2 = vx * vx + vy * vy;
3222
+ }
3223
+ const d = Math.sqrt(d2);
3224
+ const f = k * k / d;
3225
+ const ux = vx / d * f;
3226
+ const uy = vy / d * f;
3227
+ dx[i] -= ux;
3228
+ dy[i] -= uy;
3229
+ dx[j] += ux;
3230
+ dy[j] += uy;
3231
+ }
3232
+ }
3233
+ for (const [a, b] of pairs) {
3234
+ let vx = x[b] - x[a];
3235
+ let vy = y[b] - y[a];
3236
+ let d = Math.sqrt(vx * vx + vy * vy);
3237
+ if (d < 0.01) {
3238
+ vx = 1;
3239
+ vy = 0;
3240
+ d = 1;
3241
+ }
3242
+ const f = Math.min(d * d / k, k * 3);
3243
+ const ux = vx / d * f;
3244
+ const uy = vy / d * f;
3245
+ dx[a] += ux;
3246
+ dy[a] += uy;
3247
+ dx[b] -= ux;
3248
+ dy[b] -= uy;
3249
+ }
3250
+ for (let i = 0; i < n; i++) {
3251
+ if (i === pinned) continue;
3252
+ dx[i] += (cx - x[i]) * gravity;
3253
+ dy[i] += (cy - y[i]) * gravity;
3254
+ const dm = Math.sqrt(dx[i] * dx[i] + dy[i] * dy[i]);
3255
+ if (dm <= 0) continue;
3256
+ const v = Math.min(dm, temp) / dm;
3257
+ x[i] = Math.min(width, Math.max(0, x[i] + dx[i] * v));
3258
+ y[i] = Math.min(height, Math.max(0, y[i] + dy[i] * v));
3259
+ }
3260
+ }
3261
+ }
3262
+ function fitLayout(state, margin) {
3263
+ const { x, y, width, height } = state;
3264
+ const n = x.length;
3265
+ if (n === 0) return;
3266
+ let minX = Infinity;
3267
+ let maxX = -Infinity;
3268
+ let minY = Infinity;
3269
+ let maxY = -Infinity;
3270
+ for (let i = 0; i < n; i++) {
3271
+ if (x[i] < minX) minX = x[i];
3272
+ if (x[i] > maxX) maxX = x[i];
3273
+ if (y[i] < minY) minY = y[i];
3274
+ if (y[i] > maxY) maxY = y[i];
3275
+ }
3276
+ const spanX = Math.max(1e-6, maxX - minX);
3277
+ const spanY = Math.max(1e-6, maxY - minY);
3278
+ const scale = Math.min((width - margin * 2) / spanX, (height - margin * 2) / spanY, 4);
3279
+ const offX = (width - spanX * scale) / 2 - minX * scale;
3280
+ const offY = (height - spanY * scale) / 2 - minY * scale;
3281
+ for (let i = 0; i < n; i++) {
3282
+ x[i] = x[i] * scale + offX;
3283
+ y[i] = y[i] * scale + offY;
3284
+ }
3285
+ }
3286
+ function initRingLayout(state, seed = 0) {
3287
+ const { ids, index, x, y, width, height } = state;
3288
+ const n = ids.length;
3289
+ if (n === 0) return;
3290
+ if (n === 1) {
3291
+ x[0] = width / 2;
3292
+ y[0] = height / 2;
3293
+ return;
3294
+ }
3295
+ const order = [...ids].sort();
3296
+ const radius = Math.min(width, height) / 2 * 0.76;
3297
+ const deg = (seed % 360 + 360) % 360;
3298
+ const seedAngle = deg * Math.PI / 180;
3299
+ for (let rank = 0; rank < n; rank++) {
3300
+ const i = index.get(order[rank] ?? "") ?? -1;
3301
+ if (i < 0) continue;
3302
+ const angle = seedAngle - Math.PI / 2 + rank * 2 * Math.PI / n;
3303
+ x[i] = width / 2 + radius * Math.cos(angle);
3304
+ y[i] = height / 2 + radius * Math.sin(angle);
3305
+ }
3306
+ }
3307
+ function layoutGraph(nodes, edges, opts) {
3308
+ const width = Math.max(16, opts?.width ?? 320);
3309
+ const height = Math.max(16, opts?.height ?? 200);
3310
+ const state = buildLayoutState(nodes, edges, width, height);
3311
+ if (state.ids.length === 0) return /* @__PURE__ */ new Map();
3312
+ initRingLayout(state, opts?.seed ?? 0);
3313
+ relaxLayout(state, Math.max(1, Math.round(opts?.iterations ?? 300)));
3314
+ fitLayout(state, Math.max(8, Math.min(width, height) * 0.08));
3315
+ const out = /* @__PURE__ */ new Map();
3316
+ state.ids.forEach((id, i) => out.set(id, { x: state.x[i], y: state.y[i] }));
3317
+ return out;
3318
+ }
3319
+ var datumMarkInfo = /* @__PURE__ */ new WeakMap();
3320
+ var datumEventControl = /* @__PURE__ */ new WeakMap();
3321
+ function dispatchDatumEvent(root, type, mark, phase, pointer) {
3322
+ const numOr = (v) => v == null ? void 0 : Number(v);
3323
+ const detail = {
3324
+ type,
3325
+ phase,
3326
+ chart: chartDelegateMeta.get(root)?.type() ?? "vbar",
3327
+ index: numOr(mark.getAttribute("data-icen-chart-index")),
3328
+ seriesIndex: numOr(mark.getAttribute("data-icen-chart-series")),
3329
+ seriesName: mark.getAttribute("data-icen-chart-series-name") ?? void 0,
3330
+ label: mark.getAttribute("data-icen-chart-label") ?? void 0,
3331
+ value: numOr(mark.getAttribute("data-icen-chart-value")),
3332
+ target: mark,
3333
+ pointerX: pointer?.x,
3334
+ pointerY: pointer?.y,
3335
+ datum: datumMarkInfo.get(mark)?.datum
3336
+ };
3337
+ emitIcen(root, `icen:chart-${type}`, detail);
3338
+ }
3339
+ function bindDatumEvents(root) {
3340
+ const existing = datumEventControl.get(root);
3341
+ if (existing) return existing;
3342
+ const tipEnabled = () => chartDelegateMeta.get(root)?.tooltip() ?? true;
3343
+ const markOf = (e) => {
3344
+ const t2 = e.target;
3345
+ return t2 instanceof Element ? t2.closest("[data-icen-chart-mark]") : null;
3346
+ };
3347
+ let hoverMark = null;
3348
+ let suppressClick = false;
3349
+ const bindings = [];
3350
+ const on = (type, fn) => {
3351
+ const listener = (evt) => fn(evt);
3352
+ root.addEventListener(type, listener);
3353
+ bindings.push([type, listener]);
3354
+ };
3355
+ const enterMark = (mark, px, py) => {
3356
+ hoverMark = mark;
3357
+ mark.classList.add("is-hover");
3358
+ dispatchDatumEvent(root, "hover", mark, "enter", { x: px, y: py });
3359
+ if (tipEnabled()) {
3360
+ const tip = chartTooltip();
3361
+ tip.textContent = datumMarkInfo.get(mark)?.tip ?? tooltipText(mark);
3362
+ applyTooltipSizing(mark);
3363
+ tip.hidden = false;
3364
+ moveTooltip(px, py);
3365
+ }
3366
+ };
3367
+ const leaveMark = (pointer) => {
3368
+ if (!hoverMark) return;
3369
+ hoverMark.classList.remove("is-hover");
3370
+ dispatchDatumEvent(root, "hover", hoverMark, "leave", pointer);
3371
+ hoverMark = null;
3372
+ chartTooltip().hidden = true;
3373
+ };
3374
+ on("pointerover", (e) => {
3375
+ const mark = markOf(e);
3376
+ if (!mark || mark === hoverMark) return;
3377
+ leaveMark({ x: e.clientX, y: e.clientY });
3378
+ enterMark(mark, e.clientX, e.clientY);
3379
+ });
3380
+ on("pointermove", (e) => {
3381
+ if (!hoverMark) return;
3382
+ dispatchDatumEvent(root, "hover", hoverMark, "move", { x: e.clientX, y: e.clientY });
3383
+ if (tipEnabled()) moveTooltip(e.clientX, e.clientY);
3384
+ });
3385
+ on("pointerout", (e) => {
3386
+ const mark = markOf(e);
3387
+ if (mark && mark === hoverMark) leaveMark({ x: e.clientX, y: e.clientY });
3388
+ });
3389
+ on("pointerleave", () => leaveMark());
3390
+ on("click", (e) => {
3391
+ if (suppressClick) {
3392
+ suppressClick = false;
3393
+ return;
3394
+ }
3395
+ const mark = markOf(e);
3396
+ if (mark) dispatchDatumEvent(root, "click", mark, void 0, { x: e.clientX, y: e.clientY });
3397
+ });
3398
+ on("dblclick", (e) => {
3399
+ const mark = markOf(e);
3400
+ if (mark) dispatchDatumEvent(root, "dblclick", mark, void 0, { x: e.clientX, y: e.clientY });
3401
+ });
3402
+ on("contextmenu", (e) => {
3403
+ const mark = markOf(e);
3404
+ if (!mark) return;
3405
+ e.preventDefault();
3406
+ dispatchDatumEvent(root, "contextmenu", mark, void 0, { x: e.clientX, y: e.clientY });
3407
+ });
3408
+ const control = {
3409
+ unbind() {
3410
+ for (const [type, listener] of bindings) root.removeEventListener(type, listener);
3411
+ bindings.length = 0;
3412
+ if (hoverMark) {
3413
+ hoverMark.classList.remove("is-hover");
3414
+ hoverMark = null;
3415
+ }
3416
+ chartTooltip().hidden = true;
3417
+ datumEventControl.delete(root);
3418
+ },
3419
+ suppressNextClick() {
3420
+ suppressClick = true;
3421
+ }
3422
+ };
3423
+ datumEventControl.set(root, control);
3424
+ return control;
3425
+ }
3426
+ function buildClusterModel(keys, labels) {
3427
+ const seen = /* @__PURE__ */ new Set();
3428
+ for (const key of keys) {
3429
+ if (typeof key === "string" && key !== "") seen.add(key);
3430
+ }
3431
+ if (seen.size === 0) return null;
3432
+ const list = [...seen].sort();
3433
+ return {
3434
+ keys: list,
3435
+ indexOf: new Map(list.map((k, i) => [k, i])),
3436
+ label: (key) => labels?.[key] ?? key,
3437
+ color: (index) => paletteVar(index + 1)
3438
+ };
3439
+ }
3440
+ function registerDatumMark(mark, datum, tip) {
3441
+ datumMarkInfo.set(mark, { datum, tip });
3442
+ }
3443
+ function truncateText(text, max = 8) {
3444
+ const chars = Array.from(text);
3445
+ return chars.length > max ? `${chars.slice(0, max).join("")}\u2026` : text;
3446
+ }
3447
+ function svgClientPoint(svg, clientX, clientY, viewBoxWidth, viewBoxHeight) {
3448
+ const rect = svg.getBoundingClientRect();
3449
+ const scale = Math.min(rect.width / viewBoxWidth, rect.height / viewBoxHeight);
3450
+ if (!Number.isFinite(scale) || scale <= 0) {
3451
+ return { x: viewBoxWidth / 2, y: viewBoxHeight / 2 };
3452
+ }
3453
+ return {
3454
+ x: (clientX - rect.left - (rect.width - viewBoxWidth * scale) / 2) / scale,
3455
+ y: (clientY - rect.top - (rect.height - viewBoxHeight * scale) / 2) / scale
3456
+ };
3457
+ }
3458
+ function normalizeGraphSpec(raw) {
3459
+ let src = raw;
3460
+ if (typeof src === "string") {
3461
+ try {
3462
+ src = JSON.parse(src);
3463
+ } catch {
3464
+ src = {};
3465
+ }
3466
+ }
3467
+ if (!src || typeof src !== "object" || Array.isArray(src)) src = {};
3468
+ const s = src;
3469
+ const spec = { ...normalizeChartSpec(s), type: "graph", nodes: [], edges: [] };
3470
+ if (Array.isArray(s.nodes)) {
3471
+ const seen = /* @__PURE__ */ new Set();
3472
+ for (const v of s.nodes) {
3473
+ if (!v || typeof v !== "object" || Array.isArray(v)) continue;
3474
+ const r = v;
3475
+ const id = String(r.id ?? "").trim();
3476
+ if (!id || seen.has(id)) continue;
3477
+ seen.add(id);
3478
+ const node = { id };
3479
+ if (r.label != null) node.label = String(r.label);
3480
+ if (typeof r.cluster === "string" && r.cluster !== "") node.cluster = r.cluster;
3481
+ const weight = toNum(r.weight);
3482
+ if (weight != null) node.weight = weight;
3483
+ if (r.meta && typeof r.meta === "object" && !Array.isArray(r.meta)) {
3484
+ node.meta = { ...r.meta };
3485
+ }
3486
+ spec.nodes.push(node);
3487
+ }
3488
+ }
3489
+ const known = new Set(spec.nodes.map((n) => n.id));
3490
+ if (Array.isArray(s.edges)) {
3491
+ for (const v of s.edges) {
3492
+ if (!v || typeof v !== "object" || Array.isArray(v)) continue;
3493
+ const r = v;
3494
+ const source = String(r.source ?? "").trim();
3495
+ const target = String(r.target ?? "").trim();
3496
+ if (!known.has(source) || !known.has(target) || source === target) continue;
3497
+ const edge = { source, target };
3498
+ const weight = toNum(r.weight);
3499
+ if (weight != null) edge.weight = weight;
3500
+ spec.edges.push(edge);
3501
+ }
3502
+ }
3503
+ if (s.directed === true) spec.directed = true;
3504
+ if (s.clusterLabels && typeof s.clusterLabels === "object" && !Array.isArray(s.clusterLabels)) {
3505
+ const labels = {};
3506
+ for (const [key, value] of Object.entries(s.clusterLabels)) {
3507
+ if (typeof value === "string" && value !== "") labels[key] = value;
3508
+ }
3509
+ spec.clusterLabels = labels;
3510
+ }
3511
+ return spec;
3512
+ }
3513
+ function normalizeMapSpec(raw) {
3514
+ let src = raw;
3515
+ if (typeof src === "string") {
3516
+ try {
3517
+ src = JSON.parse(src);
3518
+ } catch {
3519
+ src = {};
3520
+ }
3521
+ }
3522
+ if (!src || typeof src !== "object" || Array.isArray(src)) src = {};
3523
+ const s = src;
3524
+ const spec = { ...normalizeChartSpec(s), type: "map", points: [] };
3525
+ if (Array.isArray(s.points)) {
3526
+ const seen = /* @__PURE__ */ new Set();
3527
+ for (const v of s.points) {
3528
+ if (!v || typeof v !== "object" || Array.isArray(v)) continue;
3529
+ const r = v;
3530
+ const id = String(r.id ?? "").trim();
3531
+ if (!id || seen.has(id)) continue;
3532
+ seen.add(id);
3533
+ const point = { id, x: toNum(r.x) ?? 0, y: toNum(r.y) ?? 0 };
3534
+ if (typeof r.cluster === "string" && r.cluster !== "") point.cluster = r.cluster;
3535
+ if (r.label != null) point.label = String(r.label);
3536
+ if (r.meta && typeof r.meta === "object" && !Array.isArray(r.meta)) {
3537
+ point.meta = { ...r.meta };
3538
+ }
3539
+ spec.points.push(point);
3540
+ }
3541
+ }
3542
+ if (s.clusters && typeof s.clusters === "object" && !Array.isArray(s.clusters)) {
3543
+ const labels = {};
3544
+ for (const [key, value] of Object.entries(s.clusters)) {
3545
+ if (typeof value === "string" && value !== "") labels[key] = value;
3546
+ }
3547
+ spec.clusters = labels;
3548
+ }
3549
+ return spec;
3550
+ }
3551
+ var GRAPH_W = 360;
3552
+ var GRAPH_H = 260;
3553
+ var GRAPH_PAD = 28;
3554
+ var GRAPH_LW = GRAPH_W - GRAPH_PAD * 2;
3555
+ var GRAPH_LH = GRAPH_H - GRAPH_PAD * 2;
3556
+ var graphMarkerSeq = 0;
3557
+ function renderGraph(el, raw) {
3558
+ const noop = () => void 0;
3559
+ let current2 = normalizeGraphSpec(raw);
3560
+ if (typeof document === "undefined") {
3561
+ return { el, spec: current2, update: noop, on: () => noop, destroy: noop };
3562
+ }
3563
+ const legendState = { hidden: null };
3564
+ let layout = null;
3565
+ let svgRef = null;
3566
+ let nodeEls = [];
3567
+ let edgeList = [];
3568
+ let nodeRadii = [];
3569
+ let directed = false;
3570
+ let arrowId = "";
3571
+ const edgeEnds = (from, to) => {
3572
+ const ax = GRAPH_PAD + (layout ? layout.x[from] : 0);
3573
+ const ay = GRAPH_PAD + (layout ? layout.y[from] : 0);
3574
+ const bx = GRAPH_PAD + (layout ? layout.x[to] : 0);
3575
+ const by = GRAPH_PAD + (layout ? layout.y[to] : 0);
3576
+ if (!directed) {
3577
+ return { x1: ax.toFixed(1), y1: ay.toFixed(1), x2: bx.toFixed(1), y2: by.toFixed(1) };
3578
+ }
3579
+ const dx = bx - ax;
3580
+ const dy = by - ay;
3581
+ const d = Math.hypot(dx, dy) || 1;
3582
+ const cut = (nodeRadii[to] ?? 6) + 4;
3583
+ return {
3584
+ x1: ax.toFixed(1),
3585
+ y1: ay.toFixed(1),
3586
+ x2: (bx - dx / d * cut).toFixed(1),
3587
+ y2: (by - dy / d * cut).toFixed(1)
3588
+ };
3589
+ };
3590
+ const paint = (body) => {
3591
+ directed = current2.directed === true;
3592
+ layout = null;
3593
+ svgRef = null;
3594
+ nodeEls = [];
3595
+ edgeList = [];
3596
+ arrowId = "";
3597
+ const nodes = current2.nodes;
3598
+ if (nodes.length === 0) {
3599
+ nodeRadii = [];
3600
+ renderEmpty(body, current2.emptyLabel);
3601
+ return;
3602
+ }
3603
+ const clusters = buildClusterModel(nodes.map((n) => n.cluster), current2.clusterLabels);
3604
+ const tone = toneVar(current2.tone);
3605
+ const idOf = new Map(nodes.map((n, i) => [n.id, i]));
3606
+ const degree = nodes.map(() => 0);
3607
+ for (const e of current2.edges) {
3608
+ const a = idOf.get(e.source);
3609
+ const b = idOf.get(e.target);
3610
+ if (a == null || b == null || a === b) continue;
3611
+ degree[a] += 1;
3612
+ degree[b] += 1;
3613
+ }
3614
+ const weights = nodes.map((n, i) => n.weight ?? degree[i]);
3615
+ let wMin = Infinity;
3616
+ let wMax = -Infinity;
3617
+ for (const w of weights) {
3618
+ if (w < wMin) wMin = w;
3619
+ if (w > wMax) wMax = w;
3620
+ }
3621
+ nodeRadii = weights.map((w) => 6 + (wMax > wMin ? (w - wMin) / (wMax - wMin) : 0.5) * 12);
3622
+ let eMin = Infinity;
3623
+ let eMax = -Infinity;
3624
+ for (const e of current2.edges) {
3625
+ if (e.weight == null) continue;
3626
+ if (e.weight < eMin) eMin = e.weight;
3627
+ if (e.weight > eMax) eMax = e.weight;
3628
+ }
3629
+ const edgeWidth = (e) => e.weight == null ? 1 : 0.6 + (eMax > eMin ? (e.weight - eMin) / (eMax - eMin) : 0.5) * 1.9;
3630
+ const wrap = document.createElement("div");
3631
+ wrap.className = "icen-chart-graph";
3632
+ if (current2.tone) wrap.style.setProperty("--chart-tone", toneVar(current2.tone));
3633
+ const svg = svgEl("svg", { viewBox: `0 0 ${GRAPH_W} ${GRAPH_H}`, role: "img", "aria-label": chartAriaLabel(current2.title, t("charts.type.graph")) });
3634
+ svgRef = svg;
3635
+ svg.addEventListener("pointerdown", onNodePointerDown);
3636
+ const defs = svgEl("defs", {});
3637
+ if (directed) {
3638
+ arrowId = `icen-chart-graph-arrow-${++graphMarkerSeq}`;
3639
+ const marker = svgEl("marker", {
3640
+ id: arrowId,
3641
+ viewBox: "0 0 8 8",
3642
+ refX: "7",
3643
+ refY: "4",
3644
+ markerWidth: "6",
3645
+ markerHeight: "6",
3646
+ orient: "auto"
3647
+ });
3648
+ marker.appendChild(svgEl("path", { d: "M0.5,0.8 L7.5,4 L0.5,7.2 Z", fill: "var(--icen-token-text-faint)" }));
3649
+ defs.appendChild(marker);
3650
+ }
3651
+ layout = buildLayoutState(nodes, current2.edges, GRAPH_LW, GRAPH_LH);
3652
+ initRingLayout(layout, 0);
3653
+ relaxLayout(layout, 300);
3654
+ fitLayout(layout, 20);
3655
+ const edgeLayer = svgEl("g", { class: "chart-graph-edges" });
3656
+ const nodeLayer = svgEl("g", { class: "chart-graph-nodes" });
3657
+ for (const e of current2.edges) {
3658
+ const from = idOf.get(e.source);
3659
+ const to = idOf.get(e.target);
3660
+ if (from == null || to == null || from === to) continue;
3661
+ const line = svgEl("line", {
3662
+ ...edgeEnds(from, to),
3663
+ stroke: "var(--icen-token-line-soft)",
3664
+ "stroke-width": edgeWidth(e).toFixed(2),
3665
+ ...directed ? { "marker-end": `url(#${arrowId})` } : {}
3666
+ });
3667
+ line.classList.add("icen-chart-graph-edge");
3668
+ const fromKey = nodes[from]?.cluster;
3669
+ const toKey = nodes[to]?.cluster;
3670
+ if (fromKey) line.setAttribute("data-icen-edge-cluster-from", fromKey);
3671
+ if (toKey) line.setAttribute("data-icen-edge-cluster-to", toKey);
3672
+ edgeList.push({ el: line, from, to });
3673
+ edgeLayer.appendChild(line);
3674
+ }
3675
+ nodes.forEach((node, i) => {
3676
+ const clusterKey = node.cluster;
3677
+ const ci = clusterKey != null && clusters ? clusters.indexOf.get(clusterKey) : void 0;
3678
+ const clusterName = ci != null && clusters && clusterKey != null ? clusters.label(clusterKey) : void 0;
3679
+ const fill = clusters ? ci != null ? clusters.color(ci) : "var(--icen-token-text-faint)" : tone;
3680
+ const pos = { x: GRAPH_PAD + layout.x[i], y: GRAPH_PAD + layout.y[i] };
3681
+ const g = svgEl("g", {
3682
+ class: "icen-chart-graph-node",
3683
+ transform: `translate(${pos.x.toFixed(1)},${pos.y.toFixed(1)})`
3684
+ });
3685
+ g.setAttribute("data-icen-chart-node-id", node.id);
3686
+ setMarkData(g, {
3687
+ index: i,
3688
+ seriesIndex: ci,
3689
+ seriesName: clusterName,
3690
+ label: node.label ?? node.id,
3691
+ value: weights[i]
3692
+ });
3693
+ const dot = svgEl("circle", {
3694
+ class: "icen-chart-graph-dot",
3695
+ r: (nodeRadii[i] ?? 6).toFixed(1),
3696
+ fill,
3697
+ "fill-opacity": "0.92",
3698
+ stroke: "var(--icen-token-card)",
3699
+ "stroke-width": "1.5"
3700
+ });
3701
+ const hit = svgEl("circle", {
3702
+ class: "chart-graph-hit",
3703
+ r: String(Math.max((nodeRadii[i] ?? 6) + 4, 11)),
3704
+ fill: "transparent",
3705
+ "data-icen-chart-hit": "1"
3706
+ });
3707
+ const labelText = truncateText(node.label ?? node.id);
3708
+ const head = clusterName ? `${clusterName} \xB7 ${labelText}` : labelText;
3709
+ const tip = weights[i] > 0 ? `${head}\uFF1A${weights[i]}` : head;
3710
+ const title = svgEl("title", {});
3711
+ title.textContent = tip;
3712
+ g.append(dot, hit, title);
3713
+ registerDatumMark(g, { id: node.id, label: node.label ?? node.id, cluster: clusterKey, meta: node.meta }, tip);
3714
+ nodeEls.push(g);
3715
+ nodeLayer.appendChild(g);
3716
+ });
3717
+ svg.append(defs, edgeLayer, nodeLayer);
3718
+ wrap.appendChild(svg);
3719
+ body.appendChild(wrap);
3720
+ if (clusters) {
3721
+ const legend = toggleLegend(
3722
+ clusters.keys.map((key, ci) => ({ key, label: clusters.label(key), tone: ci + 1, si: ci })),
3723
+ wrap
3724
+ );
3725
+ body.appendChild(legend);
3726
+ const syncEdges = () => {
3727
+ const hiddenOf = (key) => key != null && legend.querySelector(`[data-icen-chart-key="${CSS.escape(key)}"]`)?.classList.contains("is-off") === true;
3728
+ for (const edge of edgeList) {
3729
+ edge.el.classList.toggle(
3730
+ "is-off",
3731
+ hiddenOf(edge.el.getAttribute("data-icen-edge-cluster-from")) || hiddenOf(edge.el.getAttribute("data-icen-edge-cluster-to"))
3732
+ );
3733
+ }
3734
+ };
3735
+ legend.addEventListener("click", syncEdges);
3736
+ legend.addEventListener("keydown", (e) => {
3737
+ if (e.key === "Enter" || e.key === " ") syncEdges();
3738
+ });
3739
+ }
3740
+ };
3741
+ const paintPositions = () => {
3742
+ if (!layout) return;
3743
+ for (let i = 0; i < nodeEls.length; i++) {
3744
+ nodeEls[i]?.setAttribute(
3745
+ "transform",
3746
+ `translate(${(GRAPH_PAD + layout.x[i]).toFixed(1)},${(GRAPH_PAD + layout.y[i]).toFixed(1)})`
3747
+ );
3748
+ }
3749
+ for (const edge of edgeList) {
3750
+ const ends = edgeEnds(edge.from, edge.to);
3751
+ edge.el.setAttribute("x1", ends.x1);
3752
+ edge.el.setAttribute("y1", ends.y1);
3753
+ edge.el.setAttribute("x2", ends.x2);
3754
+ edge.el.setAttribute("y2", ends.y2);
3755
+ }
3756
+ };
3757
+ let drag = null;
3758
+ const applyDragFrame = () => {
3759
+ if (!drag || !layout || !svgRef) return;
3760
+ const p = svgClientPoint(svgRef, drag.px, drag.py, GRAPH_W, GRAPH_H);
3761
+ const i = drag.i;
3762
+ layout.x[i] = Math.min(GRAPH_LW, Math.max(0, p.x - GRAPH_PAD));
3763
+ layout.y[i] = Math.min(GRAPH_LH, Math.max(0, p.y - GRAPH_PAD));
3764
+ relaxLayout(layout, 20, i, 0.4);
3765
+ paintPositions();
3766
+ drag.raf = 0;
3767
+ };
3768
+ const onDragMove = (e) => {
3769
+ if (!drag) return;
3770
+ if (Math.abs(e.clientX - drag.px) + Math.abs(e.clientY - drag.py) > 2) drag.moved = true;
3771
+ drag.px = e.clientX;
3772
+ drag.py = e.clientY;
3773
+ if (drag.raf === 0) drag.raf = requestAnimationFrame(applyDragFrame);
3774
+ };
3775
+ const endDrag = () => {
3776
+ if (!drag) return;
3777
+ if (drag.raf !== 0) cancelAnimationFrame(drag.raf);
3778
+ nodeEls[drag.i]?.classList.remove("is-drag");
3779
+ svgRef?.classList.remove("is-dragging");
3780
+ if (drag.moved) eventsControl.suppressNextClick();
3781
+ drag = null;
3782
+ window.removeEventListener("pointermove", onDragMove);
3783
+ window.removeEventListener("pointerup", endDrag);
3784
+ window.removeEventListener("pointercancel", endDrag);
3785
+ };
3786
+ const onNodePointerDown = (e) => {
3787
+ if (e.button !== 0 || !layout) return;
3788
+ const target = e.target;
3789
+ const g = target instanceof Element ? target.closest(".icen-chart-graph-node") : null;
3790
+ if (!g) return;
3791
+ const i = layout.index.get(g.getAttribute("data-icen-chart-node-id") ?? "");
3792
+ if (i == null) return;
3793
+ e.preventDefault();
3794
+ endDrag();
3795
+ drag = { i, moved: false, raf: 0, px: e.clientX, py: e.clientY };
3796
+ g.classList.add("is-drag");
3797
+ svgRef?.classList.add("is-dragging");
3798
+ window.addEventListener("pointermove", onDragMove);
3799
+ window.addEventListener("pointerup", endDrag);
3800
+ window.addEventListener("pointercancel", endDrag);
3801
+ };
3802
+ chartDelegateMeta.set(el, { type: () => GRAPH_CHART_TYPE, tooltip: () => current2.tooltip !== false });
3803
+ const eventsControl = bindDatumEvents(el);
3804
+ const render = () => {
3805
+ if (drag) endDrag();
3806
+ const clusters = buildClusterModel(current2.nodes.map((n) => n.cluster), current2.clusterLabels);
3807
+ if (current2.legend !== void 0) legendState.hidden = current2.legend === false;
3808
+ withChrome(el, current2, clusters != null, paint, legendState);
3809
+ };
3810
+ render();
3811
+ return {
3812
+ el,
3813
+ /** 当前规格(getter:update 换引用后读到最新) */
3814
+ get spec() {
3815
+ return current2;
3816
+ },
3817
+ update(next) {
3818
+ current2 = normalizeGraphSpec(next);
3819
+ render();
3820
+ },
3821
+ on(name, listener) {
3822
+ const handler = (event) => {
3823
+ listener(event.detail, event);
3824
+ };
3825
+ el.addEventListener(`icen:chart-${name}`, handler);
3826
+ return () => el.removeEventListener(`icen:chart-${name}`, handler);
3827
+ },
3828
+ destroy() {
3829
+ if (drag) endDrag();
3830
+ el.textContent = "";
3831
+ eventsControl.unbind();
3832
+ chartTooltip().hidden = true;
3833
+ }
3834
+ };
3835
+ }
3836
+ var MAP_W = 360;
3837
+ var MAP_H = 220;
3838
+ var MAP_PAD = 14;
3839
+ function renderMap(el, raw) {
3840
+ const noop = () => void 0;
3841
+ let current2 = normalizeMapSpec(raw);
3842
+ if (typeof document === "undefined") {
3843
+ return { el, spec: current2, update: noop, on: () => noop, destroy: noop };
3844
+ }
3845
+ const legendState = { hidden: null };
3846
+ const paint = (body) => {
3847
+ const points = current2.points.filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y));
3848
+ if (points.length === 0) {
3849
+ renderEmpty(body, current2.emptyLabel);
3850
+ return;
3851
+ }
3852
+ const clusters = buildClusterModel(points.map((p) => p.cluster), current2.clusters);
3853
+ const tone = toneVar(current2.tone);
3854
+ let xMin = Infinity;
3855
+ let xMax = -Infinity;
3856
+ let yMin = Infinity;
3857
+ let yMax = -Infinity;
3858
+ for (const p of points) {
3859
+ if (p.x < xMin) xMin = p.x;
3860
+ if (p.x > xMax) xMax = p.x;
3861
+ if (p.y < yMin) yMin = p.y;
3862
+ if (p.y > yMax) yMax = p.y;
3863
+ }
3864
+ if (xMax === xMin) xMax = xMin + 1;
3865
+ if (yMax === yMin) yMax = yMin + 1;
3866
+ const xAt = (x) => MAP_PAD + (x - xMin) / (xMax - xMin) * (MAP_W - MAP_PAD * 2);
3867
+ const yAt = (y) => MAP_H - MAP_PAD - (y - yMin) / (yMax - yMin) * (MAP_H - MAP_PAD * 2);
3868
+ const wrap = document.createElement("div");
3869
+ wrap.className = "icen-chart-map";
3870
+ if (current2.tone) wrap.style.setProperty("--chart-tone", toneVar(current2.tone));
3871
+ const svg = svgEl("svg", { viewBox: `0 0 ${MAP_W} ${MAP_H}`, role: "img", "aria-label": chartAriaLabel(current2.title, t("charts.type.map")) });
3872
+ for (let i = 0; i <= 3; i++) {
3873
+ const y = MAP_PAD + i * (MAP_H - MAP_PAD * 2) / 3;
3874
+ svg.appendChild(svgEl("line", {
3875
+ x1: String(MAP_PAD),
3876
+ x2: String(MAP_W - MAP_PAD),
3877
+ y1: String(y),
3878
+ y2: String(y),
3879
+ stroke: "var(--icen-token-line-soft)",
3880
+ "stroke-width": "0.75",
3881
+ "stroke-dasharray": "2 4"
3882
+ }));
3883
+ }
3884
+ for (let i = 0; i <= 4; i++) {
3885
+ const x = MAP_PAD + i * (MAP_W - MAP_PAD * 2) / 4;
3886
+ svg.appendChild(svgEl("line", {
3887
+ x1: String(x),
3888
+ x2: String(x),
3889
+ y1: String(MAP_PAD),
3890
+ y2: String(MAP_H - MAP_PAD),
3891
+ stroke: "var(--icen-token-line-soft)",
3892
+ "stroke-width": "0.75",
3893
+ "stroke-dasharray": "2 4"
3894
+ }));
3895
+ }
3896
+ points.forEach((p, i) => {
3897
+ const ci = p.cluster != null && clusters ? clusters.indexOf.get(p.cluster) : void 0;
3898
+ const clusterName = ci != null && clusters && p.cluster != null ? clusters.label(p.cluster) : void 0;
3899
+ const color = clusters ? ci != null ? clusters.color(ci) : "var(--icen-token-text-faint)" : tone;
3900
+ const label = p.label ?? p.id;
3901
+ const labelText = truncateText(label);
3902
+ const tip = clusterName ? `${clusterName} \xB7 ${labelText}` : labelText;
3903
+ const g = svgEl("g", {
3904
+ class: "icen-chart-map-point",
3905
+ transform: `translate(${xAt(p.x).toFixed(1)},${yAt(p.y).toFixed(1)})`
3906
+ });
3907
+ g.setAttribute("data-icen-chart-point-id", p.id);
3908
+ setMarkData(g, { index: i, seriesIndex: ci, seriesName: clusterName, label });
3909
+ const dot = svgEl("circle", {
3910
+ class: "icen-chart-map-dot",
3911
+ r: "4",
3912
+ fill: color,
3913
+ "fill-opacity": "0.7",
3914
+ stroke: color,
3915
+ "stroke-width": "1"
3916
+ });
3917
+ const hit = svgEl("circle", { class: "chart-map-hit", r: "9", fill: "transparent", "data-icen-chart-hit": "1" });
3918
+ const title = svgEl("title", {});
3919
+ title.textContent = tip;
3920
+ g.append(dot, hit, title);
3921
+ registerDatumMark(g, { id: p.id, label, cluster: p.cluster, meta: p.meta }, tip);
3922
+ svg.appendChild(g);
3923
+ });
3924
+ wrap.appendChild(svg);
3925
+ body.appendChild(wrap);
3926
+ if (clusters) {
3927
+ body.appendChild(
3928
+ toggleLegend(
3929
+ clusters.keys.map((key, ci) => ({ key, label: clusters.label(key), tone: ci + 1, si: ci })),
3930
+ wrap
3931
+ )
3932
+ );
3933
+ }
3934
+ };
3935
+ chartDelegateMeta.set(el, { type: () => MAP_CHART_TYPE, tooltip: () => current2.tooltip !== false });
3936
+ const eventsControl = bindDatumEvents(el);
3937
+ const render = () => {
3938
+ const clusters = buildClusterModel(current2.points.map((p) => p.cluster), current2.clusters);
3939
+ if (current2.legend !== void 0) legendState.hidden = current2.legend === false;
3940
+ withChrome(el, current2, clusters != null, paint, legendState);
3941
+ };
3942
+ render();
3943
+ return {
3944
+ el,
3945
+ spec: current2,
3946
+ update(next) {
3947
+ current2 = normalizeMapSpec(next);
3948
+ render();
3949
+ },
3950
+ on(name, listener) {
3951
+ const handler = (event) => {
3952
+ listener(event.detail, event);
3953
+ };
3954
+ el.addEventListener(`icen:chart-${name}`, handler);
3955
+ return () => el.removeEventListener(`icen:chart-${name}`, handler);
3956
+ },
3957
+ destroy() {
3958
+ el.textContent = "";
3959
+ eventsControl.unbind();
3960
+ chartTooltip().hidden = true;
3961
+ }
3962
+ };
3963
+ }
1528
3964
  export {
1529
3965
  bindChartEvents,
1530
3966
  chartFormatValue,
1531
3967
  inferChartType,
3968
+ layoutGraph,
3969
+ lttb,
1532
3970
  normalizeChartSpec,
1533
3971
  registerChartTone,
1534
3972
  renderArea,
@@ -1536,9 +3974,11 @@ export {
1536
3974
  renderChart,
1537
3975
  renderDonut,
1538
3976
  renderGauge,
3977
+ renderGraph,
1539
3978
  renderHBar,
1540
3979
  renderHeatmap,
1541
3980
  renderLine,
3981
+ renderMap,
1542
3982
  renderRadar,
1543
3983
  renderScatter,
1544
3984
  renderSparkline,