@icen.ai/ui 0.9.0 → 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 (281) hide show
  1. package/AGENTS.md +138 -80
  2. package/README.md +66 -91
  3. package/dist/base.css +208 -179
  4. package/dist/behaviors/accordion.mjs +8 -8
  5. package/dist/behaviors/ai-branch.d.mts +1 -1
  6. package/dist/behaviors/ai-branch.mjs +1010 -94
  7. package/dist/behaviors/ai-chat.d.mts +7 -7
  8. package/dist/behaviors/ai-chat.mjs +1071 -161
  9. package/dist/behaviors/ai-composer.d.mts +44 -12
  10. package/dist/behaviors/ai-composer.mjs +1997 -524
  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 +1 -1
  15. package/dist/behaviors/ai-feedback.mjs +1035 -126
  16. package/dist/behaviors/ai-panel.d.mts +8 -8
  17. package/dist/behaviors/ai-panel.mjs +1284 -266
  18. package/dist/behaviors/ai-provider.mjs +1003 -101
  19. package/dist/behaviors/ai-threads.d.mts +1 -1
  20. package/dist/behaviors/ai-threads.mjs +1068 -154
  21. package/dist/behaviors/ai-tool.d.mts +2 -2
  22. package/dist/behaviors/ai-tool.mjs +2037 -396
  23. package/dist/behaviors/ai-tools.d.mts +89 -16
  24. package/dist/behaviors/ai-tools.mjs +4448 -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 +72 -8
  34. package/dist/behaviors/charts.mjs +1901 -256
  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 +120 -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 +1 -1
  68. package/dist/behaviors/kb-access.mjs +2129 -239
  69. package/dist/behaviors/kb-acl.d.mts +6 -6
  70. package/dist/behaviors/kb-acl.mjs +1119 -179
  71. package/dist/behaviors/kb-answer.mjs +2070 -418
  72. package/dist/behaviors/kb-audit.d.mts +1 -1
  73. package/dist/behaviors/kb-audit.mjs +1076 -154
  74. package/dist/behaviors/kb-canvas.mjs +1100 -177
  75. package/dist/behaviors/kb-chain.mjs +1022 -100
  76. package/dist/behaviors/kb-checkpoint.d.mts +1 -1
  77. package/dist/behaviors/kb-checkpoint.mjs +1319 -169
  78. package/dist/behaviors/kb-chunks.mjs +1152 -213
  79. package/dist/behaviors/kb-citation.d.mts +1 -1
  80. package/dist/behaviors/kb-citation.mjs +1201 -179
  81. package/dist/behaviors/kb-clarify.mjs +1058 -130
  82. package/dist/behaviors/kb-conflict.mjs +1041 -119
  83. package/dist/behaviors/kb-connector.mjs +1104 -176
  84. package/dist/behaviors/kb-core.d.mts +6 -1
  85. package/dist/behaviors/kb-core.mjs +1077 -162
  86. package/dist/behaviors/kb-eval.mjs +1971 -144
  87. package/dist/behaviors/kb-explain.d.mts +1 -1
  88. package/dist/behaviors/kb-explain.mjs +1024 -102
  89. package/dist/behaviors/kb-filter.mjs +2008 -178
  90. package/dist/behaviors/kb-gap.d.mts +1 -1
  91. package/dist/behaviors/kb-gap.mjs +1043 -121
  92. package/dist/behaviors/kb-hittest.d.mts +1 -1
  93. package/dist/behaviors/kb-hittest.mjs +1093 -150
  94. package/dist/behaviors/kb-metadata.mjs +1052 -130
  95. package/dist/behaviors/kb-passage.d.mts +2 -2
  96. package/dist/behaviors/kb-passage.mjs +1101 -161
  97. package/dist/behaviors/kb-pipeline.d.mts +2 -2
  98. package/dist/behaviors/kb-pipeline.mjs +1082 -162
  99. package/dist/behaviors/kb-qa.mjs +1088 -160
  100. package/dist/behaviors/kb-rerank.mjs +1047 -114
  101. package/dist/behaviors/kb-retrieval.mjs +2063 -224
  102. package/dist/behaviors/kb-review.mjs +1990 -159
  103. package/dist/behaviors/kb-sandbox.d.mts +1 -1
  104. package/dist/behaviors/kb-sandbox.mjs +1026 -104
  105. package/dist/behaviors/kb-segment.mjs +1111 -179
  106. package/dist/behaviors/kb-select.d.mts +24 -0
  107. package/dist/behaviors/kb-select.mjs +1989 -0
  108. package/dist/behaviors/kb-sources.d.mts +1 -1
  109. package/dist/behaviors/kb-sources.mjs +1084 -170
  110. package/dist/behaviors/kb-sql.d.mts +1 -1
  111. package/dist/behaviors/kb-sql.mjs +1143 -226
  112. package/dist/behaviors/kb-trace.d.mts +1 -1
  113. package/dist/behaviors/kb-trace.mjs +1042 -128
  114. package/dist/behaviors/kb-visibility.mjs +1086 -158
  115. package/dist/behaviors/kb-who-can.mjs +1974 -137
  116. package/dist/behaviors/mentions.d.mts +93 -0
  117. package/dist/behaviors/mentions.mjs +2152 -0
  118. package/dist/behaviors/modal.d.mts +4 -3
  119. package/dist/behaviors/modal.mjs +1055 -33
  120. package/dist/behaviors/nav.d.mts +1 -1
  121. package/dist/behaviors/nav.mjs +6 -6
  122. package/dist/behaviors/notification.d.mts +16 -8
  123. package/dist/behaviors/notification.mjs +1615 -265
  124. package/dist/behaviors/panel.d.mts +4 -0
  125. package/dist/behaviors/panel.mjs +994 -0
  126. package/dist/behaviors/popover.d.mts +28 -5
  127. package/dist/behaviors/popover.mjs +225 -24
  128. package/dist/behaviors/select.d.mts +5 -2
  129. package/dist/behaviors/select.mjs +1410 -117
  130. package/dist/behaviors/sidebar.d.mts +1 -1
  131. package/dist/behaviors/sidebar.mjs +13 -13
  132. package/dist/behaviors/slider.d.mts +1 -1
  133. package/dist/behaviors/slider.mjs +9 -9
  134. package/dist/behaviors/split-pane.d.mts +1 -1
  135. package/dist/behaviors/split-pane.mjs +7 -7
  136. package/dist/behaviors/suggestions.d.mts +27 -0
  137. package/dist/behaviors/suggestions.mjs +1089 -0
  138. package/dist/behaviors/svg-parse.d.mts +14 -0
  139. package/dist/behaviors/svg-parse.mjs +37 -0
  140. package/dist/behaviors/tabs.d.mts +2 -2
  141. package/dist/behaviors/tabs.mjs +25 -25
  142. package/dist/behaviors/tag-input.d.mts +23 -5
  143. package/dist/behaviors/tag-input.mjs +1359 -104
  144. package/dist/behaviors/theme-switcher.d.mts +24 -0
  145. package/dist/behaviors/theme-switcher.mjs +1981 -0
  146. package/dist/behaviors/theme.d.mts +15 -8
  147. package/dist/behaviors/theme.mjs +390 -28
  148. package/dist/behaviors/toast.d.mts +24 -25
  149. package/dist/behaviors/toast.mjs +1266 -88
  150. package/dist/behaviors/tooltip.d.mts +4 -0
  151. package/dist/behaviors/tooltip.mjs +378 -0
  152. package/dist/behaviors/tree-select.d.mts +69 -0
  153. package/dist/behaviors/tree-select.mjs +1907 -0
  154. package/dist/behaviors/tree.d.mts +1 -1
  155. package/dist/behaviors/tree.mjs +15 -15
  156. package/dist/behaviors/upload.d.mts +3 -2
  157. package/dist/behaviors/upload.mjs +1076 -66
  158. package/dist/cli.mjs +5 -6
  159. package/dist/components/ai-chat.css +666 -573
  160. package/dist/components/ai-diff.css +153 -132
  161. package/dist/components/ai-panel.css +351 -343
  162. package/dist/components/ai-tool.css +393 -238
  163. package/dist/components/autocomplete.css +61 -0
  164. package/dist/components/autocomplete.mjs +3 -0
  165. package/dist/components/back-top.css +34 -31
  166. package/dist/components/badge.css +44 -41
  167. package/dist/components/breadcrumb.css +67 -46
  168. package/dist/components/btn.css +80 -74
  169. package/dist/components/card.css +117 -106
  170. package/dist/components/cascader.css +311 -0
  171. package/dist/components/cascader.mjs +3 -0
  172. package/dist/components/chart-area.css +8 -2
  173. package/dist/components/chart-bar.css +95 -33
  174. package/dist/components/chart-gauge.css +14 -11
  175. package/dist/components/chart-graph.css +20 -17
  176. package/dist/components/chart-heatmap.css +96 -90
  177. package/dist/components/chart-line.css +8 -3
  178. package/dist/components/chart-pie.css +9 -6
  179. package/dist/components/chart-radar.css +8 -5
  180. package/dist/components/chart-scatter.css +9 -6
  181. package/dist/components/chart-sparkline.css +5 -2
  182. package/dist/components/chart-stack.css +14 -11
  183. package/dist/components/charts.css +164 -73
  184. package/dist/components/code-block.css +418 -0
  185. package/dist/components/code-block.mjs +4 -0
  186. package/dist/components/command-palette.css +138 -117
  187. package/dist/components/content.css +257 -251
  188. package/dist/components/copy.css +72 -44
  189. package/dist/components/datatable.css +423 -181
  190. package/dist/components/date-picker.css +135 -104
  191. package/dist/components/empty.css +68 -65
  192. package/dist/components/feedback.css +201 -183
  193. package/dist/components/form-validate.css +120 -0
  194. package/dist/components/form-validate.mjs +3 -0
  195. package/dist/components/form.css +150 -127
  196. package/dist/components/icon.css +102 -0
  197. package/dist/components/icon.mjs +3 -0
  198. package/dist/components/input.css +159 -104
  199. package/dist/components/kb-agent.css +188 -173
  200. package/dist/components/kb-data.css +257 -253
  201. package/dist/components/kb-ground.css +197 -185
  202. package/dist/components/kb-ingest.css +334 -350
  203. package/dist/components/kb-ops.css +265 -259
  204. package/dist/components/kb-perm.css +260 -271
  205. package/dist/components/kb-search.css +317 -360
  206. package/dist/components/kb.css +120 -109
  207. package/dist/components/layout.css +85 -82
  208. package/dist/components/material-chinese.css +73 -0
  209. package/dist/components/material-soviet.css +76 -0
  210. package/dist/components/material.css +275 -0
  211. package/dist/components/media.css +220 -166
  212. package/dist/components/mentions.css +189 -0
  213. package/dist/components/mentions.mjs +3 -0
  214. package/dist/components/menu.css +113 -105
  215. package/dist/components/modal.css +107 -112
  216. package/dist/components/nav.css +118 -115
  217. package/dist/components/notification.css +238 -174
  218. package/dist/components/pagination.css +56 -53
  219. package/dist/components/panel.css +66 -63
  220. package/dist/components/panel.mjs +1 -0
  221. package/dist/components/pill.css +81 -71
  222. package/dist/components/popover.css +67 -53
  223. package/dist/components/scroll-area.css +113 -110
  224. package/dist/components/segmented.css +146 -143
  225. package/dist/components/select.css +206 -186
  226. package/dist/components/sidebar.css +137 -127
  227. package/dist/components/slider.css +117 -114
  228. package/dist/components/split-pane.css +112 -109
  229. package/dist/components/stat.css +112 -106
  230. package/dist/components/steps.css +109 -103
  231. package/dist/components/switch.css +138 -134
  232. package/dist/components/table.css +131 -107
  233. package/dist/components/tabs.css +114 -108
  234. package/dist/components/tag-input.css +48 -133
  235. package/dist/components/tag.css +81 -71
  236. package/dist/components/theme-switcher.css +105 -0
  237. package/dist/components/theme-switcher.mjs +3 -0
  238. package/dist/components/toast.css +100 -86
  239. package/dist/components/toolbar.css +45 -42
  240. package/dist/components/tooltip.css +102 -43
  241. package/dist/components/tooltip.mjs +1 -0
  242. package/dist/components/tree-select.css +416 -0
  243. package/dist/components/tree-select.mjs +3 -0
  244. package/dist/components/upload.css +135 -92
  245. package/dist/fonts/LICENSES/ark-pixel/OFL.txt +96 -0
  246. package/dist/fonts/LICENSES/cubic-11/OFL.txt +108 -0
  247. package/dist/fonts/LICENSES/galmuri/LICENSE.txt +93 -0
  248. package/dist/fonts/OFL.txt +96 -0
  249. package/dist/fonts/README.md +14 -0
  250. package/dist/fonts/fusion-pixel-12px-zh-hans.woff2 +0 -0
  251. package/dist/index.d.mts +28 -14
  252. package/dist/index.mjs +20705 -9601
  253. package/dist/react.d.mts +91 -0
  254. package/dist/react.mjs +8208 -0
  255. package/dist/registry.json +130 -13
  256. package/dist/tokens/chinese.css +44 -0
  257. package/dist/tokens/colors.css +365 -1222
  258. package/dist/tokens/design.css +109 -0
  259. package/dist/tokens/dtcg.json +11198 -0
  260. package/dist/tokens/figma.variables.json +7017 -0
  261. package/dist/tokens/icon-masks.css +78 -0
  262. package/dist/tokens/pixel.css +50 -0
  263. package/dist/tokens/soviet.css +44 -0
  264. package/dist/tokens/style-profiles.css +36 -547
  265. package/dist/tokens/themes.json +308 -0
  266. package/dist/tokens/tokens-studio.json +6728 -0
  267. package/dist/tokens/typography.css +21 -18
  268. package/dist/tokens.css +1555 -1801
  269. package/dist/ui.css +11975 -9831
  270. package/dist/vue.d.mts +344 -0
  271. package/dist/vue.mjs +7781 -0
  272. package/docs/spec/ai-input-experience.md +75 -0
  273. package/docs/spec/ai-native.md +238 -147
  274. package/docs/spec/design-philosophy.md +189 -0
  275. package/docs/spec/design-primitives.md +196 -0
  276. package/docs/spec/kb-family.md +147 -115
  277. package/package.json +43 -8
  278. package/dist/retro-effects.css +0 -255
  279. package/dist/tokens/retro-effects.css +0 -255
  280. package/docs/research/2026-10-03-ai-kb-components.md +0 -392
  281. package/docs/research/2026-10-03-kb-permissions.md +0 -992
@@ -1,52 +1,1007 @@
1
+ // src/behaviors/events.ts
2
+ function emitIcen(source, type, detail) {
3
+ return source.dispatchEvent(
4
+ new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
5
+ );
6
+ }
7
+
8
+ // src/behaviors/i18n.ts
9
+ var ICEN_LOCALE_ZH_CN = {
10
+ /* ── aiBranch ── */
11
+ "aiBranch.nextVersion": "\u4E0B\u4E00\u7248\u672C",
12
+ "aiBranch.prevVersion": "\u4E0A\u4E00\u7248\u672C",
13
+ "aiBranch.version": "\u5206\u652F\u7248\u672C",
14
+ /* ── aiChat ── */
15
+ "aiChat.backToBottom": "\u56DE\u5230\u5E95\u90E8",
16
+ "aiChat.backToBottomCount": "\u56DE\u5230\u5E95\u90E8 \xB7 {p0} \u6761\u65B0\u6D88\u606F",
17
+ "aiChat.continue": "\u7EE7\u7EED\u751F\u6210",
18
+ "aiChat.download": "\u4E0B\u8F7D",
19
+ "aiChat.file": "\u6587\u4EF6",
20
+ "aiChat.roleSystem": "\u7CFB",
21
+ "aiChat.roleTool": "\u5DE5",
22
+ "aiChat.roleUser": "\u6211",
23
+ "aiChat.thinking": "\u601D\u8003 {p0}",
24
+ /* ── aiComposer ── */
25
+ "aiComposer.attachAgent": "\u4EE3\u7406",
26
+ "aiComposer.library": "\u6DFB\u52A0\u4E0A\u4E0B\u6587\u4E0E\u547D\u4EE4",
27
+ "aiComposer.searchLibrary": "\u641C\u7D22\u6280\u80FD\u3001\u5DE5\u5177\u3001\u6587\u4EF6\u4E0E\u547D\u4EE4",
28
+ "aiComposer.all": "\u5168\u90E8",
29
+ "aiComposer.skills": "\u6280\u80FD",
30
+ "aiComposer.tools": "\u5DE5\u5177",
31
+ "aiComposer.libraryHint": "\u4E0A\u4E0B\u6587\u968F\u6D88\u606F\u53D1\u9001 \xB7 \u547D\u4EE4\u5148\u586B\u5165\u8F93\u5165\u6846",
32
+ "aiComposer.commandHint": "\u2191 \u2193 \u6D4F\u89C8 \xB7 Enter \u6267\u884C \xB7 Esc \u8FD4\u56DE",
33
+ "aiComposer.refHint": "\u2191 \u2193 \u6D4F\u89C8 \xB7 Enter \u5F15\u7528 \xB7 Esc \u8FD4\u56DE",
34
+ "aiComposer.noLibraryMatch": "\u6CA1\u6709\u5339\u914D\u7684\u9879\u76EE\uFF0C\u8BD5\u8BD5\u5176\u4ED6\u5173\u952E\u8BCD",
35
+ "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",
36
+ "aiComposer.attachDoc": "\u6587\u6863",
37
+ "aiComposer.attachFile": "\u6587\u4EF6",
38
+ "aiComposer.attachFolder": "\u6587\u4EF6\u5939",
39
+ "aiComposer.cacheInvalidHint": "\u5207\u6362\u6A21\u578B\u4F1A\u4F7F prompt \u7F13\u5B58\u5931\u6548",
40
+ "aiComposer.commands": "\u547D\u4EE4",
41
+ "aiComposer.currentTodos": "\u5F53\u524D\u5F85\u529E",
42
+ "aiComposer.done": "\u5B8C\u6210",
43
+ "aiComposer.justNow": "\u521A\u521A \xB7 {p0}",
44
+ "aiComposer.modelAria": "\u6A21\u578B {p0}\uFF08{p1}\uFF09",
45
+ "aiComposer.noCommandMatch": "\u65E0\u5339\u914D\u547D\u4EE4",
46
+ "aiComposer.noModelMatch": "\u65E0\u5339\u914D\u6A21\u578B",
47
+ "aiComposer.noRefMatch": "\u65E0\u5339\u914D\u5F15\u7528",
48
+ "aiComposer.placeholder": "\u8F93\u5165\u6D88\u606F\uFF0C/ \u547D\u4EE4 \xB7 @ \u5F15\u7528 \xB7 Enter \u53D1\u9001",
49
+ "aiComposer.refs": "\u5F15\u7528",
50
+ "aiComposer.removeAttachment": "\u79FB\u9664\u9644\u4EF6 {p0}",
51
+ "aiComposer.removeQueued": "\u79FB\u9664\u6392\u961F\u6D88\u606F {p0}",
52
+ "aiComposer.removeRef": "\u79FB\u9664\u5F15\u7528 {p0}",
53
+ "aiComposer.searchModels": "\u641C\u7D22\u6A21\u578B",
54
+ "aiComposer.selectModel": "\u9009\u62E9\u6A21\u578B",
55
+ "aiComposer.send": "\u53D1\u9001",
56
+ "aiComposer.stop": "\u505C\u6B62\u751F\u6210",
57
+ "aiComposer.stopped": "\u5DF2\u505C\u6B62",
58
+ "aiComposer.todoChip": "\u5F53\u524D\u5F85\u529E {p0}/{p1}{p2}",
59
+ "aiComposer.todoHasError": "\uFF08\u6709\u5931\u8D25\u9879\uFF09",
60
+ /* ── aiCore ── */
61
+ "aiCore.kindBrowser": "\u6D4F\u89C8",
62
+ "aiCore.kindChart": "\u56FE\u8868",
63
+ "aiCore.kindDelete": "\u5220\u9664",
64
+ "aiCore.kindEdit": "\u7F16\u8F91",
65
+ "aiCore.kindFetch": "\u8BF7\u6C42",
66
+ "aiCore.kindGlob": "\u5339\u914D",
67
+ "aiCore.kindGrep": "\u641C\u7D22",
68
+ "aiCore.kindNote": "\u7B14\u8BB0",
69
+ "aiCore.kindPlan": "\u8BA1\u5212",
70
+ "aiCore.kindRead": "\u8BFB\u53D6",
71
+ "aiCore.kindSearch": "\u68C0\u7D22",
72
+ "aiCore.kindShell": "\u547D\u4EE4",
73
+ "aiCore.kindSkill": "\u6280\u80FD",
74
+ "aiCore.kindSubagent": "\u5B50\u4EE3\u7406",
75
+ "aiCore.kindTodo": "\u5F85\u529E",
76
+ "aiCore.kindWrite": "\u5199\u5165",
77
+ "aiCore.statusApproval": "\u5F85\u786E\u8BA4",
78
+ "aiCore.statusCancelled": "\u5DF2\u53D6\u6D88",
79
+ "aiCore.statusDone": "\u5B8C\u6210",
80
+ "aiCore.statusError": "\u5931\u8D25",
81
+ "aiCore.statusPending": "\u7B49\u5F85",
82
+ "aiCore.statusRunning": "\u6267\u884C\u4E2D",
83
+ "aiCore.statusStreaming": "\u6D41\u5F0F\u4E2D",
84
+ "aiCore.todoProgress": "{p0}/{p1} \u5B8C\u6210",
85
+ /* ── aiDiff ── */
86
+ "aiDiff.accepted": "\u5DF2\u63A5\u53D7",
87
+ "aiDiff.binary": "\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u663E\u793A\u6587\u672C\u5DEE\u5F02",
88
+ "aiDiff.empty": "\u65E0\u5DEE\u5F02\u5185\u5BB9",
89
+ "aiDiff.rejected": "\u5DF2\u62D2\u7EDD",
90
+ /* ── aiFeedback ── */
91
+ "aiFeedback.helpful": "\u6709\u5E2E\u52A9",
92
+ "aiFeedback.notHelpful": "\u65E0\u5E2E\u52A9",
93
+ "aiFeedback.reasonCitationMismatch": "\u5F15\u7528\u4E0D\u7B26",
94
+ "aiFeedback.reasonOutdated": "\u5185\u5BB9\u8FC7\u65F6",
95
+ "aiFeedback.reasonUnauthorized": "\u8D8A\u6743\u4FE1\u606F",
96
+ "aiFeedback.reasonUnsourced": "\u65E0\u5F15\u7528\u4F9D\u636E",
97
+ "aiFeedback.reasonWrong": "\u7B54\u6848\u6709\u8BEF",
98
+ "aiFeedback.thanks": "\u611F\u8C22\u53CD\u9988\uFF0C\u5DF2\u5173\u8054\u5230\u8BE5\u6761\u56DE\u7B54\u7684\u8FFD\u8E2A\u8BB0\u5F55",
99
+ /* ── aiPanel ── */
100
+ "aiPanel.audit": "\u5BA1\u8BA1",
101
+ "aiPanel.cacheRead": "\u7F13\u5B58\u8BFB",
102
+ "aiPanel.cacheWrite": "\u7F13\u5B58\u5199",
103
+ "aiPanel.callsTokens": "{p0} \u6B21 \xB7 {p1} tok{p2}",
104
+ "aiPanel.clearAudit": "\u6E05\u9664\u5BA1\u8BA1",
105
+ "aiPanel.context": "\u4E0A\u4E0B\u6587",
106
+ "aiPanel.contextWindow": "\u4E0A\u4E0B\u6587\u7A97\u53E3",
107
+ "aiPanel.errorsCount": "{p0} \u5931\u8D25",
108
+ "aiPanel.files": "\u6587\u4EF6 ({p0})",
109
+ "aiPanel.itemStatus": "{p0}\uFF0C\u72B6\u6001\uFF1A{p1}",
110
+ "aiPanel.noAudit": "\u6682\u65E0\u5BA1\u8BA1\u8BB0\u5F55",
111
+ "aiPanel.noUsage": "\u6682\u65E0\u7528\u91CF",
112
+ "aiPanel.reasoning": "\u63A8\u7406",
113
+ "aiPanel.requestsCount": "{p0} \u6B21\u8BF7\u6C42",
114
+ "aiPanel.ringPercent": "\u4E0A\u4E0B\u6587\u7A97\u53E3\u5360\u7528 {p0}%\uFF0C\u70B9\u51FB\u67E5\u770B\u8BE6\u60C5",
115
+ "aiPanel.ringUsed": "\u5DF2\u7528 {p0}\uFF0C\u70B9\u51FB\u67E5\u770B\u8BE6\u60C5",
116
+ "aiPanel.taskProgress": "\u4EFB\u52A1\u8FDB\u5EA6",
117
+ "aiPanel.toolsCount": "{p0} \u5DE5\u5177",
118
+ "aiPanel.usage": "\u7528\u91CF",
119
+ "aiPanel.usageAria": "\u7528\u91CF\uFF1A{p0}",
120
+ /* ── aiProvider ── */
121
+ "aiProvider.audioUrlOnly": "\u4EC5 URL \u7684\u97F3\u9891\uFF08input_audio \u9700 base64\uFF0C\u8BF7\u5148\u53D6\u56DE\u5185\u8054\uFF09",
122
+ "aiProvider.audioUrlOnlyAnthropic": "\u4EC5 URL \u7684\u97F3\u9891\uFF08audio \u5757\u9700 base64 source\uFF09",
123
+ "aiProvider.base64File": "base64 \u6587\u4EF6\uFF08file \u90E8\u4EF6\u9700 Files API file_id\uFF0C\u8BF7\u7F6E\u4E8E url \u69FD\uFF09",
124
+ "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",
125
+ "aiProvider.emptyResponse": "\u54CD\u5E94\u4E3A\u7A7A",
126
+ "aiProvider.glmLabel": "GLM\uFF08\u667A\u8C31\uFF09",
127
+ "aiProvider.invalidJson": "\u54CD\u5E94\u4E0D\u662F\u5408\u6CD5 JSON",
128
+ "aiProvider.kimiLabel": "Kimi\uFF08\u6708\u4E4B\u6697\u9762\uFF09",
129
+ "aiProvider.networkFailed": "\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25\uFF1A{p0}{p1}",
130
+ "aiProvider.nonPdfDocument": "\u975E PDF \u6587\u4EF6\u5757\uFF08document \u5757\u4E3A application/pdf\uFF0C\u6536\u5230 {p0}\uFF09",
131
+ "aiProvider.requestCancelled": "\u8BF7\u6C42\u5DF2\u53D6\u6D88",
132
+ "aiProvider.resourceRef": "[\u8D44\u6E90] {p0} ({p1})",
133
+ "aiProvider.streamError": "\u6D41\u5F0F\u54CD\u5E94\u9519\u8BEF",
134
+ "aiProvider.toolRoleNoCallId": "role 'tool' \u4E14\u7F3A toolCallId\uFF08openai \u65CF\u9700 tool_call_id\uFF09",
135
+ "aiProvider.toolRoleNoUseId": "role 'tool' \u4E14\u7F3A toolCallId\uFF08anthropic \u65CF\u6620\u5C04 tool_result \u9700 tool_use_id\uFF09",
136
+ "aiProvider.unsupported": "{p0} \u7EBF\u534F\u8BAE\u4E0D\u652F\u6301 {p1}",
137
+ "aiProvider.videoUnsupported": "video \u8F93\u5165\uFF08\u65E0\u4E3B\u6D41 chat API \u652F\u6301\uFF09",
138
+ "aiProvider.videoUnsupportedAnthropic": "video \u8F93\u5165",
139
+ /* ── aiThreads ── */
140
+ "aiThreads.actionsAria": "\u4F1A\u8BDD\u64CD\u4F5C",
141
+ "aiThreads.archived": "\u5DF2\u5F52\u6863",
142
+ /* ── aiTool ── */
143
+ "aiTool.allow": "\u5141\u8BB8",
144
+ "aiTool.reject": "\u62D2\u7EDD",
145
+ /* ── aiTools ── */
146
+ "aiTools.chart.area": "line \u591A\u7CFB\u5217\u586B\u5145\u9762\u79EF",
147
+ "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",
148
+ "aiTools.chart.dates": "\u8D21\u732E\u65E5\u5386 ISO \u65E5\u671F\uFF08\u4E0E values \u914D\u5BF9\uFF09",
149
+ "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",
150
+ "aiTools.chart.dims": "data[] \u7684\u5B57\u6BB5\u6620\u5C04\uFF1A{label?, series?, value?, x?, y?}",
151
+ "aiTools.chart.format": "\u6570\u503C\u683C\u5F0F\u5316\uFF1A{notation?: compact|percent|ms, unit?}",
152
+ "aiTools.chart.labels": "\u7C7B\u76EE\u8F74\uFF08line/vbar/hbar\uFF09",
153
+ "aiTools.chart.legend": "false = \u521D\u59CB\u9690\u85CF\u56FE\u4F8B",
154
+ "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",
155
+ "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",
156
+ "aiTools.chart.points": "\u6563\u70B9/\u6C14\u6CE1\uFF1A[{x,y,size?,label?}]",
157
+ "aiTools.chart.radarAxes": "\u96F7\u8FBE\u8F74\u540D",
158
+ "aiTools.chart.segments": "\u5360\u6BD4\u578B\uFF08donut/stack\uFF09\uFF1A[{label,value}]",
159
+ "aiTools.chart.series": "\u591A\u7CFB\u5217\uFF08line/vbar/radar\uFF09",
160
+ "aiTools.chart.stacked": "vbar \u591A\u7CFB\u5217\u5806\u53E0\uFF08\u9ED8\u8BA4\u5206\u7EC4\uFF09",
161
+ "aiTools.chart.title": "\u56FE\u8868\u6807\u9898\uFF08\u6E32\u67D3\u5728\u5934\u90E8\u884C\u5DE6\u4FA7\uFF09",
162
+ "aiTools.chart.tooltip": "false = \u5173\u95ED\u5185\u7F6E\u63D0\u793A",
163
+ "aiTools.chart.type": "\u56FE\u8868\u7C7B\u578B\uFF0C\u7F3A\u7701\u81EA\u52A8\u63A8\u65AD",
164
+ "aiTools.chart.values": "\u5355\u7CFB\u5217\u6570\u503C\uFF08\u4E0E labels \u914D\u5BF9\uFF09",
165
+ "aiTools.chart.zoom": "\u7F29\u653E\u4EA4\u4E92\uFF1A'x'=\u7C7B\u76EE\u8F74\u533A\u95F4\u7F29\u653E\uFF0C'brush'=\u5237\u9009\u7F29\u653E",
166
+ "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",
167
+ "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",
168
+ "aiTools.datatable.rows": "\u884C\u6570\u636E\u6570\u7EC4\uFF1A\u6BCF\u9879\u4E3A\u4E00\u4E2A\u5BF9\u8C61\uFF0C\u5B57\u6BB5\u540D\u5BF9\u9F50 columns[].key",
169
+ "aiTools.datatable.title": "\u8868\u683C\u6807\u9898\uFF08\u6E32\u67D3\u5728\u5DE5\u5177\u680F\u5DE6\u4FA7\uFF09",
170
+ "aiTools.execFailed": "\u5DE5\u5177\u6267\u884C\u5931\u8D25\uFF1A{p0}",
171
+ "aiTools.execTimeout": "\u6267\u884C\u8D85\u65F6\uFF08{p0}ms\uFF09",
172
+ "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",
173
+ "aiTools.missingRequired": "\u7F3A\u5C11\u5FC5\u586B\u5B57\u6BB5",
174
+ "aiTools.none": "\uFF08\u65E0\u5DF2\u6CE8\u518C\u7684 UI \u5DE5\u5177\uFF09",
175
+ "aiTools.notInEnum": "\u4E0D\u5728\u5141\u8BB8\u53D6\u503C\u5185\uFF08{p0}\uFF09",
176
+ "aiTools.numMax": "\u6570\u503C {p0} \u5927\u4E8E\u6700\u5927\u503C {p1}",
177
+ "aiTools.numMin": "\u6570\u503C {p0} \u5C0F\u4E8E\u6700\u5C0F\u503C {p1}",
178
+ "aiTools.removeMount": "\u79FB\u9664 {p0} \u6302\u8F7D",
179
+ "aiTools.strMax": "\u957F\u5EA6 {p0} \u8D85\u8FC7\u4E0A\u9650 {p1}",
180
+ "aiTools.strMin": "\u957F\u5EA6 {p0} \u5C0F\u4E8E\u4E0B\u9650 {p1}",
181
+ "aiTools.typeMismatch": "\u671F\u671B {p0}\uFF0C\u5B9E\u5F97 {p1}",
182
+ "aiTools.validationFailed": "\u53C2\u6570\u6821\u9A8C\u5931\u8D25\uFF08{p0} \u9879\uFF09",
183
+ /* ── autocomplete ── */
184
+ "suggestions.loading": "\u6B63\u5728\u67E5\u627E\u5EFA\u8BAE\u2026",
185
+ "suggestions.error": "\u6682\u65F6\u65E0\u6CD5\u52A0\u8F7D\u5EFA\u8BAE\uFF0C\u8BF7\u91CD\u8BD5\u3002",
186
+ "suggestions.options": "\u5EFA\u8BAE",
187
+ "suggestions.empty": "\u6CA1\u6709\u5339\u914D\u7684\u5EFA\u8BAE",
188
+ "autocomplete.empty": "\u65E0\u5339\u914D\u7ED3\u679C",
189
+ /* ── backTop ── */
190
+ "backTop.label": "\u56DE\u5230\u9876\u90E8",
191
+ /* ── carousel ── */
192
+ "carousel.item": "\u7B2C {p0} \u5F20",
193
+ "carousel.region": "\u8F6E\u64AD",
194
+ "carousel.slide": "\u5E7B\u706F\u7247",
195
+ "carousel.status": "\u7B2C {p0} / {p1} \u5F20",
196
+ /* ── charts ── */
197
+ "charts.calendarWeekdays": ["\u4E00", "", "\u4E09", "", "\u4E94", "", "\u65E5"],
198
+ "charts.few": "\u5C11",
199
+ "charts.hideLegend": "\u9690\u85CF\u56FE\u4F8B",
200
+ "charts.loading": "\u52A0\u8F7D\u4E2D",
201
+ "charts.many": "\u591A",
202
+ "charts.monthLabel": "{p0}\u6708",
203
+ "charts.noData": "\u6682\u65E0\u6570\u636E",
204
+ "charts.resetZoom": "\u91CD\u7F6E\u7F29\u653E",
205
+ "charts.sampled": "\u5DF2\u62BD\u6837 {p0}\u2192{p1} \u70B9",
206
+ "charts.type.area": "\u9762\u79EF\u56FE",
207
+ "charts.type.donut": "\u73AF\u5F62\u56FE",
208
+ "charts.type.gauge": "\u4EEA\u8868\u76D8",
209
+ "charts.type.graph": "\u77E5\u8BC6\u56FE\u8C31",
210
+ "charts.type.heatmap": "\u70ED\u529B\u56FE",
211
+ "charts.type.line": "\u6298\u7EBF\u56FE",
212
+ "charts.type.map": "\u5730\u56FE",
213
+ "charts.type.radar": "\u96F7\u8FBE\u56FE",
214
+ "charts.type.scatter": "\u6563\u70B9\u56FE",
215
+ "charts.type.sparkline": "\u8FF7\u4F60\u8D8B\u52BF",
216
+ "charts.seriesName": "\u7CFB\u5217{p0}",
217
+ "charts.showLegend": "\u663E\u793A\u56FE\u4F8B",
218
+ "charts.value": "\u6570\u503C",
219
+ /* ── themeSwitcher ── */
220
+ "themeSwitcher.appearance": "\u5916\u89C2",
221
+ "themeSwitcher.theme": "\u4E3B\u9898",
222
+ "themeSwitcher.primitive": "\u539F\u8BED",
223
+ "themeSwitcher.mode": "\u660E\u6697",
224
+ "themeSwitcher.light": "\u6D45\u8272",
225
+ "themeSwitcher.dark": "\u6DF1\u8272",
226
+ "themeSwitcher.modern": "\u73B0\u4EE3",
227
+ "themeSwitcher.dongyun": "\u4E1C\u97F5",
228
+ "themeSwitcher.jugou": "\u5DE8\u6784",
229
+ "themeSwitcher.piano": "\u94A2\u7434",
230
+ "themeSwitcher.monet": "\u83AB\u5948",
231
+ "themeSwitcher.vangogh": "\u68B5\u9AD8",
232
+ "themeSwitcher.chinese": "\u4E2D\u5F0F",
233
+ "themeSwitcher.soviet": "\u82CF\u5F0F",
234
+ "themeSwitcher.pixel": "\u50CF\u7D20",
235
+ /* ── codeBlock ── */
236
+ "codeBlock.collapse": "\u6536\u8D77",
237
+ "codeBlock.region": "\u4EE3\u7801\u5185\u5BB9",
238
+ "codeBlock.copyFailed": "\u672A\u80FD\u590D\u5236\uFF0C\u8BF7\u91CD\u8BD5\u3002",
239
+ "codeBlock.copyRetry": "\u91CD\u8BD5\u590D\u5236",
240
+ "codeBlock.copy": "\u590D\u5236",
241
+ "codeBlock.foldCollapse": "\u6298\u53E0\u4EE3\u7801",
242
+ "codeBlock.foldExpand": "\u5C55\u5F00\u4EE3\u7801",
243
+ "codeBlock.linesTotal": "\u5171 {p0} \u884C",
244
+ "codeBlock.numHide": "\u9690\u85CF\u884C\u53F7",
245
+ "codeBlock.numShow": "\u663E\u793A\u884C\u53F7",
246
+ "codeBlock.tailAria": "\u5C55\u5F00\u6216\u6536\u8D77\u4EE3\u7801",
247
+ "codeBlock.wrapOff": "\u5173\u95ED\u6362\u884C",
248
+ "codeBlock.wrapOn": "\u81EA\u52A8\u6362\u884C",
249
+ /* ── common ── */
250
+ "common.accept": "\u63A5\u53D7",
251
+ "common.add": "\u65B0\u589E",
252
+ "common.all": "\u5168\u90E8",
253
+ "common.approve": "\u6279\u51C6",
254
+ "common.cancel": "\u53D6\u6D88",
255
+ "common.actionFailed": "\u64CD\u4F5C\u672A\u5B8C\u6210\uFF0C\u8BF7\u91CD\u8BD5\u3002",
256
+ "common.close": "\u5173\u95ED",
257
+ "common.confirm": "\u786E\u8BA4",
258
+ "common.copied": "\u5DF2\u590D\u5236",
259
+ "common.copy": "\u590D\u5236",
260
+ "common.delete": "\u5220\u9664",
261
+ "common.enable": "\u542F\u7528",
262
+ "common.exportCsv": "\u5BFC\u51FA CSV",
263
+ "common.healthy": "\u6B63\u5E38",
264
+ "common.input": "\u8F93\u5165",
265
+ "common.loadingEllipsis": "\u52A0\u8F7D\u4E2D\u2026",
266
+ "common.official": "\u5B98\u65B9",
267
+ "common.output": "\u8F93\u51FA",
268
+ "common.page": "\u7B2C {p0} \u9875",
269
+ "common.reject": "\u62D2\u7EDD",
270
+ "common.remove": "\u79FB\u9664",
271
+ "common.requestAccess": "\u7533\u8BF7\u8BBF\u95EE",
272
+ "common.requestAccessFor": "\u7533\u8BF7\u8BBF\u95EE {p0}",
273
+ "common.restricted": "\u53D7\u9650",
274
+ "common.retry": "\u91CD\u8BD5",
275
+ "common.rows": "{p0} \u884C",
276
+ "common.run": "\u8FD0\u884C",
277
+ "common.steps": "{p0} \u6B65",
278
+ "common.submit": "\u63D0\u4EA4",
279
+ "common.unknown": "\u672A\u77E5",
280
+ /* ── contextMenu ── */
281
+ "contextMenu.back": "\u540E\u9000",
282
+ "contextMenu.copyPageLink": "\u590D\u5236\u9875\u9762\u94FE\u63A5",
283
+ "contextMenu.forward": "\u524D\u8FDB",
284
+ "contextMenu.refresh": "\u5237\u65B0",
285
+ /* ── copy ── */
286
+ "copy.copied": "\u5DF2\u590D\u5236",
287
+ "copy.failed": "\u672A\u80FD\u590D\u5236\uFF0C\u8BF7\u91CD\u8BD5\u3002",
288
+ "copy.denied": "clipboard API \u88AB\u62D2\uFF08{p0}\uFF09\u4E14\u56DE\u9000 execCommand \u5931\u8D25",
289
+ "copy.unavailable": "\u526A\u8D34\u677F\u4E0D\u53EF\u7528\uFF08\u65E0 clipboard API \u4E14 execCommand \u56DE\u9000\u5931\u8D25\uFF09",
290
+ "copy.unknown": "\u672A\u77E5\u9519\u8BEF",
291
+ /* ── datatable ── */
292
+ "datatable.clear": "\u6E05\u7A7A",
293
+ "datatable.collapseGroup": "\u6298\u53E0\u5206\u7EC4 {p0}",
294
+ "datatable.columnSettingsAria": "\u5217\u8BBE\u7F6E",
295
+ "datatable.countUnit": "{p0} \u6761",
296
+ "datatable.editHint": "\u7F16\u8F91\u300C{p0}\u300D\uFF1A\u53CC\u51FB\u6216\u6309 Enter",
297
+ "datatable.editHintWithValue": "{p0}\uFF08\u53CC\u51FB\u6216\u6309 Enter \u7F16\u8F91\u300C{p1}\u300D\uFF09",
298
+ "datatable.empty": "\u6682\u65E0\u6570\u636E",
299
+ "datatable.emptyValue": "\uFF08\u7A7A\uFF09",
300
+ "datatable.expandGroup": "\u5C55\u5F00\u5206\u7EC4 {p0}",
301
+ "datatable.expandRow": "\u5C55\u5F00\u884C",
302
+ "datatable.filterChipAria": "\u7B5B\u9009\u300C{p0}\u300D",
303
+ "datatable.pageSizeAria": "\u6BCF\u9875\u6761\u6570",
304
+ "datatable.paginationAria": "\u5206\u9875",
305
+ "datatable.reset": "\u91CD\u7F6E",
306
+ "datatable.resizeAria": "\u8C03\u6574\u300C{p0}\u300D\u5217\u5BBD",
307
+ "datatable.searchAria": "\u641C\u7D22\u8868\u683C",
308
+ "datatable.searchPlaceholder": "\u641C\u7D22\u2026",
309
+ "datatable.selectAll": "\u5168\u9009",
310
+ "datatable.selectRow": "\u9009\u62E9\u8BE5\u884C",
311
+ "datatable.selectedCount": "\u5DF2\u9009 {p0} \u9879",
312
+ "datatable.totalCount": "\u5171 {p0} \u6761",
313
+ "datatable.totalLabel": "\u5408\u8BA1",
314
+ /* ── datePicker ── */
315
+ "datePicker.noDates": "\u6CA1\u6709\u53EF\u9009\u65E5\u671F",
316
+ "datePicker.clear": "\u6E05\u9664",
317
+ "datePicker.nextMonth": "\u4E0B\u4E00\u6708",
318
+ "datePicker.prevMonth": "\u4E0A\u4E00\u6708",
319
+ "datePicker.selectDate": "\u9009\u62E9\u65E5\u671F",
320
+ "datePicker.today": "\u4ECA\u5929",
321
+ "datePicker.weekdays": ["\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D", "\u65E5"],
322
+ "datePicker.weekdaysSunFirst": ["\u65E5", "\u4E00", "\u4E8C", "\u4E09", "\u56DB", "\u4E94", "\u516D"],
323
+ "datePicker.yearMonth": "{p0} \u5E74 {p1} \u6708",
324
+ /* ── formValidate ── */
325
+ "formValidate.email": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u90AE\u7BB1\u5730\u5740",
326
+ "formValidate.invalid": "\u8F93\u5165\u4E0D\u5408\u6CD5",
327
+ "formValidate.matches": "\u4E24\u6B21\u8F93\u5165\u4E0D\u4E00\u81F4",
328
+ "formValidate.max": "\u4E0D\u80FD\u5927\u4E8E {max}",
329
+ "formValidate.maxlength": "\u6700\u591A\u8F93\u5165 {max} \u4E2A\u5B57\u7B26",
330
+ "formValidate.min": "\u4E0D\u80FD\u5C0F\u4E8E {min}",
331
+ "formValidate.minlength": "\u81F3\u5C11\u8F93\u5165 {min} \u4E2A\u5B57\u7B26",
332
+ "formValidate.number": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u6570\u5B57",
333
+ "formValidate.pattern": "\u8F93\u5165\u683C\u5F0F\u4E0D\u6B63\u786E",
334
+ "formValidate.required": "\u6B64\u9879\u4E3A\u5FC5\u586B\u9879",
335
+ "formValidate.selectPlaceholder": "\u8BF7\u9009\u62E9",
336
+ "formValidate.step": "\u6570\u503C\u5FC5\u987B\u662F {step} \u7684\u500D\u6570",
337
+ "formValidate.summary": "\u8868\u5355\u6709 {p0} \u9879\u5185\u5BB9\u9700\u8981\u4FEE\u6B63\uFF0C\u8BF7\u4FEE\u6539\u540E\u91CD\u65B0\u63D0\u4EA4",
338
+ "formValidate.tool.description": "\u5411\u7528\u6237\u6E32\u67D3\u4E00\u4E2A\u7ED3\u6784\u5316\u8868\u5355\u6536\u96C6\u8F93\u5165",
339
+ "formValidate.tool.fields": "\u5B57\u6BB5\u5217\u8868",
340
+ "formValidate.tool.help": "\u5B57\u6BB5\u4E0B\u65B9\u7684\u5E2E\u52A9\u6587\u6848",
341
+ "formValidate.tool.label": "\u5B57\u6BB5\u6807\u7B7E",
342
+ "formValidate.tool.max": "\u6700\u5927\u503C\uFF08number \u7528\uFF09",
343
+ "formValidate.tool.min": "\u6700\u5C0F\u503C\uFF08number \u7528\uFF09",
344
+ "formValidate.tool.name": "\u5B57\u6BB5\u540D\uFF08\u63D0\u4EA4\u503C\u7684 key\uFF09",
345
+ "formValidate.tool.options": "select \u9009\u9879\uFF08\u5B57\u7B26\u4E32\u6216 {label,value}\uFF09",
346
+ "formValidate.tool.pattern": "\u6B63\u5219\u7EA6\u675F\uFF08HTML pattern \u8BED\u6CD5\uFF0C\u6574\u4E32\u5339\u914D\uFF09",
347
+ "formValidate.tool.required": "\u662F\u5426\u5FC5\u586B",
348
+ "formValidate.tool.submitText": "\u63D0\u4EA4\u6309\u94AE\u6587\u6848\uFF0C\u9ED8\u8BA4\u300C\u63D0\u4EA4\u300D",
349
+ "formValidate.tool.title": "\u8868\u5355\u6807\u9898\uFF08\u53EF\u9009\uFF09",
350
+ "formValidate.tool.type": "\u63A7\u4EF6\u7C7B\u578B\uFF0C\u9ED8\u8BA4 text",
351
+ "formValidate.url": "\u8BF7\u8F93\u5165\u6709\u6548\u7684\u94FE\u63A5\u5730\u5740",
352
+ /* ── input ── */
353
+ "input.clearSearch": "\u6E05\u7A7A\u641C\u7D22",
354
+ "input.hidePassword": "\u9690\u85CF\u5BC6\u7801",
355
+ "input.otpPosition": "\u7B2C {p0} \u4F4D\uFF0C\u5171 {p1} \u4F4D",
356
+ "panel.expand": "\u5C55\u5F00",
357
+ "panel.collapse": "\u6298\u53E0",
358
+ "input.showPassword": "\u663E\u793A\u5BC6\u7801",
359
+ /* ── kbAccess ── */
360
+ "kbAccess.approveTip": "\u6279\u51C6\u8BE5\u8BBF\u95EE\u7533\u8BF7",
361
+ "kbAccess.approvedBadge": "\u5DF2\u6279\u51C6 \xB7 {p0}",
362
+ "kbAccess.approverNotePlaceholder": "\u7ED9\u8BF7\u6C42\u4EBA\u7684\u7559\u8A00\uFF08\u53EF\u9009\uFF09",
363
+ "kbAccess.daysLeft": "{p0} \u5929\u540E",
364
+ "kbAccess.defaultSubject": "\u8BE5\u5185\u5BB9",
365
+ "kbAccess.deniedHint": "\u672A\u901A\u8FC7\u2014\u2014\u53EF\u8C03\u6574\u7406\u7531\u540E\u91CD\u65B0\u7533\u8BF7",
366
+ "kbAccess.denyNoteLabel": "\u62D2\u7EDD\u7559\u8A00\uFF08\u53EF\u9009\uFF09",
367
+ "kbAccess.duration30d": "30 \u5929",
368
+ "kbAccess.duration7d": "7 \u5929",
369
+ "kbAccess.durationForever": "\u6C38\u4E45",
370
+ "kbAccess.durationLabel": "\u8BBF\u95EE\u65F6\u957F",
371
+ "kbAccess.expired": "\u5DF2\u5230\u671F",
372
+ "kbAccess.expiresAt": "{p0}\u5230\u671F",
373
+ "kbAccess.grantAsPrefix": "\u6279\u51C6\u4E3A{p0}",
374
+ "kbAccess.grantRoleLabel": "\u6279\u51C6\u89D2\u8272",
375
+ "kbAccess.hoursLeft": "{p0} \u5C0F\u65F6\u540E",
376
+ "kbAccess.minutesLeft": "{p0} \u5206\u949F\u540E",
377
+ "kbAccess.ownerLabel": "\u5185\u5BB9\u8D1F\u8D23\u4EBA",
378
+ "kbAccess.ownerPrefix": "\u8D1F\u8D23\u4EBA\uFF1A{p0}",
379
+ "kbAccess.reapply": "\u91CD\u65B0\u7533\u8BF7",
380
+ "kbAccess.reasonLabel": "\u7533\u8BF7\u7406\u7531",
381
+ "kbAccess.reasonPlaceholder": "\u8BF4\u660E\u4E1A\u52A1\u539F\u56E0\uFF08\u5BA1\u6279\u4EBA\u53EF\u89C1\uFF09",
382
+ "kbAccess.rejectTip": "\u62D2\u7EDD\u8BE5\u8BBF\u95EE\u7533\u8BF7",
383
+ "kbAccess.requestRenewal": "\u7533\u8BF7\u7EED\u671F",
384
+ "kbAccess.requestToPrefix": "\u8BF7\u6C42\u5C06\u53D1\u9001\u7ED9\uFF1A{p0}",
385
+ "kbAccess.restrictedContent": "\u53D7\u9650\u5185\u5BB9",
386
+ "kbAccess.restrictedHint": "\u6B64\u5185\u5BB9\u4EC5\u5BF9\u6388\u6743\u8005\u5F00\u653E",
387
+ "kbAccess.revokedHint": "\u8BBF\u95EE\u5DF2\u88AB\u6536\u56DE",
388
+ "kbAccess.roleLabel": "\u7533\u8BF7\u89D2\u8272",
389
+ "kbAccess.submitRequest": "\u63D0\u4EA4\u7533\u8BF7",
390
+ "kbAccess.switchIdentity": "\u5207\u6362\u8EAB\u4EFD",
391
+ "kbAccess.switchIdentityTip": "\u5207\u6362\u8EAB\u4EFD\u540E\u91CD\u65B0\u68C0\u67E5\u6743\u9650",
392
+ "kbAccess.unknownRequester": "\u672A\u77E5\u8BF7\u6C42\u4EBA",
393
+ "kbAccess.waitingApproval": "\u7B49\u5F85 {p0} \u5BA1\u6279",
394
+ /* ── kbAcl ── */
395
+ "kbAcl.breakHint": "\u505C\u6B62\u4ECE\u7236\u7EA7\u7EE7\u627F\uFF0C\u6539\u4E3A\u72EC\u7ACB\u7BA1\u7406\u672C\u9879\u6743\u9650",
396
+ "kbAcl.breakInheritance": "\u505C\u6B62\u7EE7\u627F",
397
+ "kbAcl.brokenNotice": "\u5DF2\u505C\u6B62\u7EE7\u627F\u2014\u2014\u672C\u9879\u6743\u9650\u72EC\u7ACB\u4E8E\u7236\u7EA7",
398
+ "kbAcl.detachedChildren": "{p0}\uFF1A{p1} \u4E2A\u5B50\u9879\u5DF2\u8131\u79BB",
399
+ "kbAcl.directAccess": "\u76F4\u63A5\u8BBF\u95EE",
400
+ "kbAcl.exception": "\u4F8B\u5916",
401
+ "kbAcl.expiredOn": "\u5DF2\u8FC7\u671F\uFF08{p0}\uFF09",
402
+ "kbAcl.expiresIn": "{p0} \u5230\u671F",
403
+ "kbAcl.grantAccess": "\u6388\u4E88\u8BBF\u95EE",
404
+ "kbAcl.inheritedFrom": "\u7EE7\u627F\u81EA {p0}",
405
+ "kbAcl.inheritedFromLabel": "\u7EE7\u627F\u81EA",
406
+ "kbAcl.linkAccess": "\u94FE\u63A5\u8BBF\u95EE",
407
+ "kbAcl.noDirect": "\u6682\u65E0\u76F4\u63A5\u6388\u6743",
408
+ "kbAcl.noLinks": "\u65E0\u94FE\u63A5",
409
+ "kbAcl.removeEntry": "\u79FB\u9664 {p0}",
410
+ "kbAcl.restoreHint": "\u6062\u590D\u7EE7\u627F\u5C06\u4E22\u5F03\u672C\u9879\u81EA\u5B9A\u4E49\u7684\u6743\u9650",
411
+ "kbAcl.restoreInheritance": "\u6062\u590D\u7EE7\u627F",
412
+ "kbAcl.rootNotice": "\u6839\u7EA7\u8D44\u6E90\u2014\u2014\u65E0\u7236\u7EA7\u7EE7\u627F",
413
+ "kbAcl.systemBadge": "\u7CFB\u7EDF\u7BA1\u7406",
414
+ "kbAcl.systemNote": "\u7CFB\u7EDF\u81EA\u52A8\uFF0C\u4E0D\u53EF\u624B\u5DE5\u79FB\u9664",
415
+ "kbAcl.unnamedResource": "\u672A\u547D\u540D\u8D44\u6E90",
416
+ "kbAcl.visibleToCount": "\u5BF9 {p0} \u4EBA\u53EF\u89C1",
417
+ /* ── kbAnswer ── */
418
+ "kbAnswer.chartBar": "\u67F1\u72B6",
419
+ "kbAnswer.chartLine": "\u6298\u7EBF",
420
+ "kbAnswer.chartPie": "\u997C\u56FE",
421
+ "kbAnswer.exportNoun": "\u5BFC\u51FA {p0}",
422
+ "kbAnswer.filtered": "\u5DF2\u6309\u4F60\u7684\u6570\u636E\u6743\u9650\u8FC7\u6EE4",
423
+ "kbAnswer.filteredDetail": "\u5DF2\u6309\u4F60\u7684\u6570\u636E\u6743\u9650\u8FC7\u6EE4\uFF1A{p0}",
424
+ "kbAnswer.followups": "\u8FFD\u95EE\u5EFA\u8BAE",
425
+ "kbAnswer.hitAssets": "\u547D\u4E2D\u8D44\u4EA7\uFF1A{p0}",
426
+ "kbAnswer.kindCurated": "\u4EBA\u5DE5\u7B56\u5C55",
427
+ "kbAnswer.kindFunction": "\u51FD\u6570",
428
+ "kbAnswer.kindParameterized": "\u53C2\u6570\u5316\u67E5\u8BE2",
429
+ "kbAnswer.maskedPrefix": "\u5DF2\u8131\u654F\u5217\uFF1A",
430
+ "kbAnswer.rerunTip": "\u53C2\u6570\u53EF\u6539\u91CD\u8DD1",
431
+ "kbAnswer.truncated": "\u5DF2\u622A\u65AD\uFF1A\u663E\u793A {p0}/{p1} \u884C{p2}",
432
+ "kbAnswer.verifiedBadge": "\u8BA4\u8BC1\u7B54\u6848",
433
+ /* ── kbAudit ── */
434
+ "kbAudit.actionDetail": "\u5904\u7F6E\uFF1A{p0} \xB7 {p1}",
435
+ "kbAudit.actionLabel": "\u5904\u7F6E",
436
+ "kbAudit.alertedNote": "\u5DF2\u5B9E\u65F6\u544A\u8B66\u5B89\u5168\u56E2\u961F",
437
+ "kbAudit.channelFilterAria": "\u5BA1\u8BA1\u901A\u9053\u8FC7\u6EE4",
438
+ "kbAudit.channelPermission": "\u6743\u9650\u53D8\u66F4",
439
+ "kbAudit.channelRead": "\u8BFB\u53D6",
440
+ "kbAudit.channelRequest": "\u7533\u8BF7",
441
+ "kbAudit.channelSystem": "\u7CFB\u7EDF",
442
+ "kbAudit.empty": "\u6682\u65E0\u5BA1\u8BA1\u4E8B\u4EF6",
443
+ "kbAudit.exportTip": "\u5BFC\u51FA\u5F53\u524D\u8FC7\u6EE4\u89C6\u56FE\u4E3A CSV",
444
+ "kbAudit.hygieneLabel": "\u6743\u9650\u5F02\u5473",
445
+ "kbAudit.noHygiene": "\u672A\u53D1\u73B0\u6743\u9650\u5F02\u5473",
446
+ /* ── kbCanvas ── */
447
+ "kbCanvas.splitterAria": "\u8C03\u6574\u753B\u5E03\u53CC\u680F\u6BD4\u4F8B",
448
+ "kbCanvas.splitterTip": "\u62D6\u62FD\u8C03\u6574\u6BD4\u4F8B\uFF08\u2190/\u2192 \u5FAE\u8C03\uFF0C\u53CC\u51FB\u590D\u4F4D\uFF09",
449
+ /* ── kbCheckpoint ── */
450
+ "kbCheckpoint.chatAuditNote": "\u8BA8\u8BBA\u5386\u53F2\u6709\u5BA1\u8BA1\u4EF7\u503C",
451
+ "kbCheckpoint.label": "\u68C0\u67E5\u70B9",
452
+ "kbCheckpoint.restore": "\u6062\u590D\u68C0\u67E5\u70B9",
453
+ "kbCheckpoint.scopeBoth": "\u5185\u5BB9\u4E0E\u5BF9\u8BDD",
454
+ "kbCheckpoint.scopeChat": "\u4EC5\u5BF9\u8BDD",
455
+ "kbCheckpoint.scopeContent": "\u4EC5\u5185\u5BB9\uFF08\u4FDD\u7559\u5BF9\u8BDD\uFF09",
456
+ "kbCheckpoint.scopeMissing": "\u8BE5\u68C0\u67E5\u70B9\u672A\u5305\u542B\u6B64\u6062\u590D\u8303\u56F4",
457
+ /* ── kbChunks ── */
458
+ "kbChunks.addKeyword": "+ \u5173\u952E\u8BCD",
459
+ "kbChunks.chunkAddKeywordAria": "chunk {p0} \u65B0\u589E\u5173\u952E\u8BCD",
460
+ "kbChunks.chunkContentAria": "chunk {p0} \u5185\u5BB9",
461
+ "kbChunks.chunkEnableAria": "chunk {p0} \u542F\u7528",
462
+ "kbChunks.deleteChunkAria": "\u5220\u9664 chunk {p0}",
463
+ "kbChunks.disableNote": "\u5173\u95ED\u540E\u4E0D\u5220\u9664\uFF0C\u4EC5\u6392\u9664\u51FA\u68C0\u7D22",
464
+ "kbChunks.edited": "\u5DF2\u7F16\u8F91",
465
+ "kbChunks.empty": "\u6682\u65E0 chunk",
466
+ "kbChunks.modeFull": "\u5168\u6587",
467
+ "kbChunks.modeLabel": "\u641C\u7D22\u6A21\u5F0F",
468
+ "kbChunks.modeVector": "\u8BED\u4E49",
469
+ "kbChunks.removeKeyword": "\u79FB\u9664\u5173\u952E\u8BCD {p0}",
470
+ "kbChunks.searchAria": "\u641C\u7D22 chunk",
471
+ "kbChunks.searchFullPlaceholder": "\u5168\u6587\u641C\u7D22 chunk\u2026",
472
+ "kbChunks.searchVectorPlaceholder": "\u8BED\u4E49\u641C\u7D22 chunk\u2026",
473
+ /* ── kbCitation ── */
474
+ "kbCitation.sourceN": "\u6765\u6E90 {p0}",
475
+ "kbCitation.unavailable": "\u6765\u6E90\u5DF2\u4E0D\u53EF\u7528",
476
+ /* ── kbClarify ── */
477
+ "kbClarify.parsedPrefix": "\u5DF2\u89E3\u6790\uFF1A",
478
+ /* ── kbConflict ── */
479
+ "kbConflict.owner": "\u8D23\u4EFB\u4EBA {p0}",
480
+ /* ── kbConnector ── */
481
+ "kbConnector.changeRate": "\u53D8\u66F4\u7387",
482
+ "kbConnector.crawlRate": "\u6293\u53D6\u7387",
483
+ "kbConnector.credentials": "\u51ED\u8BC1",
484
+ "kbConnector.credentialsExpired": "\u51ED\u8BC1\u8FC7\u671F",
485
+ "kbConnector.editSchedule": "\u7F16\u8F91\u8BA1\u5212",
486
+ "kbConnector.empty": "\u6682\u65E0\u8FDE\u63A5\u5668",
487
+ "kbConnector.failing": "\u5F02\u5E38",
488
+ "kbConnector.lastSync": "\u6700\u8FD1\u540C\u6B65 {p0}",
489
+ "kbConnector.neverSynced": "\u4ECE\u672A\u540C\u6B65",
490
+ "kbConnector.notConfigured": "\u672A\u914D\u7F6E",
491
+ "kbConnector.off": "\u5DF2\u505C\u7528",
492
+ "kbConnector.perDay": "{p0}/\u5929",
493
+ "kbConnector.reauth": "\u91CD\u65B0\u6388\u6743",
494
+ "kbConnector.stale": "\u505C\u6EDE",
495
+ "kbConnector.syncNow": "\u7ACB\u5373\u540C\u6B65",
496
+ "kbConnector.synced": "\u5DF2\u540C\u6B65",
497
+ /* ── kbCore ── */
498
+ "kbCore.accessDenied": "\u672A\u901A\u8FC7",
499
+ "kbCore.accessExpired": "\u5DF2\u8FC7\u671F",
500
+ "kbCore.accessExpiring": "\u5373\u5C06\u5230\u671F",
501
+ "kbCore.accessGranted": "\u5DF2\u5F00\u901A",
502
+ "kbCore.accessNotRequested": "\u672A\u7533\u8BF7",
503
+ "kbCore.accessPending": "\u5F85\u5BA1\u6279",
504
+ "kbCore.accessRequested": "\u5DF2\u63D0\u4EA4",
505
+ "kbCore.accessRevoked": "\u5DF2\u6536\u56DE",
506
+ "kbCore.auditApprove": "\u6279\u51C6",
507
+ "kbCore.auditBreakGlass": "\u5E94\u6025\u8BBF\u95EE",
508
+ "kbCore.auditDelegation": "\u59D4\u6D3E",
509
+ "kbCore.auditDeny": "\u62D2\u7EDD",
510
+ "kbCore.auditGrant": "\u6388\u4E88",
511
+ "kbCore.auditPermissionChange": "\u6743\u9650\u53D8\u66F4",
512
+ "kbCore.auditPreview": "\u9884\u89C8",
513
+ "kbCore.auditRead": "\u8BFB\u53D6",
514
+ "kbCore.auditRequest": "\u7533\u8BF7",
515
+ "kbCore.auditRevoke": "\u6536\u56DE",
516
+ "kbCore.auditSync": "\u6743\u9650\u540C\u6B65",
517
+ "kbCore.daysAgo": "{p0} \u5929\u524D",
518
+ "kbCore.hoursAgo": "{p0} \u5C0F\u65F6\u524D",
519
+ "kbCore.hygieneBroadGroup": "\u8FC7\u5BBD\u7EC4\u6388\u6743",
520
+ "kbCore.hygieneBrokenInheritance": "\u65AD\u7EE7\u627F",
521
+ "kbCore.hygieneOrgWideLink": "\u7EC4\u7EC7\u7EA7\u94FE\u63A5",
522
+ "kbCore.hygieneOrphanedOwner": "\u8D1F\u8D23\u4EBA\u7F3A\u4F4D",
523
+ "kbCore.hygieneOverexposed": "\u8FC7\u5EA6\u66B4\u9732",
524
+ "kbCore.hygieneSensitiveMismatch": "\u654F\u611F\u5EA6\u9519\u914D",
525
+ "kbCore.justNow": "\u521A\u521A",
526
+ "kbCore.levelComment": "\u53EF\u8BC4\u8BBA",
527
+ "kbCore.levelEdit": "\u53EF\u7F16\u8F91",
528
+ "kbCore.levelNone": "\u65E0\u6743\u9650",
529
+ "kbCore.levelOwner": "\u8D1F\u8D23\u4EBA",
530
+ "kbCore.levelView": "\u53EF\u67E5\u770B",
531
+ "kbCore.minutesAgo": "{p0} \u5206\u949F\u524D",
532
+ "kbCore.runCancelled": "\u5DF2\u53D6\u6D88",
533
+ "kbCore.runDone": "\u5DF2\u5B8C\u6210",
534
+ "kbCore.runFailed": "\u5931\u8D25",
535
+ "kbCore.runQueued": "\u6392\u961F\u4E2D",
536
+ "kbCore.runRunning": "\u8FDB\u884C\u4E2D",
537
+ "kbCore.runUnstarted": "\u672A\u5F00\u59CB",
538
+ "kbCore.stageAgent": "\u4EE3\u7406",
539
+ "kbCore.stageChunk": "\u5207\u7247",
540
+ "kbCore.stageEmbed": "\u5411\u91CF\u5316",
541
+ "kbCore.stageGeneration": "\u751F\u6210",
542
+ "kbCore.stageGuard": "\u62A4\u680F",
543
+ "kbCore.stageQuery": "\u67E5\u8BE2",
544
+ "kbCore.stageRerank": "\u91CD\u6392",
545
+ "kbCore.stageRetrieval": "\u68C0\u7D22",
546
+ "kbCore.stageTool": "\u5DE5\u5177",
547
+ "kbCore.typeApi": "\u63A5\u53E3",
548
+ "kbCore.typeCode": "\u4EE3\u7801",
549
+ "kbCore.typeDatabase": "\u6570\u636E\u5E93",
550
+ "kbCore.typeDocument": "\u6587\u6863",
551
+ "kbCore.typeEmail": "\u90AE\u4EF6",
552
+ "kbCore.typeImage": "\u56FE\u7247",
553
+ "kbCore.typeKb": "\u77E5\u8BC6\u5E93",
554
+ "kbCore.typeTable": "\u8868\u683C",
555
+ "kbCore.typeTicket": "\u5DE5\u5355",
556
+ "kbCore.typeWebpage": "\u7F51\u9875",
557
+ "kbCore.unnamedSource": "\u672A\u547D\u540D\u6765\u6E90",
558
+ "kbCore.unnamedSubject": "\u672A\u547D\u540D\u4E3B\u4F53",
559
+ "kbCore.visHidden": "\u4E0D\u53EF\u89C1",
560
+ "kbCore.visMetadataOnly": "\u4EC5\u5143\u6570\u636E",
561
+ "kbCore.visSummaryOnly": "\u4EC5\u6458\u8981",
562
+ "kbCore.visVisible": "\u53EF\u89C1",
563
+ /* ── kbEval ── */
564
+ "kbEval.avgA": "A \u5747\u5206 {p0}",
565
+ "kbEval.avgB": "B \u5747\u5206 {p0}",
566
+ "kbEval.compare": "\u5BF9\u6BD4",
567
+ "kbEval.runSelectAria": "\u9009\u62E9\u5BF9\u6BD4\u8FD0\u884C",
568
+ "kbEval.expected": "\u671F\u671B",
569
+ "kbEval.gradeImprovement": "\u63D0\u5347",
570
+ "kbEval.gradeRegression": "\u56DE\u5F52",
571
+ "kbEval.gradeTie": "\u6301\u5E73",
572
+ "kbEval.gradeTradeoff": "\u6743\u8861",
573
+ "kbEval.noData": "\u6CA1\u6709\u5BF9\u6BD4\u6570\u636E",
574
+ "kbEval.onlyRegression": "\u4EC5\u770B\u56DE\u5F52",
575
+ "kbEval.outputA": "\u8F93\u51FA A",
576
+ "kbEval.outputB": "\u8F93\u51FA B",
577
+ "kbEval.outputN": "\u8F93\u51FA {p0}",
578
+ /* ── kbExplain ── */
579
+ "kbExplain.empty": "\u6682\u65E0\u53E3\u5F84\u4FE1\u606F",
580
+ "kbExplain.groupAgg": "\u805A\u5408",
581
+ "kbExplain.groupColumn": "\u5217",
582
+ "kbExplain.groupFilter": "\u7B5B\u9009",
583
+ "kbExplain.groupTable": "\u8868",
584
+ "kbExplain.trigger": "\u8FD9\u4E2A\u6570\u5B57\u662F\u600E\u4E48\u7B97\u51FA\u6765\u7684",
585
+ "kbExplain.viewDefinition": "\u67E5\u770B\u53E3\u5F84",
586
+ /* ── kbFilter ── */
587
+ "kbFilter.addGroup": "+ \u7EC4",
588
+ "kbFilter.addRule": "+ \u89C4\u5219",
589
+ "kbFilter.and": "\u4E14",
590
+ "kbFilter.combineMode": "\u7EC4\u5408\u65B9\u5F0F {p0}",
591
+ "kbFilter.copyDsl": "\u590D\u5236 DSL",
592
+ "kbFilter.deleteGroup": "\u5220\u9664\u7EC4",
593
+ "kbFilter.deleteRule": "\u5220\u9664\u89C4\u5219",
594
+ "kbFilter.fieldLabel": "\u5B57\u6BB5",
595
+ "kbFilter.fieldPlaceholder": "\u8FC7\u6EE4\u5B57\u6BB5",
596
+ "kbFilter.noValueNeeded": "\uFF08\u65E0\u9700\u503C\uFF09",
597
+ "kbFilter.none": "\uFF08\u65E0\u8FC7\u6EE4\uFF09",
598
+ "kbFilter.opContains": "\u5305\u542B",
599
+ "kbFilter.opEq": "\u7B49\u4E8E",
600
+ "kbFilter.opExists": "\u5B58\u5728",
601
+ "kbFilter.opGt": "\u5927\u4E8E",
602
+ "kbFilter.opGte": "\u5927\u4E8E\u7B49\u4E8E",
603
+ "kbFilter.opIn": "\u5C5E\u4E8E",
604
+ "kbFilter.opLt": "\u5C0F\u4E8E",
605
+ "kbFilter.opLte": "\u5C0F\u4E8E\u7B49\u4E8E",
606
+ "kbFilter.opNe": "\u4E0D\u7B49\u4E8E",
607
+ "kbFilter.operatorLabel": "\u64CD\u4F5C\u7B26",
608
+ "kbFilter.or": "\u6216",
609
+ "kbFilter.toggleAndOr": "\u5207\u6362 AND / OR",
610
+ "kbFilter.valueAria": "\u8FC7\u6EE4\u503C",
611
+ "kbFilter.valueLabel": "\u503C",
612
+ "kbFilter.valuePlaceholder": "\u503C 1, \u503C 2\uFF08\u9017\u53F7\u5206\u9694\uFF09",
613
+ /* ── kbGap ── */
614
+ "kbGap.addSynonym": "\u52A0\u540C\u4E49\u8BCD",
615
+ "kbGap.colCount": "\u6B21\u6570",
616
+ "kbGap.colLastSeen": "\u6700\u8FD1\u51FA\u73B0",
617
+ "kbGap.colQuery": "\u67E5\u8BE2",
618
+ "kbGap.colShare": "\u5360\u6BD4",
619
+ "kbGap.createDoc": "\u5EFA\u6587\u6863",
620
+ "kbGap.empty": "\u6CA1\u6709\u672A\u547D\u4E2D\u67E5\u8BE2\u2014\u2014\u8986\u76D6\u826F\u597D",
621
+ "kbGap.title": "\u672A\u547D\u4E2D\u67E5\u8BE2\u8D8B\u52BF",
622
+ "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",
623
+ "kbGap.zeroClickRate": "\u96F6\u70B9\u51FB\u7387",
624
+ "kbGap.zeroClickTitle": "\u96F6\u70B9\u51FB \u2260 \u5931\u8D25",
625
+ /* ── kbHittest ── */
626
+ "kbHittest.addAria": "\u65B0\u589E\u6D4B\u8BD5\u95EE\u9898",
627
+ "kbHittest.addPlaceholder": "\u65B0\u589E\u95EE\u9898\uFF0CEnter \u6DFB\u52A0",
628
+ "kbHittest.avgScore": "\u5747\u5206",
629
+ "kbHittest.deleteQuestion": "\u5220\u9664\u95EE\u9898",
630
+ "kbHittest.empty": "\u6682\u65E0\u6D4B\u8BD5\u95EE\u9898\u2014\u2014\u6DFB\u52A0\u7B2C\u4E00\u6761\uFF0C\u5F00\u59CB\u53EC\u56DE\u56DE\u5F52",
631
+ "kbHittest.expectHit": "\u671F\u671B\u547D\u4E2D\u5728\u524D\u6392\u547D\u4E2D\u4E2D",
632
+ "kbHittest.expectMiss": "\u671F\u671B\u672A\u547D\u4E2D",
633
+ "kbHittest.hitRate": "\u547D\u4E2D\u7387",
634
+ "kbHittest.noExpectation": "\u672A\u8BBE\u671F\u671B",
635
+ "kbHittest.notRun": "\u672A\u8FD0\u884C",
636
+ "kbHittest.pass": "\u901A\u8FC7",
637
+ "kbHittest.pin": "\u7F6E\u9876\u56FA\u5B9A",
638
+ "kbHittest.runAll": "\u8FD0\u884C\u5168\u90E8",
639
+ "kbHittest.sessionOnly": "\u53C2\u6570\u4EC5\u672C\u6B21\u4F1A\u8BDD\u751F\u6548",
640
+ "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",
641
+ "kbHittest.unpin": "\u53D6\u6D88\u7F6E\u9876",
642
+ /* ── kbMetadata ── */
643
+ "kbMetadata.addField": "\u65B0\u589E\u5B57\u6BB5",
644
+ "kbMetadata.builtIn": "\u5185\u7F6E",
645
+ "kbMetadata.colField": "\u5B57\u6BB5",
646
+ "kbMetadata.colType": "\u7C7B\u578B",
647
+ "kbMetadata.colValue": "\u503C",
648
+ "kbMetadata.empty": "\u6682\u65E0\u5143\u6570\u636E\u5B57\u6BB5",
649
+ "kbMetadata.nameLabel": "\u65B0\u5B57\u6BB5\u540D",
650
+ "kbMetadata.namePlaceholder": "\u5B57\u6BB5\u540D\uFF0C\u5982 department",
651
+ "kbMetadata.removeField": "\u79FB\u9664\u5B57\u6BB5 {p0}",
652
+ "kbMetadata.typeLabel": "\u65B0\u5B57\u6BB5\u7C7B\u578B",
653
+ "kbMetadata.valueOf": "{p0} \u7684\u503C",
654
+ /* ── kbPassage ── */
655
+ "kbPassage.blockRange": "\u5757 {p0}\u2013{p1}",
656
+ "kbPassage.close": "\u5173\u95ED\u539F\u6587",
657
+ "kbPassage.expandContext": "\u5C55\u5F00\u4E0A\u4E0B\u6587",
658
+ "kbPassage.locate": "\u539F\u6587\u5B9A\u4F4D",
659
+ "kbPassage.none": "\uFF08\u65E0\u539F\u6587\uFF09",
660
+ "kbPassage.pageRange": "\u7B2C {p0}\u2013{p1} \u9875",
661
+ "kbPassage.restricted": "\u5185\u5BB9\u53D7\u9650",
662
+ "kbPassage.unitByte": "\u5B57\u8282",
663
+ "kbPassage.unitChar": "\u5B57\u7B26",
664
+ /* ── kbPipeline ── */
665
+ "kbPipeline.empty": "\u6682\u65E0\u89E3\u6790\u4EFB\u52A1",
666
+ "kbPipeline.progress": "\u89E3\u6790\u8FDB\u5EA6",
667
+ "kbPipeline.rerun": "\u91CD\u8DD1",
668
+ "kbPipeline.unknownStage": "\u672A\u77E5\u9636\u6BB5",
669
+ /* ── kbQa ── */
670
+ "kbQa.colAnswer": "\u7B54\u6848",
671
+ "kbQa.colQuestion": "\u95EE\u9898",
672
+ "kbQa.csvHint": "CSV \u6A21\u677F\uFF1Aquestion,answer\uFF08\u6BCF\u884C\u4E00\u5BF9\uFF0CUTF-8\uFF09",
673
+ "kbQa.deleteRow": "\u5220\u9664\u7B2C {p0} \u884C",
674
+ "kbQa.empty": "\u6682\u65E0\u8BA4\u8BC1\u95EE\u7B54\u5BF9",
675
+ "kbQa.importCsv": "\u5BFC\u5165 CSV",
676
+ /* ── kbRerank ── */
677
+ "kbRerank.after": "\u91CD\u6392\u540E",
678
+ "kbRerank.before": "\u91CD\u6392\u524D",
679
+ "kbRerank.compareTitle": "\u91CD\u6392\u5BF9\u6BD4",
680
+ "kbRerank.down": "\u4E0B\u964D {p0} \u4F4D",
681
+ "kbRerank.dropped": "\u843D\u9009",
682
+ "kbRerank.empty": "\u4E24\u4FA7\u5747\u4E3A\u7A7A\u2014\u2014\u8FD0\u884C\u68C0\u7D22\u540E\u518D\u5BF9\u6BD4",
683
+ "kbRerank.enableCompare": "\u542F\u7528\u91CD\u6392\u5BF9\u6BD4",
684
+ "kbRerank.newIn": "\u65B0\u8FDB",
685
+ "kbRerank.unmarked": "\u672A\u6807",
686
+ "kbRerank.up": "\u4E0A\u5347 {p0} \u4F4D",
687
+ /* ── kbRetrieval ── */
688
+ "kbRetrieval.breakdown": "\u62C6\u89E3",
689
+ "kbRetrieval.copyParams": "\u590D\u5236\u53C2\u6570 JSON",
690
+ "kbRetrieval.decreaseTopK": "\u51CF\u5C11 topK",
691
+ "kbRetrieval.empty": "\u8F93\u5165\u95EE\u9898\u5E76\u8FD0\u884C\uFF0C\u547D\u4E2D\u7ED3\u679C\u5C06\u51FA\u73B0\u5728\u8FD9\u91CC",
692
+ "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",
693
+ "kbRetrieval.hybridWeight": "\u6DF7\u5408\u6743\u91CD\uFF08\u5DE6\u8BED\u4E49\u53F3\u5173\u952E\u8BCD\uFF09",
694
+ "kbRetrieval.identityLabel": "\u68C0\u7D22\u8EAB\u4EFD",
695
+ "kbRetrieval.identityShort": "\u8EAB\u4EFD",
696
+ "kbRetrieval.increaseTopK": "\u589E\u52A0 topK",
697
+ "kbRetrieval.keywordHint": "\u5173\u952E\u8BCD\uFF1A\u5927\u5E93\u5FEB\u901F\u7CBE\u786E",
698
+ "kbRetrieval.layerLabel": "\u8FC7\u6EE4\u5C42",
699
+ "kbRetrieval.layerNone": "none\uFF1Afail-open\uFF0C\u65E0\u8EAB\u4EFD\u8FC7\u6EE4\u8FD4\u56DE\u5168\u90E8\u2014\u2014\u5371\u9669\uFF0C\u4EC5\u6F14\u793A",
700
+ "kbRetrieval.layerPost": "post-filter\uFF1A\u5206\u6570\u5DF2\u53EF\u89C2\u5BDF\u2014\u2014\u7F3A\u9677\u5C42\uFF0C\u4EC5\u6F14\u793A",
701
+ "kbRetrieval.layerPre": "pre-filter\uFF1AACL \u5728\u68C0\u7D22\u6267\u884C\u4F53\u5185\u751F\u6548\u2014\u2014\u5B89\u5168\u57FA\u7EBF",
702
+ "kbRetrieval.leakNote": "\u5206\u6BCD\u672A\u8FC7\u6EE4\u524D\u8BA1\u6570\u5C5E\u6CC4\u9732\uFF0C\u4E0D\u663E\u793A",
703
+ "kbRetrieval.modeHybrid": "\u6DF7\u5408",
704
+ "kbRetrieval.modeKeyword": "\u5173\u952E\u8BCD",
705
+ "kbRetrieval.modeLabel": "\u68C0\u7D22\u6A21\u5F0F",
706
+ "kbRetrieval.modeVector": "\u5411\u91CF",
707
+ "kbRetrieval.postFilterNotice": "post-filter\uFF1A\u5206\u6570\u5DF2\u5728\u8FC7\u6EE4\u524D\u4EA7\u751F\uFF08\u7F3A\u9677\u5C42\u6F14\u793A\uFF09",
708
+ "kbRetrieval.queryLabel": "\u68C0\u7D22\u95EE\u9898",
709
+ "kbRetrieval.queryPlaceholder": "\u68C0\u7D22\u95EE\u9898\uFF0CEnter \u8FD0\u884C",
710
+ "kbRetrieval.rerankLabel": "\u91CD\u6392",
711
+ "kbRetrieval.rerankThresholdOff": "\u9608\u503C \u5173 \xB7 \u5B58\u6D3B {p0}",
712
+ "kbRetrieval.rerankThresholdOn": "\u9608\u503C {p0} \xB7 \u5B58\u6D3B {p1}",
713
+ "kbRetrieval.running": "\u8FD0\u884C\u4E2D\u2026",
714
+ "kbRetrieval.thresholdLabel": "\u5206\u6570\u9608\u503C",
715
+ "kbRetrieval.thresholdValue": "\u9608\u503C N/A",
716
+ "kbRetrieval.vectorHint": "\u8BED\u4E49\uFF1A\u8DE8\u8BED\u8A00/\u65E0\u7CBE\u786E\u8BCD",
717
+ "kbRetrieval.vectorPercent": "\u8BED\u4E49 {p0}",
718
+ "kbRetrieval.weightValue": "\u5411\u91CF {p0}% / \u5173\u952E\u8BCD {p1}%",
719
+ /* ── kbReview ── */
720
+ "kbReview.assignee": "\u5206\u914D\u4EBA",
721
+ "kbReview.doneNext": "\u5B8C\u6210\u5E76\u4E0B\u4E00\u6761",
722
+ "kbReview.empty": "\u6CA1\u6709\u5F85\u5BA1\u67E5\u6761\u76EE",
723
+ "kbReview.emptyAssignee": "\u8BE5\u5206\u914D\u4EBA\u4E0B\u6CA1\u6709\u6761\u76EE",
724
+ "kbReview.emptyQueue": "\u961F\u5217\u4E3A\u7A7A",
725
+ "kbReview.kbCloseHint": "\u5173\u95ED\u63D0\u793A",
726
+ "kbReview.kbSelectOption": "\u9009\u62E9\u9009\u9879",
727
+ "kbReview.kbSwitchItem": "\u5207\u6362\u6761\u76EE",
728
+ "kbReview.kbToggleHint": "\u663E\u793A/\u9690\u85CF\u672C\u63D0\u793A",
729
+ "kbReview.shortcuts": "? \u5FEB\u6377\u952E",
730
+ "kbReview.typeAnswer": "\u56DE\u7B54",
731
+ "kbReview.typeMessage": "\u6D88\u606F",
732
+ "kbReview.typeTrace": "\u8F68\u8FF9",
733
+ "kbReview.unassigned": "\u672A\u5206\u914D",
734
+ /* ── kbSandbox ── */
735
+ "kbSandbox.noResolver": "\u5BBF\u4E3B\u672A\u63D0\u4F9B ui:// \u89E3\u6790\u5668",
736
+ "kbSandbox.notReady": "kb-sandbox: \u5E94\u7528\u672A\u5C31\u7EEA",
737
+ "kbSandbox.resolveFailed": "ui:// \u89E3\u6790\u5931\u8D25\uFF1A{p0}",
738
+ "kbSandbox.timeout": "kb-sandbox: \u8C03\u7528\u8D85\u65F6\uFF08{p0}ms\uFF09",
739
+ "kbSandbox.title": "MCP App \u6C99\u7BB1",
740
+ /* ── kbSegment ── */
741
+ "kbSegment.avgLength": "\u5747\u957F {p0}",
742
+ "kbSegment.childNote": "\u5B50\u5206\u6BB5\u6309\u957F\u5EA6\u81EA\u52A8\u751F\u6210",
743
+ "kbSegment.cleanBlankLines": "\u5408\u5E76\u8FDE\u7EED\u7A7A\u884C",
744
+ "kbSegment.cleanEmails": "\u53BB\u9664\u90AE\u7BB1",
745
+ "kbSegment.cleanLinks": "\u53BB\u9664\u94FE\u63A5",
746
+ "kbSegment.empty": "\u6837\u672C\u4E3A\u7A7A\uFF0C\u6682\u65E0\u53EF\u9884\u89C8\u5206\u6BB5",
747
+ "kbSegment.maxLengthLabel": "\u6700\u5927\u957F\u5EA6",
748
+ "kbSegment.maxLengthPlaceholder": "\u5206\u6BB5\u6700\u5927\u957F\u5EA6",
749
+ "kbSegment.modeAuto": "\u81EA\u52A8",
750
+ "kbSegment.modeCustom": "\u81EA\u5B9A\u4E49",
751
+ "kbSegment.modeLabel": "\u5206\u6BB5\u6A21\u5F0F",
752
+ "kbSegment.modeNote": "\u5206\u6BB5\u6A21\u5F0F\u521B\u5EFA\u540E\u4E0D\u53EF\u66F4\u6539\uFF0C\u53C2\u6570\u53EF\u968F\u65F6\u8C03\u6574",
753
+ "kbSegment.modeParentChild": "\u7236\u5B50",
754
+ "kbSegment.overlapInvalid": "\u91CD\u53E0\u9700\u5C0F\u4E8E\u6700\u5927\u957F\u5EA6",
755
+ "kbSegment.overlapLabel": "\u91CD\u53E0",
756
+ "kbSegment.overlapPlaceholder": "\u5206\u6BB5\u91CD\u53E0",
757
+ "kbSegment.previewNote": "\u5171 {p0} \u6BB5\uFF0C\u4EC5\u9884\u89C8\u524D {p1} \u6BB5",
758
+ "kbSegment.segmentsCount": "{p0} \u6BB5",
759
+ "kbSegment.separatorLabel": "\u5206\u9694\u7B26",
760
+ "kbSegment.separatorPlaceholder": "\u5206\u6BB5\u5206\u9694\u7B26",
761
+ "kbSegment.toggleTip": "\u70B9\u51FB\u5C55\u5F00/\u6536\u8D77",
762
+ /* ── kbSources ── */
763
+ "kbSources.cited": "\u88AB\u5F15 \xD7{p0}",
764
+ "kbSources.empty": "\u4E0B\u4E00\u6761\u7B54\u6848\u5F15\u7528\u7684\u6BB5\u843D\u5C06\u51FA\u73B0\u5728\u8FD9\u91CC",
765
+ /* ── kbSql ── */
766
+ "kbSql.adminOnly": "\u4EC5\u7BA1\u7406\u5458\u53EF\u89C1",
767
+ "kbSql.applyFix": "\u5E94\u7528\u4FEE\u590D\u5E76\u91CD\u8DD1",
768
+ "kbSql.confirmRerun": "\u786E\u8BA4\u5E76\u91CD\u8DD1",
769
+ "kbSql.editRerun": "\u7F16\u8F91\u91CD\u8DD1",
770
+ "kbSql.fixReady": "\u68C0\u6D4B\u5230\u4E0A\u7248\u62A5\u9519\uFF0C\u4FEE\u590D\u5EFA\u8BAE\u5DF2\u5C31\u7EEA",
771
+ "kbSql.generated": "\u751F\u6210\u7684 SQL",
772
+ /* ── kbTrace ── */
773
+ "kbTrace.empty": "\u6682\u65E0\u8C03\u7528\u94FE\u6570\u636E",
774
+ "kbTrace.hits": "\u547D\u4E2D {p0}",
775
+ /* ── kbVisibility ── */
776
+ "kbVisibility.adminOnlyNote": "\u4EE5\u4E0A\u5BF9\u7167\u4EC5\u9650 admin \u5BA1\u8BA1",
777
+ "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",
778
+ "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",
779
+ "kbVisibility.focusColumn": "\u805A\u7126\u8EAB\u4EFD {p0} \u7684\u5217",
780
+ "kbVisibility.layerValue": "\u8FC7\u6EE4\u5C42\uFF1A{p0}",
781
+ "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",
782
+ "kbVisibility.postFilterNote": "post-filter\uFF1AtopK \u88AB\u65E0\u6743\u7ED3\u679C\u5360\u7528\u4E14\u76F8\u4F3C\u5EA6\u5206\u6570\u53EF\u88AB\u89C2\u5BDF",
783
+ "kbVisibility.queryLabel": "\u67E5\u8BE2\uFF1A{p0}",
784
+ "kbVisibility.safeCount": "\u5B89\u5168\u8BA1\u6570 {p0}",
785
+ "kbVisibility.sourceUnavailable": "\u539F\u6587\u4E0D\u53EF\u7528",
786
+ "kbVisibility.unnamedHit": "\u672A\u547D\u540D\u547D\u4E2D",
787
+ /* ── kbWhoCan ── */
788
+ "kbWhoCan.check": "\u68C0\u67E5",
789
+ "kbWhoCan.checkIdentity": "\u68C0\u67E5\u8EAB\u4EFD",
790
+ "kbWhoCan.daysUntil": "{p0} \u5929\u540E",
791
+ "kbWhoCan.deniedBy": "\u88AB\u7B56\u7565\u62D2\u7EDD\uFF1A{p0}",
792
+ "kbWhoCan.denyVeto": "\u88AB Deny \u4E00\u7968\u5426\u51B3",
793
+ "kbWhoCan.direct": "\u76F4\u63A5",
794
+ "kbWhoCan.expiresSoon": "\u6700\u8FD1\u5230\u671F {p0}",
795
+ "kbWhoCan.hoursUntil": "{p0} \u5C0F\u65F6\u540E",
796
+ "kbWhoCan.inherit": "\u7EE7\u627F",
797
+ "kbWhoCan.link": "\u94FE\u63A5",
798
+ "kbWhoCan.noDeny": "\u65E0 Deny \u751F\u6548",
799
+ "kbWhoCan.noIdentity": "\u672A\u63D0\u4F9B\u53EF\u68C0\u67E5\u7684\u8EAB\u4EFD",
800
+ "kbWhoCan.none": "\u672A\u547D\u4E2D\u4EFB\u4F55\u6388\u6743\u2014\u2014\u9ED8\u8BA4\u62D2\u7EDD",
801
+ "kbWhoCan.recheckTip": "\u91CD\u65B0\u68C0\u67E5\u5F53\u524D\u8EAB\u4EFD\u7684\u6709\u6548\u6743\u9650",
802
+ "kbWhoCan.stackRule": "\u53E0\u52A0\u89C4\u5219\uFF1A\u591A\u6765\u6E90\u5E76\u96C6 \u2192 {p0}\uFF1B{p1}",
803
+ /* ── mentions ── */
804
+ "mentions.create": "\u521B\u5EFA\u201C{p0}\u201D",
805
+ "mentions.customBadge": "\u81EA\u5B9A\u4E49",
806
+ "mentions.loading": "\u52A0\u8F7D\u4E2D\u2026",
807
+ /* ── notification ── */
808
+ "notification.groupBusy": "\u64CD\u4F5C\u7ED3\u675F\u540E\u53EF\u4EE5\u6536\u8D77\u8FD9\u7EC4\u901A\u77E5\u3002",
809
+ "notification.groupUnread": "{count} \u6761\u540C\u7EC4\u901A\u77E5\uFF0C{unread} \u6761\u672A\u8BFB",
810
+ "notification.groupCount": "{count} \u6761\u540C\u7EC4\u901A\u77E5",
811
+ "notification.groupExpanded": "\u6536\u8D77\u8FD9\u7EC4\u901A\u77E5",
812
+ "notification.ariaLabel": "\u901A\u77E5",
813
+ "notification.viewDetails": "\u67E5\u770B\u8BE6\u60C5",
814
+ /* ── pagination ── */
815
+ "pagination.next": "\u4E0B\u4E00\u9875",
816
+ "pagination.pageSize": "{p0} \u6761/\u9875",
817
+ "pagination.prev": "\u4E0A\u4E00\u9875",
818
+ /* ── select ── */
819
+ "select.fieldLabel": "\u9009\u62E9\u9009\u9879",
820
+ "select.noMatch": "\u65E0\u5339\u914D\u9879",
821
+ "select.remove": "\u79FB\u9664 {p0}",
822
+ "select.searchPlaceholder": "\u641C\u7D22\u2026",
823
+ /* ── cascader / tree-select ── */
824
+ "cascader.placeholder": "\u8BF7\u9009\u62E9",
825
+ "cascader.noMatch": "\u65E0\u5339\u914D",
826
+ "cascader.search": "\u641C\u7D22",
827
+ "treeSelect.placeholder": "\u8BF7\u9009\u62E9",
828
+ "treeSelect.noMatch": "\u65E0\u5339\u914D",
829
+ "treeSelect.search": "\u641C\u7D22",
830
+ /* ── tagInput ── */
831
+ "tagInput.ariaLabel": "\u6807\u7B7E\u8F93\u5165\uFF0C\u5171 {p0} \u4E2A",
832
+ "tagInput.remove": "\u79FB\u9664 {p0}",
833
+ "tagInput.field": "\u6DFB\u52A0\u6807\u7B7E",
834
+ "tagInput.added": "\u5DF2\u6DFB\u52A0 {p0} \u4E2A\u6807\u7B7E\u3002",
835
+ "tagInput.removed": "\u5DF2\u79FB\u9664 {p0}\u3002",
836
+ "tagInput.duplicate": "\u5DF2\u6709\u300C{p0}\u300D\uFF0C\u53EF\u4EE5\u7F16\u8F91\u540E\u91CD\u8BD5\u3002",
837
+ "tagInput.limit": "\u6700\u591A {p0} \u4E2A\u6807\u7B7E\uFF0C\u8BF7\u5148\u79FB\u9664\u4E00\u4E2A\u3002",
838
+ /* ── upload ── */
839
+ "upload.remove": "\u79FB\u9664 {p0}",
840
+ "upload.tooBig": "\u6587\u4EF6\u8D85\u8FC7 {p0}",
841
+ "upload.tooMany": "\u6700\u591A {p0} \u4E2A\u6587\u4EF6",
842
+ "upload.typeNotAllowed": "\u6587\u4EF6\u7C7B\u578B\u4E0D\u652F\u6301"
843
+ };
844
+ var current = { ...ICEN_LOCALE_ZH_CN };
845
+ function t(key, vars) {
846
+ const raw = current[key] ?? ICEN_LOCALE_ZH_CN[key];
847
+ if (vars == null || Array.isArray(raw)) return raw;
848
+ return raw.replace(/\{(\w+)\}/g, (m, name) => name in vars ? String(vars[name]) : m);
849
+ }
850
+
851
+ // src/behaviors/icons.ts
852
+ var ICON_PATHS = {
853
+ 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",
854
+ 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",
855
+ 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",
856
+ 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",
857
+ 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",
858
+ 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",
859
+ 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",
860
+ 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",
861
+ 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",
862
+ "line-numbers": "M3 5h2M3 10h2M3 15h2M8 5h9M8 10h9M8 15h9",
863
+ wrap: "M3 5h14M3 10h10a3 3 0 0 1 0 6h-3M12.5 13.5 10 16l2.5 2.5",
864
+ 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",
865
+ close: "M6 6l8 8M14 6l-8 8",
866
+ check: "M4.5 10.2l3.6 3.6 7.4-7.6",
867
+ "chevron-down": "M5.5 7.75 10 12.25l4.5-4.5",
868
+ "chevron-up": "M5.5 12.25 10 7.75l4.5 4.5",
869
+ "chevron-right": "M7.75 5.5 12.25 10l-4.5 4.5",
870
+ "chevron-left": "M12.25 5.5 7.75 10l4.5 4.5",
871
+ "arrow-right": "M3.5 10h13M11 4.5l5.5 5.5-5.5 5.5",
872
+ "arrow-left": "M16.5 10h-13M9 4.5 3.5 10 9 15.5",
873
+ "arrow-up": "M10 16.5v-13M4.5 9 10 3.5 15.5 9",
874
+ "arrow-down": "M10 3.5v13M4.5 11l5.5 5.5 5.5-5.5",
875
+ plus: "M10 4v12M4 10h12",
876
+ minus: "M4 10h12",
877
+ sort: "M6 15V4M3 7l3-3 3 3M14 5v11M11 13l3 3 3-3",
878
+ search: "M13.3 13.3 17 17M14.5 8.5a6 6 0 1 1-12 0 6 6 0 0 1 12 0",
879
+ 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",
880
+ 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",
881
+ 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",
882
+ pixel: "M3 3h5v5H3ZM12 3h5v5h-5ZM3 12h5v5H3ZM12 12h5v5h-5Z",
883
+ chinese: "M3 8c3 0 5-2 7-5 2 3 4 5 7 5M4 8h12M6 8v8m8-8v8M3 16h14M8 16v-5h4v5",
884
+ soviet: "M3 17h14M5 17V8h4v9m0-12h6v12M7 8V5h2m2 3h2m-2 3h2m-2 3h2M10 5V3h4v2",
885
+ more: "M4 9.9v.2M10 9.9v.2M16 9.9v.2",
886
+ 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",
887
+ 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",
888
+ 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",
889
+ trash: "M3.5 5.5h13M7 5.5V3h6v2.5M5 5.5l.7 11.5h8.6L15 5.5M8 9v5M12 9v5",
890
+ spinner: "M10 3a7 7 0 1 1-7 7",
891
+ "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",
892
+ "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",
893
+ "circle-info": "M18 10a8 8 0 1 1-16 0 8 8 0 0 1 16 0ZM10 9v5M10 6.1v.15",
894
+ "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",
895
+ 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",
896
+ 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",
897
+ 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",
898
+ settings: "M3.5 5.5h13M3.5 10h13M3.5 14.5h13M7 4v3M12.5 8.5v3M8 13v3",
899
+ 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",
900
+ 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",
901
+ "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",
902
+ 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",
903
+ 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",
904
+ 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",
905
+ activity: "M17 10h-2.6l-2 5.8L8 4.2 6 10H3",
906
+ "trend-up": "M3.5 14.5l4.2-4.2 2.8 2.8 5.9-5.9M12.4 7.2h4.2v4.2",
907
+ "trend-down": "M3.5 5.5l4.2 4.2 2.8-2.8 5.9 5.9M12.4 12.8h4.2V8.6",
908
+ 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",
909
+ bold: "M6.5 4h4.2a3 3 0 0 1 0 6H6.5M6.5 10h5a3 3 0 0 1 0 6H6.5M6.5 4v12",
910
+ italic: "M8.5 4.5h7M4.5 15.5h7M12.5 4.5l-5 11",
911
+ underline: "M6 4v5.5a4 4 0 0 0 8 0V4M4.5 16h11",
912
+ "align-left": "M3.5 4.5h13M3.5 10h8.5M3.5 15.5h11",
913
+ "align-center": "M3.5 4.5h13M5.75 10h8.5M4.5 15.5h11",
914
+ "align-right": "M3.5 4.5h13M8 10h8.5M5.5 15.5h11",
915
+ list: "M6.5 5h10M6.5 10h10M6.5 15h10M3.5 5h.01M3.5 10h.01M3.5 15h.01",
916
+ 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",
917
+ 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",
918
+ 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",
919
+ "chart-line": "M3.5 3.5v13h13M6.5 12l2.8-2.8 2.2 2.2 4-4.4",
920
+ 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",
921
+ box: "M17 6 10 3.2 3 6v8l7 2.8 7-2.8ZM3 6l7 2.8L17 6M10 9v7.8",
922
+ filter: "M4 4.5h12l-4.4 5.1v5l-3.2-1.6V9.6Z",
923
+ columns: "M3.5 4h13v12h-13ZM8.2 4v12M12.8 4v12",
924
+ 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"
925
+ };
926
+ var custom = /* @__PURE__ */ new Map();
927
+ var escape = (s) => s.replace(/[&<>"']/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
928
+ function iconPath(name) {
929
+ return Object.hasOwn(ICON_PATHS, name) ? ICON_PATHS[name] : custom.get(name) ?? null;
930
+ }
931
+ function iconAttributes(o = {}) {
932
+ const size = Number.isFinite(o.size) && o.size > 0 ? o.size : 16;
933
+ 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" } };
934
+ }
935
+ function iconHTML(name, o = {}) {
936
+ const path = iconPath(name);
937
+ if (!path) return "";
938
+ return `<svg${Object.entries(iconAttributes(o)).map(([k, v]) => ` ${k}="${escape(v)}"`).join("")}><path d="${escape(path)}"></path></svg>`;
939
+ }
940
+ function createIcon(name, o = {}) {
941
+ if (typeof document === "undefined") return null;
942
+ const path = iconPath(name);
943
+ if (!path) return null;
944
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
945
+ for (const [key, value] of Object.entries(iconAttributes(o))) svg.setAttribute(key, value);
946
+ const line = document.createElementNS(svg.namespaceURI, "path");
947
+ line.setAttribute("d", path);
948
+ svg.append(line);
949
+ return svg;
950
+ }
951
+
952
+ // src/behaviors/svg-parse.ts
953
+ function parseSvg(source) {
954
+ if (typeof document === "undefined" || typeof DOMParser === "undefined") return null;
955
+ const trimmed = source.trimStart();
956
+ if (!trimmed.startsWith("<svg")) return null;
957
+ const src = /^<svg(?![^>]*\bxmlns[=:])/i.test(trimmed) ? trimmed.replace(/^<svg/i, '<svg xmlns="http://www.w3.org/2000/svg"') : trimmed;
958
+ let parsed;
959
+ try {
960
+ parsed = new DOMParser().parseFromString(src, "image/svg+xml");
961
+ } catch {
962
+ return null;
963
+ }
964
+ const root = parsed.documentElement;
965
+ if (!root || root.tagName.toLowerCase() !== "svg" || parsed.querySelector("parsererror")) return null;
966
+ const sanitize = (el) => {
967
+ for (const attr of Array.from(el.attributes)) {
968
+ const name = attr.name.toLowerCase();
969
+ if (name.startsWith("on")) el.removeAttribute(attr.name);
970
+ if ((name === "href" || name === "xlink:href") && attr.value.trim().toLowerCase().startsWith("javascript:")) {
971
+ el.removeAttribute(attr.name);
972
+ }
973
+ }
974
+ for (const child of Array.from(el.children)) {
975
+ const tag = child.tagName.toLowerCase();
976
+ if (tag === "script" || tag === "foreignobject") {
977
+ child.remove();
978
+ continue;
979
+ }
980
+ sanitize(child);
981
+ }
982
+ };
983
+ sanitize(root);
984
+ return document.importNode(root, true);
985
+ }
986
+
1
987
  // src/behaviors/ai-core.ts
2
- var svg = (body) => `<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">${body}</svg>`;
3
- var ICON_TERMINAL = svg('<path d="m4 17 6-6-6-6"/><path d="M12 19h8"/>');
4
- var ICON_FILE = svg(
5
- '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>'
6
- );
7
- var ICON_FILE_PEN = svg(
8
- '<path d="M12.5 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8l5 5v3.5"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M19 6.5 16.5 4"/><path d="M21.378 16.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"/>'
9
- );
10
- var ICON_FILE_PLUS = svg(
11
- '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M9 15h6"/><path d="M12 12v6"/>'
12
- );
13
- var ICON_TRASH = svg(
14
- '<path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M10 11v6"/><path d="M14 11v6"/>'
15
- );
16
- var ICON_SEARCH = svg('<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>');
17
- var ICON_FOLDER_SEARCH = svg(
18
- '<path d="M11 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v4"/><circle cx="17" cy="17" r="3"/><path d="m21 21-1.9-1.9"/>'
19
- );
20
- var ICON_SCAN_SEARCH = svg(
21
- '<path d="M3 7V5a2 2 0 0 1 2-2h2"/><path d="M17 3h2a2 2 0 0 1 2 2v2"/><path d="M21 17v2a2 2 0 0 1-2 2h-2"/><path d="M7 21H5a2 2 0 0 1-2-2v-2"/><circle cx="12" cy="12" r="3"/><path d="m16 16-1.9-1.9"/>'
22
- );
23
- var ICON_GLOBE = svg(
24
- '<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>'
25
- );
26
- var ICON_DOWNLOAD = svg(
27
- '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/><path d="M12 15V3"/>'
28
- );
29
- var ICON_BLOCKS = svg(
30
- '<rect width="7" height="7" x="4" y="4" rx="1"/><rect width="7" height="7" x="13" y="4" rx="1"/><rect width="7" height="7" x="4" y="13" rx="1"/><rect width="7" height="7" x="13" y="13" rx="1"/>'
31
- );
32
- var ICON_ZAP = svg(
33
- '<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>'
34
- );
35
- var ICON_LIST = svg(
36
- '<path d="M3 12h.01"/><path d="M3 18h.01"/><path d="M3 6h.01"/><path d="M8 12h13"/><path d="M8 18h13"/><path d="M8 6h13"/>'
37
- );
38
- var ICON_LIST_CHECKS = svg(
39
- '<path d="m3 17 2 2 4-4"/><path d="m3 7 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/>'
40
- );
41
- var ICON_BOT = svg(
42
- '<path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/>'
43
- );
44
- var ICON_CHART = svg(
45
- '<path d="M3 3v18h18"/><path d="M7 15l4-6 3 4 5-8"/>'
46
- );
47
- var ICON_FILE_TEXT = svg(
48
- '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/>'
49
- );
988
+ var ICON_TERMINAL = iconHTML("terminal");
989
+ var ICON_FILE = iconHTML("file");
990
+ var ICON_FILE_PEN = iconHTML("edit");
991
+ var ICON_FILE_PLUS = iconHTML("plus");
992
+ var ICON_TRASH = iconHTML("trash");
993
+ var ICON_SEARCH = iconHTML("search");
994
+ var ICON_FOLDER_SEARCH = iconHTML("folder");
995
+ var ICON_SCAN_SEARCH = iconHTML("search");
996
+ var ICON_GLOBE = iconHTML("globe");
997
+ var ICON_DOWNLOAD = iconHTML("download");
998
+ var ICON_BLOCKS = iconHTML("server");
999
+ var ICON_ZAP = iconHTML("sparkle");
1000
+ var ICON_LIST = iconHTML("list");
1001
+ var ICON_LIST_CHECKS = iconHTML("list");
1002
+ var ICON_BOT = iconHTML("agent");
1003
+ var ICON_CHART = iconHTML("chart-line");
1004
+ var ICON_FILE_TEXT = iconHTML("document");
50
1005
  function isObj(v) {
51
1006
  return typeof v === "object" && v !== null;
52
1007
  }
@@ -96,86 +1051,58 @@ var sum = (fn) => {
96
1051
  return (input) => fn(input) ?? "";
97
1052
  };
98
1053
  var BUILTIN_KINDS = {
99
- shell: { label: "\u547D\u4EE4", icon: ICON_TERMINAL, tint: "accent", summarize: sum(summarizeShell) },
100
- read: { label: "\u8BFB\u53D6", icon: ICON_FILE, tint: "info", summarize: sum(summarizeRead) },
101
- edit: { label: "\u7F16\u8F91", icon: ICON_FILE_PEN, tint: "warning", summarize: sum(summarizePath) },
102
- write: { label: "\u5199\u5165", icon: ICON_FILE_PLUS, tint: "warning", summarize: sum(summarizePath) },
103
- rm: { label: "\u5220\u9664", icon: ICON_TRASH, tint: "error", summarize: sum(summarizePath) },
104
- grep: { label: "\u641C\u7D22", icon: ICON_SEARCH, tint: "info", summarize: sum(summarizeGrep) },
105
- glob: { label: "\u5339\u914D", icon: ICON_FOLDER_SEARCH, tint: "info", summarize: sum((i) => pickStr(i, ["pattern", "glob", "path"])) },
106
- browser: { label: "\u6D4F\u89C8", icon: ICON_GLOBE, tint: "accent", summarize: sum((i) => pickStr(i, ["url", "uri", "target"])) },
107
- search: { label: "\u68C0\u7D22", icon: ICON_SCAN_SEARCH, tint: "accent", summarize: sum((i) => pickStr(i, ["query", "q", "pattern"])) },
108
- fetch: { label: "\u8BF7\u6C42", icon: ICON_DOWNLOAD, tint: "info", summarize: sum((i) => pickStr(i, ["url", "uri", "endpoint"])) },
1054
+ shell: { label: t("aiCore.kindShell"), icon: ICON_TERMINAL, tint: "accent", summarize: sum(summarizeShell) },
1055
+ read: { label: t("aiCore.kindRead"), icon: ICON_FILE, tint: "info", summarize: sum(summarizeRead) },
1056
+ edit: { label: t("aiCore.kindEdit"), icon: ICON_FILE_PEN, tint: "warning", summarize: sum(summarizePath) },
1057
+ write: { label: t("aiCore.kindWrite"), icon: ICON_FILE_PLUS, tint: "warning", summarize: sum(summarizePath) },
1058
+ rm: { label: t("aiCore.kindDelete"), icon: ICON_TRASH, tint: "error", summarize: sum(summarizePath) },
1059
+ grep: { label: t("aiCore.kindGrep"), icon: ICON_SEARCH, tint: "info", summarize: sum(summarizeGrep) },
1060
+ glob: { label: t("aiCore.kindGlob"), icon: ICON_FOLDER_SEARCH, tint: "info", summarize: sum((i) => pickStr(i, ["pattern", "glob", "path"])) },
1061
+ browser: { label: t("aiCore.kindBrowser"), icon: ICON_GLOBE, tint: "accent", summarize: sum((i) => pickStr(i, ["url", "uri", "target"])) },
1062
+ search: { label: t("aiCore.kindSearch"), icon: ICON_SCAN_SEARCH, tint: "accent", summarize: sum((i) => pickStr(i, ["query", "q", "pattern"])) },
1063
+ fetch: { label: t("aiCore.kindFetch"), icon: ICON_DOWNLOAD, tint: "info", summarize: sum((i) => pickStr(i, ["url", "uri", "endpoint"])) },
109
1064
  mcp: { label: "MCP", icon: ICON_BLOCKS, tint: "muted", summarize: sum((i) => pickStr(i, ["tool", "method", "name"])) },
110
- skill: { label: "\u6280\u80FD", icon: ICON_ZAP, tint: "accent", summarize: sum((i) => pickStr(i, ["skill", "name", "command"])) },
1065
+ skill: { label: t("aiCore.kindSkill"), icon: ICON_ZAP, tint: "accent", summarize: sum((i) => pickStr(i, ["skill", "name", "command"])) },
111
1066
  todo: {
112
- label: "\u5F85\u529E",
1067
+ label: t("aiCore.kindTodo"),
113
1068
  icon: ICON_LIST,
114
1069
  tint: "info",
115
1070
  summarize: sum((input) => {
116
1071
  const items = input && typeof input === "object" && Array.isArray(input.items) ? input.items : Array.isArray(input) ? input : [];
117
1072
  const done = items.filter((it) => it?.status === "done").length;
118
- return items.length > 0 ? `${done}/${items.length} \u5B8C\u6210` : "";
1073
+ return items.length > 0 ? t("aiCore.todoProgress", { p0: done, p1: items.length }) : "";
119
1074
  })
120
1075
  },
121
- plan: { label: "\u8BA1\u5212", icon: ICON_LIST_CHECKS, tint: "info" },
122
- subagent: { label: "\u5B50\u4EE3\u7406", icon: ICON_BOT, tint: "accent", summarize: sum((i) => pickStr(i, ["task", "description", "prompt", "message", "subagent_type"])) },
123
- note: { label: "\u7B14\u8BB0", icon: ICON_FILE_TEXT, tint: "muted" },
124
- chart: { label: "\u56FE\u8868", icon: ICON_CHART, tint: "info", summarize: sum((i) => pickStr(i, ["title", "name"])) }
1076
+ plan: { label: t("aiCore.kindPlan"), icon: ICON_LIST_CHECKS, tint: "info" },
1077
+ subagent: { label: t("aiCore.kindSubagent"), icon: ICON_BOT, tint: "accent", summarize: sum((i) => pickStr(i, ["task", "description", "prompt", "message", "subagent_type"])) },
1078
+ note: { label: t("aiCore.kindNote"), icon: ICON_FILE_TEXT, tint: "muted" },
1079
+ chart: { label: t("aiCore.kindChart"), icon: ICON_CHART, tint: "info", summarize: sum((i) => pickStr(i, ["title", "name"])) }
125
1080
  };
126
1081
  var kindRegistry = new Map(Object.entries(BUILTIN_KINDS));
127
1082
  function h(tag, className, text) {
1083
+ if (typeof document === "undefined") throw new Error("h() \u4EC5\u6D4F\u89C8\u5668\u73AF\u5883\u53EF\u7528\uFF08SSR \u65F6\u7531\u8C03\u7528\u65B9\u5B88\u536B\uFF0C\u89C1\u6A21\u5757\u5934\u6CE8\u91CA\uFF09");
128
1084
  const node = document.createElement(tag);
129
1085
  if (className) node.className = className;
130
1086
  if (text != null) node.textContent = text;
131
1087
  return node;
132
1088
  }
133
- function svgIcon(svg2) {
134
- if (typeof document === "undefined" || typeof DOMParser === "undefined") return null;
135
- let parsed;
136
- try {
137
- parsed = new DOMParser().parseFromString(svg2, "image/svg+xml");
138
- } catch {
139
- return null;
140
- }
141
- const root = parsed.documentElement;
142
- if (!root || root.tagName.toLowerCase() !== "svg" || parsed.querySelector("parsererror")) {
143
- return null;
144
- }
145
- const sanitize = (el) => {
146
- for (const attr of Array.from(el.attributes)) {
147
- const name = attr.name.toLowerCase();
148
- if (name.startsWith("on")) el.removeAttribute(attr.name);
149
- if ((name === "href" || name === "xlink:href") && attr.value.trim().toLowerCase().startsWith("javascript:")) {
150
- el.removeAttribute(attr.name);
151
- }
152
- }
153
- for (const child of Array.from(el.children)) {
154
- const tag = child.tagName.toLowerCase();
155
- if (tag === "script" || tag === "foreignobject") {
156
- child.remove();
157
- continue;
158
- }
159
- sanitize(child);
160
- }
161
- };
162
- sanitize(root);
163
- return document.importNode(root, true);
1089
+ function svgIcon(svg) {
1090
+ return parseSvg(svg);
164
1091
  }
165
1092
 
166
1093
  // src/behaviors/kb-core.ts
167
1094
  var ic = (label, d) => ({ label, icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">${d}</svg>` });
168
1095
  var BUILTIN_SOURCE_TYPES = {
169
- doc: ic("\u6587\u6863", '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/>'),
170
- web: ic("\u7F51\u9875", '<circle cx="12" cy="12" r="10"/><path d="M2 12h20"/><path d="M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20z"/>'),
171
- wiki: ic("\u77E5\u8BC6\u5E93", '<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>'),
172
- sheet: ic("\u8868\u683C", '<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M3 15h18M9 3v18M15 3v18"/>'),
173
- db: ic("\u6570\u636E\u5E93", '<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14c0 1.7 4 3 9 3s9-1.3 9-3V5"/><path d="M3 12c0 1.7 4 3 9 3s9-1.3 9-3"/>'),
174
- ticket: ic("\u5DE5\u5355", '<path d="M3 9V7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v2a2 2 0 0 0 0 6v2a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-2a2 2 0 0 0 0-6z"/><path d="M13 5v14"/>'),
175
- code: ic("\u4EE3\u7801", '<path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/>'),
176
- api: ic("\u63A5\u53E3", '<path d="M4 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 0 0 4v3a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-3a2 2 0 0 0 0-4z"/>'),
177
- image: ic("\u56FE\u7247", '<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-4.6-4.6a2 2 0 0 0-2.8 0L3 21"/>'),
178
- mail: ic("\u90AE\u4EF6", '<rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 7L2 7"/>')
1096
+ doc: ic(t("kbCore.typeDocument"), '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/>'),
1097
+ web: ic(t("kbCore.typeWebpage"), '<circle cx="12" cy="12" r="10"/><path d="M2 12h20"/><path d="M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20z"/>'),
1098
+ wiki: ic(t("kbCore.typeKb"), '<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>'),
1099
+ sheet: ic(t("kbCore.typeTable"), '<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M3 15h18M9 3v18M15 3v18"/>'),
1100
+ db: ic(t("kbCore.typeDatabase"), '<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14c0 1.7 4 3 9 3s9-1.3 9-3V5"/><path d="M3 12c0 1.7 4 3 9 3s9-1.3 9-3"/>'),
1101
+ ticket: ic(t("kbCore.typeTicket"), '<path d="M3 9V7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v2a2 2 0 0 0 0 6v2a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-2a2 2 0 0 0 0-6z"/><path d="M13 5v14"/>'),
1102
+ code: ic(t("kbCore.typeCode"), '<path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/>'),
1103
+ api: ic(t("kbCore.typeApi"), '<path d="M4 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 0 0 4v3a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-3a2 2 0 0 0 0-4z"/>'),
1104
+ image: ic(t("kbCore.typeImage"), '<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-4.6-4.6a2 2 0 0 0-2.8 0L3 21"/>'),
1105
+ mail: ic(t("kbCore.typeEmail"), '<rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 7L2 7"/>')
179
1106
  };
180
1107
  var sourceTypeRegistry = new Map(Object.entries(BUILTIN_SOURCE_TYPES));
181
1108
  var CITE_RE = /\[(\d{1,3})\]/g;
@@ -193,6 +1120,14 @@ function parseInlineCitations(text) {
193
1120
  if (last < text.length) out.push({ text: text.slice(last) });
194
1121
  return out;
195
1122
  }
1123
+ var RUN_LABELS = {
1124
+ unstart: t("kbCore.runUnstarted"),
1125
+ queued: t("kbCore.runQueued"),
1126
+ running: t("kbCore.runRunning"),
1127
+ cancel: t("kbCore.runCancelled"),
1128
+ done: t("kbCore.runDone"),
1129
+ fail: t("kbCore.runFailed")
1130
+ };
196
1131
  function normalizeQueryResult(raw) {
197
1132
  const columns = Array.isArray(raw.columns) ? raw.columns.map((c) => typeof c === "string" ? { name: c } : { name: String(c.name ?? ""), type: c.type }) : [];
198
1133
  return {
@@ -206,19 +1141,23 @@ function normalizeQueryResult(raw) {
206
1141
  exportFormats: Array.isArray(raw.exportFormats) ? raw.exportFormats.map(String) : void 0
207
1142
  };
208
1143
  }
1144
+ var SPAN_LABELS = {
1145
+ retrieval: t("kbCore.stageRetrieval"),
1146
+ generation: t("kbCore.stageGeneration"),
1147
+ rerank: t("kbCore.stageRerank"),
1148
+ embed: t("kbCore.stageEmbed"),
1149
+ tool: t("kbCore.stageTool"),
1150
+ guard: t("kbCore.stageGuard"),
1151
+ chunk: t("kbCore.stageChunk"),
1152
+ query: t("kbCore.stageQuery"),
1153
+ agent: t("kbCore.stageAgent")
1154
+ };
209
1155
  function formatCount(n) {
210
1156
  if (n == null || !Number.isFinite(n)) return "\u2014";
211
1157
  if (Math.abs(n) >= 1e4) return `${(n / 1e3).toFixed(1)}k`;
212
1158
  return n.toLocaleString("en-US");
213
1159
  }
214
1160
 
215
- // src/behaviors/events.ts
216
- function emitIcen(source, type, detail) {
217
- return source.dispatchEvent(
218
- new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
219
- );
220
- }
221
-
222
1161
  // src/behaviors/kb-clarify.ts
223
1162
  var ICON_HELP = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.1 9a3 3 0 0 1 5.8 1c0 2-3 2.4-3 4"/><path d="M12 17.5h.01"/></svg>';
224
1163
  function toClarification(raw) {
@@ -235,44 +1174,44 @@ function renderKbClarify(el, c, opts) {
235
1174
  if (typeof document === "undefined") return el;
236
1175
  const clar = toClarification(c);
237
1176
  el.textContent = "";
238
- const root = h("div", "kb-clarify");
239
- if (clar.id) root.dataset.clarifyId = clar.id;
240
- const q = h("div", "kb-clarify-q");
241
- const iconBox = h("span", "kb-clarify-qicon");
242
- const svg2 = svgIcon(ICON_HELP);
243
- if (svg2) iconBox.appendChild(svg2);
1177
+ const root = h("div", "icen-kb-clarify");
1178
+ if (clar.id) root.dataset.icenClarifyId = clar.id;
1179
+ const q = h("div", "icen-kb-clarify-q");
1180
+ const iconBox = h("span", "icen-kb-clarify-qicon");
1181
+ const svg = svgIcon(ICON_HELP);
1182
+ if (svg) iconBox.appendChild(svg);
244
1183
  q.appendChild(iconBox);
245
- q.appendChild(h("span", "kb-clarify-qtext", clar.question));
1184
+ q.appendChild(h("span", "icen-kb-clarify-qtext", clar.question));
246
1185
  root.appendChild(q);
247
- const optsWrap = h("div", "kb-clarify-opts");
248
- const resolved = h("div", "kb-clarify-resolved");
1186
+ const optsWrap = h("div", "icen-kb-clarify-opts");
1187
+ const resolved = h("div", "icen-kb-clarify-resolved");
249
1188
  resolved.hidden = true;
250
1189
  const pick = (btn) => {
251
1190
  if (root.classList.contains("is-answered")) return;
252
1191
  root.classList.add("is-answered");
253
1192
  btn.classList.add("is-picked");
254
- Array.from(optsWrap.querySelectorAll(".kb-clarify-opt")).forEach((b) => {
1193
+ Array.from(optsWrap.querySelectorAll(".icen-kb-clarify-opt")).forEach((b) => {
255
1194
  if (b !== btn) b.disabled = true;
256
1195
  });
257
1196
  resolved.textContent = "";
258
- resolved.appendChild(document.createTextNode("\u5DF2\u89E3\u6790\uFF1A"));
259
- resolved.appendChild(h("b", "kb-clarify-picked", btn.dataset.label ?? ""));
1197
+ resolved.appendChild(document.createTextNode(t("kbClarify.parsedPrefix")));
1198
+ resolved.appendChild(h("b", "icen-kb-clarify-picked", btn.dataset.icenLabel ?? ""));
260
1199
  resolved.hidden = false;
261
- const answer = { id: clar.id, value: btn.dataset.value ?? "", label: btn.dataset.label ?? "" };
1200
+ const answer = { id: clar.id, value: btn.dataset.icenValue ?? "", label: btn.dataset.icenLabel ?? "" };
262
1201
  emitIcen(btn, "icen:kb-clarify-answer", answer);
263
1202
  opts?.onAnswer?.(answer);
264
1203
  };
265
1204
  for (const o of clar.options) {
266
- const btn = h("button", "kb-clarify-opt", o.label);
1205
+ const btn = h("button", "icen-kb-clarify-opt", o.label);
267
1206
  btn.type = "button";
268
- btn.dataset.value = o.value;
269
- btn.dataset.label = o.label;
1207
+ btn.dataset.icenValue = o.value;
1208
+ btn.dataset.icenLabel = o.label;
270
1209
  btn.addEventListener("click", () => pick(btn));
271
1210
  optsWrap.appendChild(btn);
272
1211
  }
273
1212
  if (optsWrap.children.length > 0) root.appendChild(optsWrap);
274
1213
  root.appendChild(resolved);
275
- if (clar.reason) root.appendChild(h("div", "kb-clarify-reason", clar.reason));
1214
+ if (clar.reason) root.appendChild(h("div", "icen-kb-clarify-reason", clar.reason));
276
1215
  el.appendChild(root);
277
1216
  return el;
278
1217
  }
@@ -299,46 +1238,46 @@ function toExplainFact(raw) {
299
1238
  }
300
1239
  function renderGroup(label, items) {
301
1240
  if (!items?.length) return null;
302
- const group = h("div", "kb-explain-group");
303
- group.appendChild(h("span", "kb-explain-group-label", label));
304
- for (const item of items) group.appendChild(h("span", "kb-chip", item));
1241
+ const group = h("div", "icen-kb-explain-group");
1242
+ group.appendChild(h("span", "icen-kb-explain-group-label", label));
1243
+ for (const item of items) group.appendChild(h("span", "icen-kb-chip", item));
305
1244
  return group;
306
1245
  }
307
1246
  function renderKbExplain(el, fact) {
308
1247
  if (typeof document === "undefined") return el;
309
1248
  const f = toExplainFact(fact);
310
1249
  el.textContent = "";
311
- const root = h("div", "kb-explain");
312
- root.dataset.kbExplain = "";
1250
+ const root = h("div", "icen-kb-explain");
1251
+ root.dataset.icenKbExplain = "";
313
1252
  const count = (f.tables?.length ?? 0) + (f.columns?.length ?? 0) + (f.filters?.length ?? 0) + (f.aggregates?.length ?? 0) + (f.metrics?.length ?? 0);
314
- const head = h("button", "kb-fold-head kb-explain-head");
1253
+ const head = h("button", "icen-kb-fold-head icen-kb-explain-head");
315
1254
  head.type = "button";
316
1255
  head.setAttribute("aria-expanded", "false");
317
- head.appendChild(h("span", "kb-fold-caret", "\u25B8"));
318
- head.appendChild(h("span", "kb-explain-title", "\u8FD9\u4E2A\u6570\u5B57\u662F\u600E\u4E48\u7B97\u51FA\u6765\u7684"));
1256
+ head.appendChild(h("span", "icen-kb-fold-caret", "\u25B8"));
1257
+ head.appendChild(h("span", "icen-kb-explain-title", t("kbExplain.trigger")));
319
1258
  if (count > 0) {
320
- const countBox = h("span", "kb-num kb-explain-count", String(count));
321
- countBox.classList.add("kb-fold-count");
1259
+ const countBox = h("span", "icen-kb-num icen-kb-explain-count", String(count));
1260
+ countBox.classList.add("icen-kb-fold-count");
322
1261
  head.appendChild(countBox);
323
1262
  }
324
1263
  root.appendChild(head);
325
- const body = h("div", "kb-explain-body");
1264
+ const body = h("div", "icen-kb-explain-body");
326
1265
  body.hidden = true;
327
1266
  const groups = [
328
- renderGroup("\u8868", f.tables),
329
- renderGroup("\u5217", f.columns),
330
- renderGroup("\u7B5B\u9009", f.filters),
331
- renderGroup("\u805A\u5408", f.aggregates)
1267
+ renderGroup(t("kbExplain.groupTable"), f.tables),
1268
+ renderGroup(t("kbExplain.groupColumn"), f.columns),
1269
+ renderGroup(t("kbExplain.groupFilter"), f.filters),
1270
+ renderGroup(t("kbExplain.groupAgg"), f.aggregates)
332
1271
  ];
333
1272
  for (const g of groups) if (g) body.appendChild(g);
334
1273
  if (f.metrics?.length) {
335
- const metrics = h("div", "kb-explain-metrics");
1274
+ const metrics = h("div", "icen-kb-explain-metrics");
336
1275
  for (const m of f.metrics) {
337
- const row = h("div", "kb-explain-metric");
338
- row.appendChild(h("span", "kb-explain-metric-name", m.name));
1276
+ const row = h("div", "icen-kb-explain-metric");
1277
+ row.appendChild(h("span", "icen-kb-explain-metric-name", m.name));
339
1278
  if (m.ref) {
340
1279
  if (!/^\s*(javascript|data|vbscript)\s*:/i.test(m.ref)) {
341
- const a = h("a", "kb-explain-metric-ref", "\u67E5\u770B\u53E3\u5F84");
1280
+ const a = h("a", "icen-kb-explain-metric-ref", t("kbExplain.viewDefinition"));
342
1281
  a.href = m.ref;
343
1282
  a.rel = "noopener";
344
1283
  row.appendChild(a);
@@ -349,7 +1288,7 @@ function renderKbExplain(el, fact) {
349
1288
  body.appendChild(metrics);
350
1289
  }
351
1290
  if (body.children.length === 0) {
352
- body.appendChild(h("div", "kb-empty", "\u6682\u65E0\u53E3\u5F84\u4FE1\u606F"));
1291
+ body.appendChild(h("div", "icen-kb-empty", t("kbExplain.empty")));
353
1292
  }
354
1293
  root.appendChild(body);
355
1294
  head.addEventListener("click", () => {
@@ -370,16 +1309,16 @@ function toneVar(tone = "accent") {
370
1309
  if (registered) return registered;
371
1310
  switch (tone) {
372
1311
  case "success":
373
- return "var(--token-success)";
1312
+ return "var(--icen-token-success)";
374
1313
  case "warning":
375
- return "var(--token-warning)";
1314
+ return "var(--icen-token-warning)";
376
1315
  case "error":
377
- return "var(--token-error)";
1316
+ return "var(--icen-token-error)";
378
1317
  case "muted":
379
- return "var(--token-text-faint)";
1318
+ return "var(--icen-token-text-faint)";
380
1319
  case "accent":
381
1320
  default:
382
- return "var(--token-accent)";
1321
+ return "var(--icen-token-accent)";
383
1322
  }
384
1323
  }
385
1324
  function fmt(opts, value) {
@@ -390,13 +1329,66 @@ function maxOf(values) {
390
1329
  for (const v of values) if (v > max) max = v;
391
1330
  return max;
392
1331
  }
393
- function sampleLabels(labels, max = 7) {
394
- if (labels.length <= max) return labels;
395
- const out = [];
396
- for (let i = 0; i < max; i++) {
397
- out.push(labels[Math.round(i * (labels.length - 1) / (max - 1))] ?? "");
1332
+ function pointLabelsRow(labels, width, pad) {
1333
+ const row = document.createElement("div");
1334
+ row.className = "icen-chart-labels icen-chart-labels--points";
1335
+ const count = Math.min(labels.length, Math.max(2, Math.min(7, Math.floor(width / 64))));
1336
+ const step = count > 1 ? (width - 2 * pad) / (count - 1) : width;
1337
+ for (let tick = 0; tick < count; tick++) {
1338
+ const index = count <= 1 ? 0 : Math.round(tick * (labels.length - 1) / (count - 1));
1339
+ const x = labels.length <= 1 ? width / 2 : pad + index / (labels.length - 1) * (width - 2 * pad);
1340
+ const span = document.createElement("span");
1341
+ span.textContent = labels[index] ?? "";
1342
+ span.title = span.textContent;
1343
+ span.dir = "auto";
1344
+ span.dataset.icenChartIndex = String(index);
1345
+ span.style.left = `${x / width * 100}%`;
1346
+ span.style.width = `${Math.max(0, Math.min(step * 0.9, 2 * x, 2 * (width - x)))}px`;
1347
+ row.appendChild(span);
398
1348
  }
399
- return out;
1349
+ return row;
1350
+ }
1351
+ function clampNum(v, lo, hi) {
1352
+ return Math.min(hi, Math.max(lo, v));
1353
+ }
1354
+ function lttb(points, threshold, getX, getY) {
1355
+ const len = points.length;
1356
+ const n = Math.floor(threshold);
1357
+ if (!Number.isFinite(n) || n < 3 || n >= len || len <= 3) return points;
1358
+ const sampled = new Array(n);
1359
+ sampled[0] = points[0];
1360
+ sampled[n - 1] = points[len - 1];
1361
+ const every = (len - 2) / (n - 2);
1362
+ let a = 0;
1363
+ for (let i = 0; i < n - 2; i++) {
1364
+ const avgStart = Math.floor((i + 1) * every) + 1;
1365
+ const avgEnd = Math.min(Math.floor((i + 2) * every) + 1, len);
1366
+ let avgX = 0;
1367
+ let avgY = 0;
1368
+ for (let j = avgStart; j < avgEnd; j++) {
1369
+ avgX += getX(points[j]);
1370
+ avgY += getY(points[j]);
1371
+ }
1372
+ const avgLen = Math.max(1, avgEnd - avgStart);
1373
+ avgX /= avgLen;
1374
+ avgY /= avgLen;
1375
+ const rangeStart = Math.floor(i * every) + 1;
1376
+ const rangeEnd = Math.min(Math.floor((i + 1) * every) + 1, len);
1377
+ const ax = getX(points[a]);
1378
+ const ay = getY(points[a]);
1379
+ let maxArea = -1;
1380
+ let nextA = rangeStart;
1381
+ for (let j = rangeStart; j < rangeEnd; j++) {
1382
+ const area = Math.abs((ax - avgX) * (getY(points[j]) - ay) - (ax - getX(points[j])) * (avgY - ay)) * 0.5;
1383
+ if (area > maxArea) {
1384
+ maxArea = area;
1385
+ nextA = j;
1386
+ }
1387
+ }
1388
+ sampled[i + 1] = points[nextA];
1389
+ a = nextA;
1390
+ }
1391
+ return sampled;
400
1392
  }
401
1393
  function svgEl(tag, attrs) {
402
1394
  const el = document.createElementNS(SVG_NS, tag);
@@ -405,28 +1397,28 @@ function svgEl(tag, attrs) {
405
1397
  }
406
1398
  function renderEmpty(el, label) {
407
1399
  const box = document.createElement("div");
408
- box.className = "chart-empty";
409
- box.textContent = label ?? "\u6682\u65E0\u6570\u636E";
1400
+ box.className = "icen-chart-empty";
1401
+ box.textContent = label ?? t("charts.noData");
410
1402
  el.appendChild(box);
411
1403
  }
412
1404
  function legendItem(label, valueText, tone) {
413
1405
  const row = document.createElement("div");
414
- row.className = "chart-legend-item";
1406
+ row.className = "icen-chart-legend-item";
415
1407
  const dot = document.createElement("span");
416
- dot.className = "chart-legend-dot";
1408
+ dot.className = "icen-chart-legend-dot";
417
1409
  if (tone) dot.style.setProperty("--chart-tone", toneVar(tone));
418
1410
  const name = document.createElement("span");
419
- name.className = "chart-legend-label";
1411
+ name.className = "icen-chart-legend-label";
420
1412
  name.textContent = label;
421
1413
  const val = document.createElement("span");
422
- val.className = "chart-legend-value";
1414
+ val.className = "icen-chart-legend-value";
423
1415
  val.textContent = valueText;
424
1416
  row.append(dot, name, val);
425
1417
  return row;
426
1418
  }
427
1419
  function labelsRow(labels) {
428
1420
  const row = document.createElement("div");
429
- row.className = "chart-labels";
1421
+ row.className = "icen-chart-labels";
430
1422
  for (const label of labels) {
431
1423
  const span = document.createElement("span");
432
1424
  span.textContent = label;
@@ -441,85 +1433,90 @@ function normalizeSeriesInput(opts) {
441
1433
  function toneOf(tone, seriesIndex) {
442
1434
  if (typeof tone === "string") return toneVar(tone);
443
1435
  if (typeof tone === "number") return paletteVar(tone);
444
- return seriesIndex > 0 ? paletteVar((seriesIndex - 1) % 6 + 1) : void 0;
1436
+ return seriesIndex > 0 ? paletteVar(seriesIndex % 6 + 1) : void 0;
445
1437
  }
446
1438
  var PALETTE_VARS = [
447
- "var(--token-accent)",
448
- "var(--token-accent-cold)",
449
- "var(--token-success)",
450
- "var(--token-warning)",
451
- "var(--token-info)",
452
- "color-mix(in srgb, var(--token-text) 50%, transparent)"
1439
+ "var(--icen-token-accent)",
1440
+ "var(--icen-token-accent-cold)",
1441
+ "var(--icen-token-success)",
1442
+ "var(--icen-token-warning)",
1443
+ "var(--icen-token-info)",
1444
+ "color-mix(in srgb, var(--icen-token-text) 50%, transparent)"
453
1445
  ];
454
1446
  function paletteVar(index) {
455
1447
  const i = ((Math.floor(index) - 1) % 6 + 6) % 6;
456
1448
  return PALETTE_VARS[i];
457
1449
  }
458
1450
  function setMarkData(node, data) {
459
- node.setAttribute("data-chart-mark", "1");
460
- if (data.index != null) node.setAttribute("data-chart-index", String(data.index));
461
- if (data.seriesIndex != null) node.setAttribute("data-chart-series", String(data.seriesIndex));
462
- if (data.seriesName != null) node.setAttribute("data-chart-series-name", String(data.seriesName));
463
- if (data.label != null) node.setAttribute("data-chart-label", String(data.label));
464
- if (data.value != null) node.setAttribute("data-chart-value", String(data.value));
1451
+ node.setAttribute("data-icen-chart-mark", "1");
1452
+ if (data.index != null) node.setAttribute("data-icen-chart-index", String(data.index));
1453
+ if (data.seriesIndex != null) node.setAttribute("data-icen-chart-series", String(data.seriesIndex));
1454
+ if (data.seriesName != null) node.setAttribute("data-icen-chart-series-name", String(data.seriesName));
1455
+ if (data.label != null) node.setAttribute("data-icen-chart-label", String(data.label));
1456
+ if (data.value != null) node.setAttribute("data-icen-chart-value", String(data.value));
465
1457
  }
466
1458
  function copyMarkData(target, source) {
467
1459
  for (const attr of Array.from(source.attributes)) {
468
- if (attr.name.startsWith("data-chart-")) target.setAttribute(attr.name, attr.value);
1460
+ if (attr.name.startsWith("data-icen-chart-")) target.setAttribute(attr.name, attr.value);
469
1461
  }
470
1462
  }
471
- 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>';
472
- 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>';
473
- function parseChartSvg(svg2) {
474
- try {
475
- const doc = new DOMParser().parseFromString(svg2, "image/svg+xml");
476
- const el = doc.documentElement;
477
- return el && el.tagName.toLowerCase() === "svg" && !doc.querySelector("parsererror") ? document.importNode(el, true) : null;
478
- } catch {
479
- return null;
480
- }
481
- }
482
- function withChrome(el, opts, hasLegend, paint, state) {
483
- if (el.dataset.chartBody != null) {
1463
+ function withChrome(el, opts, hasLegend, paint, state, extras) {
1464
+ if (el.dataset.icenChartBody != null) {
484
1465
  el.textContent = "";
485
1466
  paint(el);
486
1467
  return;
487
1468
  }
488
1469
  el.textContent = "";
489
1470
  const body = document.createElement("div");
490
- body.className = "chart-body";
491
- body.dataset.chartBody = "";
492
- if (!opts.title && !hasLegend) {
1471
+ body.className = "icen-chart-body";
1472
+ body.dataset.icenChartBody = "";
1473
+ const note = extras?.note ?? null;
1474
+ const onZoomReset = extras?.onZoomReset ?? null;
1475
+ if (!opts.title && !hasLegend && note == null && !onZoomReset) {
493
1476
  el.appendChild(body);
494
1477
  paint(body);
495
1478
  return;
496
1479
  }
497
1480
  let hidden = state && state.hidden != null ? state.hidden : opts.legend === false;
498
1481
  if (state) state.hidden = hidden;
499
- el.setAttribute("data-legend-hidden", String(hidden));
1482
+ el.setAttribute("data-icen-legend-hidden", String(hidden));
500
1483
  const head = document.createElement("div");
501
- head.className = "chart-head";
1484
+ head.className = "icen-chart-head";
502
1485
  const title = document.createElement("div");
503
- title.className = "chart-title";
1486
+ title.className = "icen-chart-title";
504
1487
  title.textContent = opts.title ?? "";
505
1488
  if (!opts.title) title.setAttribute("aria-hidden", "true");
1489
+ if (note != null) {
1490
+ const noteEl = document.createElement("span");
1491
+ noteEl.className = "icen-chart-note";
1492
+ noteEl.textContent = note;
1493
+ head.appendChild(noteEl);
1494
+ }
1495
+ if (onZoomReset) {
1496
+ const chip = document.createElement("button");
1497
+ chip.type = "button";
1498
+ chip.className = "icen-chart-zoom-reset";
1499
+ chip.textContent = t("charts.resetZoom");
1500
+ chip.addEventListener("click", onZoomReset);
1501
+ head.appendChild(chip);
1502
+ }
506
1503
  const eye = document.createElement("button");
507
1504
  eye.type = "button";
508
- eye.className = "chart-legend-eye";
509
- eye.dataset.chartLegendEye = "";
1505
+ eye.className = "icen-chart-legend-eye";
1506
+ eye.dataset.icenChartLegendEye = "";
510
1507
  const syncEye = () => {
511
- const nowHidden = el.getAttribute("data-legend-hidden") === "true";
1508
+ const nowHidden = el.getAttribute("data-icen-legend-hidden") === "true";
512
1509
  eye.setAttribute("aria-pressed", String(!nowHidden));
513
- eye.setAttribute("aria-label", nowHidden ? "\u663E\u793A\u56FE\u4F8B" : "\u9690\u85CF\u56FE\u4F8B");
514
- eye.setAttribute("title", nowHidden ? "\u663E\u793A\u56FE\u4F8B" : "\u9690\u85CF\u56FE\u4F8B");
515
- const icon = parseChartSvg(nowHidden ? CHART_EYE_OFF_SVG : CHART_EYE_SVG);
1510
+ eye.setAttribute("aria-label", nowHidden ? t("charts.showLegend") : t("charts.hideLegend"));
1511
+ eye.setAttribute("title", nowHidden ? t("charts.showLegend") : t("charts.hideLegend"));
1512
+ const icon = createIcon(nowHidden ? "eye-off" : "eye");
516
1513
  if (icon) eye.replaceChildren(icon);
517
1514
  };
518
1515
  eye.addEventListener("click", () => {
519
- const nowHidden = el.getAttribute("data-legend-hidden") === "true";
1516
+ const nowHidden = el.getAttribute("data-icen-legend-hidden") === "true";
520
1517
  hidden = !nowHidden;
521
1518
  if (state) state.hidden = hidden;
522
- el.setAttribute("data-legend-hidden", String(hidden));
1519
+ el.setAttribute("data-icen-legend-hidden", String(hidden));
523
1520
  syncEye();
524
1521
  emitIcen(el, "icen:chart-legend-visibility", { visible: hidden ? false : true });
525
1522
  });
@@ -528,23 +1525,30 @@ function withChrome(el, opts, hasLegend, paint, state) {
528
1525
  el.append(head, body);
529
1526
  paint(body);
530
1527
  }
1528
+ var hiddenLegendKeys = /* @__PURE__ */ new WeakMap();
531
1529
  function toggleLegend(items, scope) {
532
1530
  const legend = document.createElement("div");
533
- legend.className = "chart-legend chart-legend--toggle";
1531
+ legend.className = "icen-chart-legend icen-chart-legend--toggle";
1532
+ const owner = scope instanceof Element ? scope.closest("[data-icen-chart-body]")?.parentElement ?? scope : null;
1533
+ const hidden = owner ? hiddenLegendKeys.get(owner) ?? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set();
1534
+ if (owner) hiddenLegendKeys.set(owner, hidden);
534
1535
  for (const item of items) {
535
1536
  const row = legendItem(item.label, "", item.tone);
536
- row.classList.add("chart-legend-toggle-item");
1537
+ row.classList.add("icen-chart-legend-toggle-item");
537
1538
  row.setAttribute("role", "button");
538
1539
  row.tabIndex = 0;
539
- row.setAttribute("aria-pressed", "true");
540
- row.dataset.chartKey = item.key;
541
- const dot = row.querySelector(".chart-legend-dot");
542
- const toneColor = toneOf(item.tone, item.si + 1);
1540
+ row.classList.toggle("is-off", hidden.has(item.key));
1541
+ row.setAttribute("aria-pressed", String(!hidden.has(item.key)));
1542
+ row.dataset.icenChartKey = item.key;
1543
+ const dot = row.querySelector(".icen-chart-legend-dot");
1544
+ const toneColor = toneOf(item.tone, item.si);
543
1545
  if (dot && toneColor) dot.style.setProperty("--chart-tone", toneColor);
544
1546
  const toggle = () => {
545
1547
  const off = row.classList.toggle("is-off");
1548
+ if (off) hidden.add(item.key);
1549
+ else hidden.delete(item.key);
546
1550
  row.setAttribute("aria-pressed", String(!off));
547
- scope.querySelectorAll(`[data-chart-key="${CSS.escape(item.key)}"]`).forEach((m) => {
1551
+ scope.querySelectorAll(`[data-icen-chart-key="${CSS.escape(item.key)}"]`).forEach((m) => {
548
1552
  m.classList.toggle("is-off", off);
549
1553
  });
550
1554
  emitIcen(legend, "icen:chart-legend-toggle", { key: item.key, seriesIndex: item.si, hidden: off });
@@ -559,8 +1563,9 @@ function toggleLegend(items, scope) {
559
1563
  legend.appendChild(row);
560
1564
  }
561
1565
  for (const item of items) {
562
- scope.querySelectorAll(`[data-chart-series="${item.si}"]`).forEach((m) => {
563
- if (!m.hasAttribute("data-chart-key")) m.setAttribute("data-chart-key", item.key);
1566
+ scope.querySelectorAll(`[data-icen-chart-series="${item.si}"]`).forEach((m) => {
1567
+ if (!m.hasAttribute("data-icen-chart-key")) m.setAttribute("data-icen-chart-key", item.key);
1568
+ m.classList.toggle("is-off", hidden.has(item.key));
564
1569
  });
565
1570
  }
566
1571
  return legend;
@@ -587,19 +1592,19 @@ function renderVBar(el, opts) {
587
1592
  for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
588
1593
  }
589
1594
  const wrap = document.createElement("div");
590
- wrap.className = "chart-vbar";
1595
+ wrap.className = "icen-chart-vbar";
591
1596
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
592
1597
  for (let i = 0; i < n; i++) {
593
1598
  const track = document.createElement("div");
594
- track.className = "chart-vbar-track";
1599
+ track.className = "icen-chart-vbar-track";
595
1600
  if (seriesList.length > 1) {
596
- track.classList.add("chart-vbar-track--multi");
597
- if (opts.stacked) track.classList.add("chart-vbar-track--stacked");
1601
+ track.classList.add("icen-chart-vbar-track--multi");
1602
+ if (opts.stacked) track.classList.add("icen-chart-vbar-track--stacked");
598
1603
  }
599
1604
  seriesList.forEach((s, si) => {
600
1605
  const value = s.values[i] ?? 0;
601
1606
  const bar = document.createElement("div");
602
- bar.className = "chart-vbar-bar";
1607
+ bar.className = "icen-chart-vbar-bar";
603
1608
  const height = value > 0 ? Math.max(6, value / max * 100) : 0;
604
1609
  bar.style.height = `${height}%`;
605
1610
  const toneColor = toneOf(s.tone, si);
@@ -617,7 +1622,7 @@ function renderVBar(el, opts) {
617
1622
  wrap.appendChild(track);
618
1623
  }
619
1624
  body.append(wrap, labelsRow(labelsUsed));
620
- if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
1625
+ 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));
621
1626
  });
622
1627
  attachChartInteraction(el, "vbar");
623
1628
  return el;
@@ -634,22 +1639,22 @@ function renderHBar(el, opts) {
634
1639
  const values = opts.values.slice(0, n);
635
1640
  const max = maxOf(values);
636
1641
  const wrap = document.createElement("div");
637
- wrap.className = "chart-hbar";
1642
+ wrap.className = "icen-chart-hbar";
638
1643
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
639
1644
  values.forEach((value, i) => {
640
1645
  const row = document.createElement("div");
641
- row.className = "chart-hbar-row";
1646
+ row.className = "icen-chart-hbar-row";
642
1647
  const label = document.createElement("span");
643
- label.className = "chart-hbar-label";
1648
+ label.className = "icen-chart-hbar-label";
644
1649
  label.textContent = labels[i] ?? "";
645
1650
  const track = document.createElement("div");
646
- track.className = "chart-hbar-track";
1651
+ track.className = "icen-chart-hbar-track";
647
1652
  const bar = document.createElement("div");
648
- bar.className = "chart-hbar-bar";
1653
+ bar.className = "icen-chart-hbar-bar";
649
1654
  bar.style.width = value > 0 ? `${Math.max(2, value / max * 100)}%` : "0%";
650
1655
  setMarkData(bar, { index: i, label: labels[i] ?? "", value });
651
1656
  const val = document.createElement("span");
652
- val.className = "chart-hbar-value";
1657
+ val.className = "icen-chart-hbar-value";
653
1658
  val.textContent = fmt(opts, value);
654
1659
  track.appendChild(bar);
655
1660
  row.append(label, track, val);
@@ -669,10 +1674,10 @@ function renderStack(el, opts) {
669
1674
  return;
670
1675
  }
671
1676
  const bar = document.createElement("div");
672
- bar.className = "chart-stack";
1677
+ bar.className = "icen-chart-stack";
673
1678
  for (const [segIndex, seg] of opts.segments.entries()) {
674
1679
  const s = document.createElement("div");
675
- s.className = "chart-stack-seg";
1680
+ s.className = "icen-chart-stack-seg";
676
1681
  s.style.width = `${seg.value / total * 100}%`;
677
1682
  if (seg.tone) s.style.setProperty("--chart-tone", toneVar(seg.tone));
678
1683
  s.setAttribute("title", `${seg.label}: ${fmt(opts, seg.value)}`);
@@ -680,7 +1685,7 @@ function renderStack(el, opts) {
680
1685
  bar.appendChild(s);
681
1686
  }
682
1687
  const legend = document.createElement("div");
683
- legend.className = "chart-legend";
1688
+ legend.className = "icen-chart-legend";
684
1689
  for (const seg of opts.segments) {
685
1690
  legend.appendChild(legendItem(seg.label, fmt(opts, seg.value), seg.tone));
686
1691
  }
@@ -700,15 +1705,15 @@ function renderDonut(el, opts) {
700
1705
  const radius = 42;
701
1706
  const circumference = 2 * Math.PI * radius;
702
1707
  const wrap = document.createElement("div");
703
- wrap.className = "chart-donut";
704
- const svg2 = svgEl("svg", { viewBox: "0 0 120 120", role: "img" });
705
- svg2.style.transform = "rotate(-90deg)";
706
- svg2.appendChild(svgEl("circle", {
1708
+ wrap.className = "icen-chart-donut";
1709
+ const svg = svgEl("svg", { viewBox: "0 0 120 120", role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.donut")) });
1710
+ svg.style.transform = "rotate(-90deg)";
1711
+ svg.appendChild(svgEl("circle", {
707
1712
  cx: "60",
708
1713
  cy: "60",
709
1714
  r: String(radius),
710
1715
  fill: "none",
711
- stroke: "var(--token-line-soft)",
1716
+ stroke: "var(--icen-token-line-soft)",
712
1717
  "stroke-width": "14"
713
1718
  }));
714
1719
  let offset = 0;
@@ -726,15 +1731,15 @@ function renderDonut(el, opts) {
726
1731
  "stroke-linecap": "butt"
727
1732
  });
728
1733
  setMarkData(arc, { index: segIndex, label: seg.label, value: seg.value });
729
- svg2.appendChild(arc);
1734
+ svg.appendChild(arc);
730
1735
  offset += dash;
731
1736
  });
732
1737
  const legend = document.createElement("div");
733
- legend.className = "chart-legend";
1738
+ legend.className = "icen-chart-legend";
734
1739
  for (const seg of opts.segments) {
735
1740
  legend.appendChild(legendItem(seg.label, `${Math.round(seg.value / total * 100)}%`, seg.tone));
736
1741
  }
737
- wrap.append(svg2, legend);
1742
+ wrap.append(svg, legend);
738
1743
  body.appendChild(wrap);
739
1744
  });
740
1745
  attachChartInteraction(el, "donut");
@@ -752,23 +1757,23 @@ function renderLine(el, opts) {
752
1757
  return;
753
1758
  }
754
1759
  const labelsUsed = labels.slice(0, n);
755
- const width = 360;
1760
+ const width = fluidWidth(body);
756
1761
  const height = 150;
757
1762
  const pad = 14;
758
1763
  let max = 1;
759
1764
  for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
760
1765
  const wrap = document.createElement("div");
761
- wrap.className = "chart-line";
762
- const svg2 = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img" });
1766
+ wrap.className = "icen-chart-line";
1767
+ const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.line")) });
763
1768
  const defs = svgEl("defs", {});
764
1769
  for (let i = 0; i < 4; i++) {
765
1770
  const y = pad + i * (height - pad * 2) / 3;
766
- svg2.appendChild(svgEl("line", {
1771
+ svg.appendChild(svgEl("line", {
767
1772
  x1: String(pad),
768
1773
  x2: String(width - pad),
769
1774
  y1: String(y),
770
1775
  y2: String(y),
771
- stroke: "var(--token-line-soft)",
1776
+ stroke: "var(--icen-token-line-soft)",
772
1777
  "stroke-width": "1"
773
1778
  }));
774
1779
  }
@@ -798,7 +1803,7 @@ function renderLine(el, opts) {
798
1803
  if (seriesList.length > 1) {
799
1804
  setMarkData(areaPath, { seriesIndex: si, seriesName: s.name, label: s.name, value: values.reduce((a, b) => a + b, 0) });
800
1805
  }
801
- svg2.appendChild(areaPath);
1806
+ svg.appendChild(areaPath);
802
1807
  }
803
1808
  const linePath = svgEl("path", {
804
1809
  d: line,
@@ -809,16 +1814,16 @@ function renderLine(el, opts) {
809
1814
  "stroke-linejoin": "round"
810
1815
  });
811
1816
  const lineTitle = svgEl("title", {});
812
- lineTitle.textContent = s.name || "\u6570\u503C";
1817
+ lineTitle.textContent = s.name || t("charts.value");
813
1818
  linePath.appendChild(lineTitle);
814
1819
  setMarkData(linePath, { seriesIndex: si, seriesName: s.name, label: s.name, value: values[values.length - 1] ?? 0 });
815
- svg2.appendChild(linePath);
1820
+ svg.appendChild(linePath);
816
1821
  for (const p of points) {
817
1822
  const dot = svgEl("circle", {
818
1823
  cx: p.x.toFixed(1),
819
1824
  cy: p.y.toFixed(1),
820
1825
  r: "3",
821
- fill: "var(--token-card)",
1826
+ fill: "var(--icen-token-card)",
822
1827
  stroke: tone,
823
1828
  "stroke-width": "2"
824
1829
  });
@@ -832,23 +1837,24 @@ function renderLine(el, opts) {
832
1837
  label: p.label,
833
1838
  value: p.value
834
1839
  });
835
- svg2.appendChild(dot);
1840
+ svg.appendChild(dot);
836
1841
  const hit = svgEl("circle", {
837
1842
  cx: p.x.toFixed(1),
838
1843
  cy: p.y.toFixed(1),
839
1844
  r: "8",
840
1845
  fill: "transparent",
841
- "data-chart-hit": "1"
1846
+ "data-icen-chart-hit": "1"
842
1847
  });
843
1848
  copyMarkData(hit, dot);
844
- svg2.appendChild(hit);
1849
+ svg.appendChild(hit);
845
1850
  }
846
1851
  });
847
- if (defs.firstChild) svg2.insertBefore(defs, svg2.firstChild);
848
- wrap.append(svg2, labelsRow(sampleLabels(labelsUsed)));
1852
+ if (defs.firstChild) svg.insertBefore(defs, svg.firstChild);
1853
+ wrap.append(svg, pointLabelsRow(labelsUsed, width, pad));
849
1854
  body.appendChild(wrap);
850
- if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
1855
+ 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));
851
1856
  });
1857
+ if (el.dataset.icenChartBody == null) observeFluidWidth(el, () => renderLine(el, opts));
852
1858
  attachChartInteraction(el, "line");
853
1859
  return el;
854
1860
  }
@@ -880,30 +1886,30 @@ function renderRadar(el, opts) {
880
1886
  y: cy + r * Math.sin(angleOf(i))
881
1887
  });
882
1888
  const wrap = document.createElement("div");
883
- wrap.className = "chart-radar";
884
- const svg2 = svgEl("svg", { viewBox: `0 0 ${size} ${size}`, role: "img" });
1889
+ wrap.className = "icen-chart-radar";
1890
+ const svg = svgEl("svg", { viewBox: `0 0 ${size} ${size}`, role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.radar")) });
885
1891
  for (let lv = 1; lv <= levels; lv++) {
886
1892
  const r = radius * lv / levels;
887
1893
  const pts = Array.from({ length: n }, (_, i) => {
888
1894
  const p = pointAt(i, r);
889
1895
  return `${p.x.toFixed(1)},${p.y.toFixed(1)}`;
890
1896
  }).join(" ");
891
- svg2.appendChild(svgEl("polygon", {
1897
+ svg.appendChild(svgEl("polygon", {
892
1898
  points: pts,
893
1899
  fill: "none",
894
- stroke: "var(--token-line-soft)",
1900
+ stroke: "var(--icen-token-line-soft)",
895
1901
  "stroke-width": "1",
896
1902
  "stroke-dasharray": lv === levels ? "0" : "3 2"
897
1903
  }));
898
1904
  }
899
1905
  for (let i = 0; i < n; i++) {
900
1906
  const edge = pointAt(i, radius);
901
- svg2.appendChild(svgEl("line", {
1907
+ svg.appendChild(svgEl("line", {
902
1908
  x1: String(cx),
903
1909
  y1: String(cy),
904
1910
  x2: edge.x.toFixed(1),
905
1911
  y2: edge.y.toFixed(1),
906
- stroke: "var(--token-line-soft)",
1912
+ stroke: "var(--icen-token-line-soft)",
907
1913
  "stroke-width": "1"
908
1914
  }));
909
1915
  const labelP = pointAt(i, radius + 14);
@@ -913,14 +1919,14 @@ function renderRadar(el, opts) {
913
1919
  "text-anchor": "middle",
914
1920
  "dominant-baseline": "middle",
915
1921
  "font-size": "9",
916
- fill: "var(--token-text-muted)"
1922
+ fill: "var(--icen-token-text-muted)"
917
1923
  });
918
1924
  txt.textContent = axes[i] ?? "";
919
- svg2.appendChild(txt);
1925
+ svg.appendChild(txt);
920
1926
  }
921
1927
  const legendItems = [];
922
1928
  opts.series.forEach((s, si) => {
923
- const tone = toneVar(s.tone);
1929
+ const tone = toneOf(s.tone, si) ?? toneVar(s.tone);
924
1930
  const pts = Array.from({ length: n }, (_, i) => {
925
1931
  const v = Math.max(0, Math.min(maxVal, s.values[i] ?? 0));
926
1932
  const p = pointAt(i, v / maxVal * radius);
@@ -938,7 +1944,7 @@ function renderRadar(el, opts) {
938
1944
  title.textContent = `${s.name}: ${s.values.map((v) => fmt(opts, v)).join(" / ")}`;
939
1945
  polyFill.appendChild(title);
940
1946
  setMarkData(polyFill, { seriesIndex: si, seriesName: s.name || void 0, label: s.name, value: s.values.reduce((a, b) => a + (b || 0), 0) });
941
- svg2.appendChild(polyFill);
1947
+ svg.appendChild(polyFill);
942
1948
  for (let i = 0; i < n; i++) {
943
1949
  const v = Math.max(0, Math.min(maxVal, s.values[i] ?? 0));
944
1950
  const p = pointAt(i, v / maxVal * radius);
@@ -946,18 +1952,19 @@ function renderRadar(el, opts) {
946
1952
  cx: p.x.toFixed(1),
947
1953
  cy: p.y.toFixed(1),
948
1954
  r: "2.5",
949
- fill: "var(--token-card)",
1955
+ fill: "var(--icen-token-card)",
950
1956
  stroke: tone,
951
1957
  "stroke-width": "1.5"
952
1958
  });
953
1959
  setMarkData(dot, { index: i, seriesIndex: si, seriesName: s.name || void 0, label: axes[i] ?? "", value: s.values[i] ?? 0 });
954
- svg2.appendChild(dot);
1960
+ svg.appendChild(dot);
955
1961
  }
956
- legendItems.push({ key: s.name || `\u7CFB\u5217${si + 1}`, label: s.name || `\u7CFB\u5217${si + 1}`, tone: s.tone, si });
1962
+ legendItems.push({ key: s.name || t("charts.seriesName", { p0: si + 1 }), label: s.name || t("charts.seriesName", { p0: si + 1 }), tone: s.tone, si });
957
1963
  });
958
- const legend = opts.series.length > 1 ? toggleLegend(legendItems, wrap) : null;
959
- wrap.append(svg2, ...legend ? [legend] : []);
1964
+ wrap.appendChild(svg);
960
1965
  body.appendChild(wrap);
1966
+ const legend = opts.series.length > 1 ? toggleLegend(legendItems, wrap) : null;
1967
+ if (legend) wrap.appendChild(legend);
961
1968
  });
962
1969
  attachChartInteraction(el, "radar");
963
1970
  return el;
@@ -990,23 +1997,23 @@ function padFirstWeek(grid, firstDate, weekStart) {
990
1997
  const pad = (first.getDay() + 7 - weekStart) % 7;
991
1998
  for (let i = 0; i < pad; i++) {
992
1999
  const blank = document.createElement("span");
993
- blank.className = "chart-heatmap-cell is-blank";
2000
+ blank.className = "icen-chart-heatmap-cell is-blank";
994
2001
  grid.appendChild(blank);
995
2002
  }
996
2003
  return pad;
997
2004
  }
998
2005
  function heatmapLegend(labels) {
999
2006
  const legend = document.createElement("div");
1000
- legend.className = "chart-heatmap-legend";
2007
+ legend.className = "icen-chart-heatmap-legend";
1001
2008
  const less = document.createElement("span");
1002
- less.textContent = labels?.less ?? "\u5C11";
2009
+ less.textContent = labels?.less ?? t("charts.few");
1003
2010
  const more = document.createElement("span");
1004
- more.textContent = labels?.more ?? "\u591A";
2011
+ more.textContent = labels?.more ?? t("charts.many");
1005
2012
  legend.appendChild(less);
1006
2013
  for (let lv = 0; lv <= 4; lv++) {
1007
2014
  const cell = document.createElement("span");
1008
- cell.className = "chart-heatmap-cell";
1009
- cell.dataset.level = String(lv);
2015
+ cell.className = "icen-chart-heatmap-cell";
2016
+ cell.dataset.icenLevel = String(lv);
1010
2017
  legend.appendChild(cell);
1011
2018
  }
1012
2019
  legend.appendChild(more);
@@ -1033,16 +2040,17 @@ function renderHeatmap(el, opts) {
1033
2040
  if (entries.length > weeks * 7) entries = entries.slice(entries.length - weeks * 7);
1034
2041
  const max = maxOf(entries.map((e) => e.value));
1035
2042
  const wrap = document.createElement("div");
1036
- wrap.className = "chart-heatmap";
2043
+ wrap.className = "icen-chart-heatmap";
1037
2044
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
1038
2045
  const grid = document.createElement("div");
1039
- grid.className = "chart-heatmap-grid";
2046
+ grid.className = "icen-chart-heatmap-grid";
1040
2047
  grid.setAttribute("role", "img");
2048
+ grid.setAttribute("aria-label", chartAriaLabel(opts.title, t("charts.type.heatmap")));
1041
2049
  padFirstWeek(grid, entries[0].date, opts.weekStart === 0 ? 0 : 1);
1042
2050
  for (const [i, entry] of entries.entries()) {
1043
2051
  const cell = document.createElement("span");
1044
- cell.className = "chart-heatmap-cell";
1045
- cell.dataset.level = String(levelOf(entry.value, max));
2052
+ cell.className = "icen-chart-heatmap-cell";
2053
+ cell.dataset.icenLevel = String(levelOf(entry.value, max));
1046
2054
  cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
1047
2055
  setMarkData(cell, { index: i, label: entry.date, value: entry.value });
1048
2056
  grid.appendChild(cell);
@@ -1077,16 +2085,16 @@ function renderSparkline(el, opts) {
1077
2085
  const area = `${line} L${last.x.toFixed(1)},${height - pad} L${first.x.toFixed(1)},${height - pad} Z`;
1078
2086
  const gradientId = `icen-chart-line-${++lineGradientSeq}`;
1079
2087
  const wrap = document.createElement("div");
1080
- wrap.className = "chart-sparkline";
1081
- const svg2 = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img" });
2088
+ wrap.className = "icen-chart-sparkline";
2089
+ const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.sparkline")) });
1082
2090
  const defs = svgEl("defs", {});
1083
2091
  const gradient = svgEl("linearGradient", { id: gradientId, x1: "0", x2: "0", y1: "0", y2: "1" });
1084
2092
  gradient.appendChild(svgEl("stop", { offset: "0%", "stop-color": tone, "stop-opacity": "0.28" }));
1085
2093
  gradient.appendChild(svgEl("stop", { offset: "100%", "stop-color": tone, "stop-opacity": "0.02" }));
1086
2094
  defs.appendChild(gradient);
1087
- svg2.appendChild(defs);
1088
- svg2.appendChild(svgEl("path", { d: area, fill: `url(#${gradientId})` }));
1089
- svg2.appendChild(svgEl("path", {
2095
+ svg.appendChild(defs);
2096
+ svg.appendChild(svgEl("path", { d: area, fill: `url(#${gradientId})` }));
2097
+ svg.appendChild(svgEl("path", {
1090
2098
  d: line,
1091
2099
  fill: "none",
1092
2100
  stroke: tone,
@@ -1098,15 +2106,15 @@ function renderSparkline(el, opts) {
1098
2106
  cx: last.x.toFixed(1),
1099
2107
  cy: last.y.toFixed(1),
1100
2108
  r: "2.4",
1101
- fill: "var(--token-card)",
2109
+ fill: "var(--icen-token-card)",
1102
2110
  stroke: tone,
1103
2111
  "stroke-width": "1.6"
1104
2112
  });
1105
2113
  const title = svgEl("title", {});
1106
2114
  title.textContent = fmt(opts, last.value);
1107
2115
  dot.appendChild(title);
1108
- svg2.appendChild(dot);
1109
- wrap.appendChild(svg2);
2116
+ svg.appendChild(dot);
2117
+ wrap.appendChild(svg);
1110
2118
  body.appendChild(wrap);
1111
2119
  });
1112
2120
  attachChartInteraction(el, "sparkline");
@@ -1120,20 +2128,20 @@ function renderGauge(el, opts) {
1120
2128
  const radius = 42;
1121
2129
  const circumference = 2 * Math.PI * radius;
1122
2130
  const wrap = document.createElement("div");
1123
- wrap.className = "chart-gauge";
2131
+ wrap.className = "icen-chart-gauge";
1124
2132
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
1125
- const svg2 = svgEl("svg", { viewBox: "0 0 120 120", role: "img" });
1126
- svg2.style.transform = "rotate(-90deg)";
1127
- svg2.appendChild(svgEl("circle", {
2133
+ const svg = svgEl("svg", { viewBox: "0 0 120 120", role: "img", "aria-label": chartAriaLabel(opts.label, t("charts.type.gauge")) });
2134
+ svg.style.transform = "rotate(-90deg)";
2135
+ svg.appendChild(svgEl("circle", {
1128
2136
  cx: "60",
1129
2137
  cy: "60",
1130
2138
  r: String(radius),
1131
2139
  fill: "none",
1132
- stroke: "var(--token-line-soft)",
2140
+ stroke: "var(--icen-token-line-soft)",
1133
2141
  "stroke-width": "10"
1134
2142
  }));
1135
2143
  const dash = ratio * circumference;
1136
- svg2.appendChild(svgEl("circle", {
2144
+ svg.appendChild(svgEl("circle", {
1137
2145
  cx: "60",
1138
2146
  cy: "60",
1139
2147
  r: String(radius),
@@ -1144,18 +2152,18 @@ function renderGauge(el, opts) {
1144
2152
  "stroke-linecap": "round"
1145
2153
  }));
1146
2154
  const center = document.createElement("div");
1147
- center.className = "chart-gauge-center";
2155
+ center.className = "icen-chart-gauge-center";
1148
2156
  const val = document.createElement("span");
1149
- val.className = "chart-gauge-value";
2157
+ val.className = "icen-chart-gauge-value";
1150
2158
  val.textContent = opts.formatValue ? opts.formatValue(opts.value) : `${Math.round(ratio * 100)}%`;
1151
2159
  center.appendChild(val);
1152
2160
  if (opts.label) {
1153
2161
  const lab = document.createElement("span");
1154
- lab.className = "chart-gauge-label";
2162
+ lab.className = "icen-chart-gauge-label";
1155
2163
  lab.textContent = opts.label;
1156
2164
  center.appendChild(lab);
1157
2165
  }
1158
- wrap.append(svg2, center);
2166
+ wrap.append(svg, center);
1159
2167
  el.appendChild(wrap);
1160
2168
  attachChartInteraction(el, "gauge");
1161
2169
  return el;
@@ -1173,19 +2181,20 @@ function renderCalendar(el, opts) {
1173
2181
  return;
1174
2182
  }
1175
2183
  const wrap = document.createElement("div");
1176
- wrap.className = "chart-calendar";
2184
+ wrap.className = "icen-chart-calendar";
1177
2185
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
1178
2186
  const weekStart = opts.weekStart === 0 ? 0 : 1;
1179
- const weekdayDefaults = ["\u4E00", "", "\u4E09", "", "\u4E94", "", "\u65E5"];
2187
+ const weekdayDefaults = t("charts.calendarWeekdays");
1180
2188
  const weekdayLabels = opts.labels?.weekdays ?? weekdayDefaults.map((_, i) => weekdayLabelAt(weekdayDefaults, (weekStart + i) % 7));
1181
- const monthLabel = (m) => opts.labels?.months?.[m] ?? `${m + 1}\u6708`;
2189
+ const monthLabel = (m) => opts.labels?.months?.[m] ?? t("charts.monthLabel", { p0: m + 1 });
1182
2190
  const grid = document.createElement("div");
1183
- grid.className = "chart-heatmap-grid";
2191
+ grid.className = "icen-chart-heatmap-grid";
1184
2192
  grid.setAttribute("role", "img");
2193
+ grid.setAttribute("aria-label", chartAriaLabel(opts.title, t("charts.type.heatmap")));
1185
2194
  const months = document.createElement("div");
1186
- months.className = "chart-calendar-months";
2195
+ months.className = "icen-chart-calendar-months";
1187
2196
  const weekdayRow = document.createElement("div");
1188
- weekdayRow.className = "chart-calendar-weekdays";
2197
+ weekdayRow.className = "icen-chart-calendar-weekdays";
1189
2198
  weekdayLabels.forEach((w) => {
1190
2199
  const s = document.createElement("span");
1191
2200
  s.textContent = w;
@@ -1200,14 +2209,14 @@ function renderCalendar(el, opts) {
1200
2209
  if (d.getMonth() !== lastMonth) {
1201
2210
  lastMonth = d.getMonth();
1202
2211
  const label = document.createElement("span");
1203
- label.className = "chart-calendar-month";
2212
+ label.className = "icen-chart-calendar-month";
1204
2213
  label.textContent = monthLabel(d.getMonth());
1205
2214
  label.style.setProperty("--col", String(col));
1206
2215
  months.appendChild(label);
1207
2216
  }
1208
2217
  const cell = document.createElement("span");
1209
- cell.className = "chart-heatmap-cell";
1210
- cell.dataset.level = String(levelOf(entry.value, max));
2218
+ cell.className = "icen-chart-heatmap-cell";
2219
+ cell.dataset.icenLevel = String(levelOf(entry.value, max));
1211
2220
  cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
1212
2221
  setMarkData(cell, { index: i, label: entry.date, value: entry.value });
1213
2222
  grid.appendChild(cell);
@@ -1229,7 +2238,7 @@ function renderScatter(el, opts) {
1229
2238
  renderEmpty(body, opts.emptyLabel);
1230
2239
  return;
1231
2240
  }
1232
- const width = 360;
2241
+ const width = fluidWidth(body);
1233
2242
  const height = 220;
1234
2243
  const pad = 30;
1235
2244
  let xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, sMax = 0;
@@ -1244,19 +2253,19 @@ function renderScatter(el, opts) {
1244
2253
  if (yMax === yMin) yMax = yMin + 1;
1245
2254
  const rOf = (size) => size && sMax > 0 ? 3 + size / sMax * 7 : 4;
1246
2255
  const wrap = document.createElement("div");
1247
- wrap.className = "chart-scatter";
2256
+ wrap.className = "icen-chart-scatter";
1248
2257
  const tone = toneVar(opts.tone);
1249
- const svg2 = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img" });
2258
+ const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.scatter")) });
1250
2259
  const xAt = (x) => pad + (x - xMin) / (xMax - xMin) * (width - pad * 2);
1251
2260
  const yAt = (y) => height - pad - (y - yMin) / (yMax - yMin) * (height - pad * 2);
1252
2261
  for (let i = 0; i <= 3; i++) {
1253
2262
  const y = pad + i * (height - pad * 2) / 3;
1254
- svg2.appendChild(svgEl("line", {
2263
+ svg.appendChild(svgEl("line", {
1255
2264
  x1: String(pad),
1256
2265
  x2: String(width - pad),
1257
2266
  y1: String(y),
1258
2267
  y2: String(y),
1259
- stroke: "var(--token-line-soft)",
2268
+ stroke: "var(--icen-token-line-soft)",
1260
2269
  "stroke-width": "1"
1261
2270
  }));
1262
2271
  const tv = yMin + (yMax - yMin) * (3 - i) / 3;
@@ -1265,19 +2274,19 @@ function renderScatter(el, opts) {
1265
2274
  y: String(y + 3),
1266
2275
  "text-anchor": "end",
1267
2276
  "font-size": "8",
1268
- fill: "var(--token-text-faint)"
2277
+ fill: "var(--icen-token-text-faint)"
1269
2278
  });
1270
2279
  txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
1271
- svg2.appendChild(txt);
2280
+ svg.appendChild(txt);
1272
2281
  }
1273
2282
  for (let i = 0; i <= 4; i++) {
1274
2283
  const x = pad + i * (width - pad * 2) / 4;
1275
- svg2.appendChild(svgEl("line", {
2284
+ svg.appendChild(svgEl("line", {
1276
2285
  x1: String(x),
1277
2286
  x2: String(x),
1278
2287
  y1: String(pad),
1279
2288
  y2: String(height - pad),
1280
- stroke: "var(--token-line-soft)",
2289
+ stroke: "var(--icen-token-line-soft)",
1281
2290
  "stroke-width": "1",
1282
2291
  "stroke-dasharray": "2 3"
1283
2292
  }));
@@ -1287,20 +2296,20 @@ function renderScatter(el, opts) {
1287
2296
  y: String(height - pad + 12),
1288
2297
  "text-anchor": "middle",
1289
2298
  "font-size": "8",
1290
- fill: "var(--token-text-faint)"
2299
+ fill: "var(--icen-token-text-faint)"
1291
2300
  });
1292
2301
  txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
1293
- svg2.appendChild(txt);
2302
+ svg.appendChild(txt);
1294
2303
  }
1295
2304
  if (opts.yLabel) {
1296
- const t = svgEl("text", { x: "10", y: String(pad - 10), "font-size": "8", fill: "var(--token-text-faint)" });
1297
- t.textContent = opts.yLabel;
1298
- svg2.appendChild(t);
2305
+ const t2 = svgEl("text", { x: "10", y: String(pad - 10), "font-size": "8", fill: "var(--icen-token-text-faint)" });
2306
+ t2.textContent = opts.yLabel;
2307
+ svg.appendChild(t2);
1299
2308
  }
1300
2309
  if (opts.xLabel) {
1301
- const t = svgEl("text", { x: String(width - pad), y: String(pad - 10), "text-anchor": "end", "font-size": "8", fill: "var(--token-text-faint)" });
1302
- t.textContent = opts.xLabel;
1303
- svg2.appendChild(t);
2310
+ const t2 = svgEl("text", { x: String(width - pad), y: String(pad - 10), "text-anchor": "end", "font-size": "8", fill: "var(--icen-token-text-faint)" });
2311
+ t2.textContent = opts.xLabel;
2312
+ svg.appendChild(t2);
1304
2313
  }
1305
2314
  pts.forEach((p, i) => {
1306
2315
  const c = svgEl("circle", {
@@ -1317,11 +2326,12 @@ function renderScatter(el, opts) {
1317
2326
  c.appendChild(title);
1318
2327
  setMarkData(c, { index: i, label: p.label ?? `#${i + 1}`, value: p.y });
1319
2328
  c.dataset.chartX = String(p.x);
1320
- svg2.appendChild(c);
2329
+ svg.appendChild(c);
1321
2330
  });
1322
- wrap.appendChild(svg2);
2331
+ wrap.appendChild(svg);
1323
2332
  body.appendChild(wrap);
1324
2333
  });
2334
+ if (el.dataset.icenChartBody == null) observeFluidWidth(el, () => renderScatter(el, opts));
1325
2335
  attachChartInteraction(el, "scatter");
1326
2336
  return el;
1327
2337
  }
@@ -1371,7 +2381,7 @@ function pivotData(spec) {
1371
2381
  const labels = [];
1372
2382
  const seenLabel = /* @__PURE__ */ new Set();
1373
2383
  const hasSeries = data.some((r) => r[seriesField] != null && r[seriesField] !== "");
1374
- const seriesNames = [];
2384
+ const seriesNames = hasSeries ? [] : [""];
1375
2385
  const seenSeries = /* @__PURE__ */ new Set();
1376
2386
  for (const r of data) {
1377
2387
  const l = str(r[labelField]);
@@ -1460,8 +2470,8 @@ function normalizeChartSpec(raw) {
1460
2470
  const s = src;
1461
2471
  const spec = {};
1462
2472
  if (typeof s.type === "string") {
1463
- const t = s.type.trim().toLowerCase();
1464
- spec.type = CHART_TYPE_ALIASES[t] ?? t;
2473
+ const t2 = s.type.trim().toLowerCase();
2474
+ spec.type = CHART_TYPE_ALIASES[t2] ?? t2;
1465
2475
  }
1466
2476
  for (const k of ["title", "tone", "emptyLabel"]) {
1467
2477
  if (typeof s[k] === "string") spec[k] = s[k];
@@ -1483,7 +2493,7 @@ function normalizeChartSpec(raw) {
1483
2493
  if (Array.isArray(s.series)) {
1484
2494
  const series = s.series.filter((v) => !!v && typeof v === "object").map((v, i) => {
1485
2495
  const values = Array.isArray(v.values) ? v.values.map(toNum).filter((n) => n != null) : [];
1486
- const sv = { name: String(v.name ?? `\u7CFB\u5217${i + 1}`), values };
2496
+ const sv = { name: String(v.name ?? t("charts.seriesName", { p0: i + 1 })), values };
1487
2497
  const tone = v.tone;
1488
2498
  if (typeof tone === "string" || typeof tone === "number") sv.tone = tone;
1489
2499
  return sv;
@@ -1518,6 +2528,40 @@ function normalizeChartSpec(raw) {
1518
2528
  }
1519
2529
  spec.format = format;
1520
2530
  }
2531
+ if (typeof s.downsample === "boolean") spec.downsample = s.downsample;
2532
+ else {
2533
+ const n = toNum(s.downsample);
2534
+ if (n != null) spec.downsample = n;
2535
+ }
2536
+ if (s.zoom === "x" || s.zoom === "brush") spec.zoom = s.zoom;
2537
+ if (typeof s.loading === "boolean") spec.loading = s.loading;
2538
+ if (Array.isArray(s.marks)) {
2539
+ const marks = [];
2540
+ for (const raw2 of s.marks) {
2541
+ if (!raw2 || typeof raw2 !== "object" || Array.isArray(raw2)) continue;
2542
+ const m = raw2;
2543
+ if (m.type === "line") {
2544
+ if (m.axis !== "x" && m.axis !== "y") continue;
2545
+ if (typeof m.value !== "number" && typeof m.value !== "string") continue;
2546
+ const mark = { type: "line", axis: m.axis, value: m.value };
2547
+ if (typeof m.label === "string" && m.label !== "") mark.label = m.label;
2548
+ if (m.tone === "success" || m.tone === "warning" || m.tone === "error" || m.tone === "info") {
2549
+ mark.tone = m.tone;
2550
+ }
2551
+ marks.push(mark);
2552
+ } else if (m.type === "point") {
2553
+ if (typeof m.label !== "string" || m.label === "") continue;
2554
+ const mark = { type: "point", label: m.label };
2555
+ const si = toNum(m.seriesIndex);
2556
+ if (si != null && si >= 0) mark.seriesIndex = Math.floor(si);
2557
+ const v = toNum(m.value);
2558
+ if (v != null) mark.value = v;
2559
+ if (typeof m.text === "string") mark.text = m.text;
2560
+ marks.push(mark);
2561
+ }
2562
+ }
2563
+ if (marks.length > 0) spec.marks = marks;
2564
+ }
1521
2565
  const isDateRecords = spec.data?.length && spec.data[0] && "date" in spec.data[0];
1522
2566
  if (spec.data && spec.data.length > 0 && !isDateRecords && !spec.series && !spec.points && !spec.values && !spec.segments) {
1523
2567
  Object.assign(spec, pivotData(spec));
@@ -1557,6 +2601,391 @@ function inferChartType(spec) {
1557
2601
  if (typeof spec.max === "number" && spec.values && spec.values.length === 1) return "gauge";
1558
2602
  return "vbar";
1559
2603
  }
2604
+ var DOWNSAMPLE_DEFAULT_THRESHOLD = 1e3;
2605
+ var CHART_ZOOM_DRAG_MIN_PX = 5;
2606
+ var CHART_LINE_W = 360;
2607
+ var CHART_LINE_H = 150;
2608
+ var CHART_LINE_PAD = 14;
2609
+ function isDownsampleableType(type) {
2610
+ return type === "line" || type === "area" || type === "scatter";
2611
+ }
2612
+ function isZoomableType(type) {
2613
+ return type === "line" || type === "area" || type === "vbar";
2614
+ }
2615
+ function isStreamableType(type) {
2616
+ return type === "line" || type === "area" || type === "vbar";
2617
+ }
2618
+ function downsampleView(view, type) {
2619
+ const raw = view.downsample;
2620
+ if (raw == null || raw === false) return { view, note: null };
2621
+ const threshold = raw === true ? DOWNSAMPLE_DEFAULT_THRESHOLD : Math.floor(raw);
2622
+ if (!Number.isFinite(threshold)) return { view, note: null };
2623
+ if (type === "scatter") {
2624
+ const pts = view.points ?? [];
2625
+ if (pts.length === 0) return { view, note: null };
2626
+ const sampled2 = lttb(pts, threshold, (p) => p.x, (p) => p.y);
2627
+ if (sampled2.length === pts.length) return { view, note: null };
2628
+ return { view: { ...view, points: sampled2 }, note: t("charts.sampled", { p0: pts.length, p1: sampled2.length }) };
2629
+ }
2630
+ const labels = view.labels ?? [];
2631
+ if (labels.length === 0) return { view, note: null };
2632
+ const guide = view.series && view.series.length > 0 ? view.series[0]?.values ?? [] : view.values ?? [];
2633
+ const count = Math.min(labels.length, guide.length);
2634
+ if (count === 0) return { view, note: null };
2635
+ const indexed = guide.slice(0, count).map((v, i) => ({ i, v }));
2636
+ const sampled = lttb(indexed, threshold, (p) => p.i, (p) => p.v);
2637
+ if (sampled.length >= count) return { view, note: null };
2638
+ const idx = sampled.map((p) => p.i);
2639
+ return {
2640
+ view: {
2641
+ ...view,
2642
+ labels: idx.map((i) => labels[i] ?? ""),
2643
+ values: view.values ? idx.map((i) => view.values?.[i] ?? 0) : void 0,
2644
+ series: view.series?.map((s) => ({ ...s, values: idx.map((i) => s.values[i] ?? 0) }))
2645
+ },
2646
+ note: t("charts.sampled", { p0: count, p1: idx.length })
2647
+ };
2648
+ }
2649
+ function renderChartSkeleton(body) {
2650
+ const box = document.createElement("div");
2651
+ box.className = "icen-chart-skeleton";
2652
+ box.setAttribute("role", "status");
2653
+ box.setAttribute("aria-busy", "true");
2654
+ box.setAttribute("aria-label", t("charts.loading"));
2655
+ const widths = [100, 84, 92, 68, 76, 52];
2656
+ for (const w of widths) {
2657
+ const bar = document.createElement("div");
2658
+ bar.className = "icen-chart-skeleton-bar";
2659
+ bar.style.width = `${w}%`;
2660
+ box.appendChild(bar);
2661
+ }
2662
+ body.appendChild(box);
2663
+ }
2664
+ var CHART_MARK_ACCENT = "var(--icen-token-accent)";
2665
+ function markToneVar(tone) {
2666
+ switch (tone) {
2667
+ case "success":
2668
+ return "var(--icen-token-success)";
2669
+ case "warning":
2670
+ return "var(--icen-token-warning)";
2671
+ case "error":
2672
+ return "var(--icen-token-error)";
2673
+ case "info":
2674
+ return "var(--icen-token-info)";
2675
+ default:
2676
+ return "var(--icen-token-text-faint)";
2677
+ }
2678
+ }
2679
+ function markWarnOnce(warned, mark, reason) {
2680
+ const key = JSON.stringify(mark);
2681
+ if (warned.has(key)) return;
2682
+ warned.add(key);
2683
+ console.warn(`[icen:chart] \u65E0\u6548\u6807\u8BB0\u5DF2\u8DF3\u8FC7\uFF08${reason}\uFF09`, mark);
2684
+ }
2685
+ function paintChartMarks(root, view, spec, formatValue, warned) {
2686
+ const marks = spec.marks;
2687
+ if (!marks || marks.length === 0) return;
2688
+ const type = spec.type;
2689
+ if (type === "line" || type === "area") paintSvgChartMarks(root, view, marks, formatValue, warned);
2690
+ else if (type === "vbar") paintVBarChartMarks(root, view, spec, marks, formatValue, warned);
2691
+ }
2692
+ function paintSvgChartMarks(root, view, marks, formatValue, warned) {
2693
+ const svg = root.querySelector(".icen-chart-line svg");
2694
+ if (!svg) return;
2695
+ const labels = view.labels ?? [];
2696
+ const n = labels.length;
2697
+ if (n === 0) return;
2698
+ const seriesList = view.series && view.series.length > 0 ? view.series : view.values ? [{ name: "", values: view.values }] : [];
2699
+ let max = 1;
2700
+ for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
2701
+ const dims = svgDimsOf(svg);
2702
+ const W = dims.w;
2703
+ const H = dims.h;
2704
+ const PAD = CHART_LINE_PAD;
2705
+ const xAt = (i) => n <= 1 ? W / 2 : PAD + i / (n - 1) * (W - PAD * 2);
2706
+ const yAt = (v) => H - PAD - Math.max(0, v) / max * (H - PAD * 2);
2707
+ for (const mark of marks) {
2708
+ if (mark.type === "line") {
2709
+ const color = markToneVar(mark.tone);
2710
+ if (mark.axis === "y") {
2711
+ const v = typeof mark.value === "number" ? mark.value : Number(mark.value);
2712
+ if (!Number.isFinite(v)) {
2713
+ markWarnOnce(warned, mark, "\u53C2\u8003\u7EBF\u6570\u503C NaN");
2714
+ continue;
2715
+ }
2716
+ const y = clampNum(yAt(v), CHART_LINE_PAD, H - PAD);
2717
+ svg.appendChild(svgEl("line", {
2718
+ class: "icen-chart-mark-line",
2719
+ x1: String(PAD),
2720
+ x2: String(W - PAD),
2721
+ y1: y.toFixed(1),
2722
+ y2: y.toFixed(1),
2723
+ stroke: color,
2724
+ "stroke-width": "1",
2725
+ "stroke-dasharray": "4 3"
2726
+ }));
2727
+ const label = svgEl("text", {
2728
+ class: "icen-chart-mark-label",
2729
+ x: String(W - PAD),
2730
+ y: String(clampNum(y - 4, 8, CHART_LINE_H - 4)),
2731
+ "text-anchor": "end",
2732
+ "font-size": "8",
2733
+ fill: color
2734
+ });
2735
+ label.textContent = mark.label ?? formatValue(v);
2736
+ svg.appendChild(label);
2737
+ } else {
2738
+ const idx = typeof mark.value === "number" ? Math.round(mark.value) : labels.indexOf(mark.value);
2739
+ if (idx < 0 || idx >= n) {
2740
+ markWarnOnce(warned, mark, "\u53C2\u8003\u7EBF\u7C7B\u76EE\u4E0D\u5B58\u5728");
2741
+ continue;
2742
+ }
2743
+ const x = xAt(idx);
2744
+ svg.appendChild(svgEl("line", {
2745
+ class: "icen-chart-mark-line",
2746
+ x1: x.toFixed(1),
2747
+ x2: x.toFixed(1),
2748
+ y1: String(PAD),
2749
+ y2: String(H - PAD),
2750
+ stroke: color,
2751
+ "stroke-width": "1",
2752
+ "stroke-dasharray": "4 3"
2753
+ }));
2754
+ const label = svgEl("text", {
2755
+ class: "icen-chart-mark-label",
2756
+ x: String(clampNum(x, 10, W - 10)),
2757
+ y: String(PAD + 8),
2758
+ "text-anchor": "middle",
2759
+ "font-size": "8",
2760
+ fill: color
2761
+ });
2762
+ label.textContent = mark.label ?? labels[idx] ?? "";
2763
+ svg.appendChild(label);
2764
+ }
2765
+ } else {
2766
+ const idx = labels.indexOf(mark.label);
2767
+ if (idx < 0 || idx >= n) {
2768
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u7C7B\u76EE\u4E0D\u5B58\u5728");
2769
+ continue;
2770
+ }
2771
+ const si = Math.max(0, Math.floor(mark.seriesIndex ?? 0));
2772
+ const s = seriesList[si];
2773
+ if (!s) {
2774
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u7CFB\u5217\u4E0D\u5B58\u5728");
2775
+ continue;
2776
+ }
2777
+ const v = mark.value ?? s.values[idx] ?? NaN;
2778
+ if (!Number.isFinite(v)) {
2779
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u6570\u503C NaN");
2780
+ continue;
2781
+ }
2782
+ const x = xAt(idx);
2783
+ const y = clampNum(yAt(v), CHART_LINE_PAD, H - PAD);
2784
+ const g = svgEl("g", { class: "icen-chart-mark-point" });
2785
+ g.appendChild(svgEl("circle", {
2786
+ cx: x.toFixed(1),
2787
+ cy: y.toFixed(1),
2788
+ r: "6",
2789
+ fill: "none",
2790
+ stroke: CHART_MARK_ACCENT,
2791
+ "stroke-width": "1.5"
2792
+ }));
2793
+ g.appendChild(svgEl("circle", { cx: x.toFixed(1), cy: y.toFixed(1), r: "2", fill: CHART_MARK_ACCENT }));
2794
+ const label = svgEl("text", {
2795
+ class: "icen-chart-mark-label",
2796
+ x: String(clampNum(x, 12, W - 12)),
2797
+ y: String(clampNum(y - 10, 8, CHART_LINE_H - 4)),
2798
+ "text-anchor": "middle",
2799
+ "font-size": "8",
2800
+ fill: CHART_MARK_ACCENT
2801
+ });
2802
+ label.textContent = mark.text ?? mark.label;
2803
+ g.appendChild(label);
2804
+ svg.appendChild(g);
2805
+ }
2806
+ }
2807
+ }
2808
+ function paintVBarChartMarks(root, view, spec, marks, formatValue, warned) {
2809
+ const wrap = root.querySelector(".icen-chart-vbar");
2810
+ if (!wrap) return;
2811
+ const seriesList = view.series && view.series.length > 0 ? view.series : view.values ? [{ name: "", values: view.values }] : [];
2812
+ const labels = view.labels ?? [];
2813
+ const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
2814
+ if (n === 0 || seriesList.length === 0) return;
2815
+ const multi = seriesList.length > 1;
2816
+ let max = 1;
2817
+ if (spec.stacked && multi) {
2818
+ for (let i = 0; i < n; i++) {
2819
+ let sum2 = 0;
2820
+ for (const s of seriesList) sum2 += Math.max(0, s.values[i] ?? 0);
2821
+ if (sum2 > max) max = sum2;
2822
+ }
2823
+ } else {
2824
+ for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
2825
+ }
2826
+ const centerPct = (i) => (i + 0.5) / n * 100;
2827
+ for (const mark of marks) {
2828
+ if (mark.type === "line") {
2829
+ if (mark.axis === "y") {
2830
+ const v = typeof mark.value === "number" ? mark.value : Number(mark.value);
2831
+ if (!Number.isFinite(v)) {
2832
+ markWarnOnce(warned, mark, "\u53C2\u8003\u7EBF\u6570\u503C NaN");
2833
+ continue;
2834
+ }
2835
+ const line = document.createElement("div");
2836
+ line.className = "icen-chart-mark-line icen-chart-mark-line--h";
2837
+ line.style.top = `${(clampNum(1 - Math.max(0, v) / max, 0.02, 0.98) * 100).toFixed(2)}%`;
2838
+ line.style.setProperty("--chart-mark-tone", markToneVar(mark.tone));
2839
+ const lab = document.createElement("span");
2840
+ lab.className = "icen-chart-mark-label";
2841
+ lab.textContent = mark.label ?? formatValue(v);
2842
+ line.appendChild(lab);
2843
+ wrap.appendChild(line);
2844
+ } else {
2845
+ const idx = typeof mark.value === "number" ? Math.round(mark.value) : labels.indexOf(mark.value);
2846
+ if (idx < 0 || idx >= n) {
2847
+ markWarnOnce(warned, mark, "\u53C2\u8003\u7EBF\u7C7B\u76EE\u4E0D\u5B58\u5728");
2848
+ continue;
2849
+ }
2850
+ const line = document.createElement("div");
2851
+ line.className = "icen-chart-mark-line icen-chart-mark-line--v";
2852
+ line.style.left = `${centerPct(idx).toFixed(2)}%`;
2853
+ line.style.setProperty("--chart-mark-tone", markToneVar(mark.tone));
2854
+ const lab = document.createElement("span");
2855
+ lab.className = "icen-chart-mark-label";
2856
+ lab.textContent = mark.label ?? labels[idx] ?? "";
2857
+ line.appendChild(lab);
2858
+ wrap.appendChild(line);
2859
+ }
2860
+ } else {
2861
+ const idx = labels.indexOf(mark.label);
2862
+ if (idx < 0 || idx >= n) {
2863
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u7C7B\u76EE\u4E0D\u5B58\u5728");
2864
+ continue;
2865
+ }
2866
+ const si = Math.max(0, Math.floor(mark.seriesIndex ?? 0));
2867
+ const s = seriesList[si];
2868
+ if (!s) {
2869
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u7CFB\u5217\u4E0D\u5B58\u5728");
2870
+ continue;
2871
+ }
2872
+ const v = mark.value ?? s.values[idx] ?? NaN;
2873
+ if (!Number.isFinite(v)) {
2874
+ markWarnOnce(warned, mark, "\u6807\u8BB0\u70B9\u6570\u503C NaN");
2875
+ continue;
2876
+ }
2877
+ const dot = document.createElement("div");
2878
+ dot.className = "icen-chart-mark-point";
2879
+ dot.style.left = `${centerPct(idx).toFixed(2)}%`;
2880
+ dot.style.bottom = `${(clampNum(Math.max(0, v) / max, 0, 1) * 100).toFixed(2)}%`;
2881
+ const lab = document.createElement("span");
2882
+ lab.className = "icen-chart-mark-label";
2883
+ lab.textContent = mark.text ?? mark.label;
2884
+ dot.appendChild(lab);
2885
+ wrap.appendChild(dot);
2886
+ }
2887
+ }
2888
+ }
2889
+ function chartAriaLabel(title, typeName) {
2890
+ return title ? `${typeName}\uFF1A${title}` : typeName;
2891
+ }
2892
+ function svgDimsOf(svg) {
2893
+ const vb = (svg.getAttribute("viewBox") ?? "").split(/\s+/).map(Number);
2894
+ const w = Number.isFinite(vb[2]) && (vb[2] ?? 0) > 0 ? vb[2] : CHART_LINE_W;
2895
+ const h2 = Number.isFinite(vb[3]) && (vb[3] ?? 0) > 0 ? vb[3] : CHART_LINE_H;
2896
+ return { w, h: h2 };
2897
+ }
2898
+ var fluidObservers = /* @__PURE__ */ new WeakMap();
2899
+ function fluidWidth(host) {
2900
+ const w = Math.round(host.getBoundingClientRect().width);
2901
+ return w > 0 ? Math.max(29, w) : 360;
2902
+ }
2903
+ function observeFluidWidth(el, rerender) {
2904
+ if (typeof ResizeObserver === "undefined") return;
2905
+ fluidObservers.get(el)?.disconnect();
2906
+ let last = Math.round(el.getBoundingClientRect().width);
2907
+ const ro = new ResizeObserver(() => {
2908
+ if (!el.isConnected) {
2909
+ ro.disconnect();
2910
+ fluidObservers.delete(el);
2911
+ return;
2912
+ }
2913
+ const w = Math.round(el.getBoundingClientRect().width);
2914
+ if (Math.abs(w - last) < 2) return;
2915
+ last = w;
2916
+ rerender();
2917
+ });
2918
+ ro.observe(el);
2919
+ fluidObservers.set(el, ro);
2920
+ }
2921
+ function releaseFluidWidth(el) {
2922
+ fluidObservers.get(el)?.disconnect();
2923
+ fluidObservers.delete(el);
2924
+ }
2925
+ function createChartToDataURL(root) {
2926
+ const cache = /* @__PURE__ */ new Map();
2927
+ return (opts) => {
2928
+ if (typeof document === "undefined") return void 0;
2929
+ const scale = clampNum(toNum(opts?.scale) ?? 2, 0.5, 4);
2930
+ const svgs = Array.from(root.querySelectorAll("svg"));
2931
+ if (svgs.length === 0) return void 0;
2932
+ const parts = [];
2933
+ for (const svg of svgs) {
2934
+ const { w, h: h2 } = svgDimsOf(svg);
2935
+ const clone = svg.cloneNode(true);
2936
+ clone.setAttribute("xmlns", SVG_NS);
2937
+ clone.setAttribute("width", String(w));
2938
+ clone.setAttribute("height", String(h2));
2939
+ let str;
2940
+ try {
2941
+ str = new XMLSerializer().serializeToString(clone);
2942
+ } catch {
2943
+ return void 0;
2944
+ }
2945
+ parts.push({ src: `data:image/svg+xml;charset=utf-8,${encodeURIComponent(str)}`, w, h: h2 });
2946
+ }
2947
+ const key = `${scale}|${parts.map((p) => p.src).join("")}`;
2948
+ const hit = cache.get(key);
2949
+ if (hit) return hit;
2950
+ const canvas = document.createElement("canvas");
2951
+ let ctx = null;
2952
+ try {
2953
+ ctx = canvas.getContext("2d");
2954
+ } catch {
2955
+ return void 0;
2956
+ }
2957
+ if (!ctx) return void 0;
2958
+ const context = ctx;
2959
+ const totalW = Math.max(...parts.map((p) => p.w));
2960
+ const totalH = parts.reduce((sum2, p) => sum2 + p.h, 0);
2961
+ canvas.width = Math.max(1, Math.round(totalW * scale));
2962
+ canvas.height = Math.max(1, Math.round(totalH * scale));
2963
+ let loaded = 0;
2964
+ let failed = false;
2965
+ let offsetY = 0;
2966
+ for (const part of parts) {
2967
+ const dy = offsetY;
2968
+ offsetY += part.h;
2969
+ const img = new Image();
2970
+ img.onload = () => {
2971
+ if (failed) return;
2972
+ context.drawImage(img, 0, dy * scale, part.w * scale, part.h * scale);
2973
+ loaded += 1;
2974
+ if (loaded === parts.length) {
2975
+ try {
2976
+ cache.set(key, canvas.toDataURL("image/png"));
2977
+ } catch {
2978
+ }
2979
+ }
2980
+ };
2981
+ img.onerror = () => {
2982
+ failed = true;
2983
+ };
2984
+ img.src = part.src;
2985
+ }
2986
+ return cache.get(key);
2987
+ };
2988
+ }
1560
2989
  function dispatchChartEvent(root, type, mark, chart, phase, pointer) {
1561
2990
  const read = (name) => mark.getAttribute(name) ?? void 0;
1562
2991
  const numOr = (v) => v == null ? void 0 : Number(v);
@@ -1564,11 +2993,11 @@ function dispatchChartEvent(root, type, mark, chart, phase, pointer) {
1564
2993
  type,
1565
2994
  phase,
1566
2995
  chart,
1567
- index: numOr(read("data-chart-index")),
1568
- seriesIndex: numOr(read("data-chart-series")),
1569
- seriesName: read("data-chart-series-name"),
1570
- label: read("data-chart-label"),
1571
- value: numOr(read("data-chart-value")),
2996
+ index: numOr(read("data-icen-chart-index")),
2997
+ seriesIndex: numOr(read("data-icen-chart-series")),
2998
+ seriesName: read("data-icen-chart-series-name"),
2999
+ label: read("data-icen-chart-label"),
3000
+ value: numOr(read("data-icen-chart-value")),
1572
3001
  target: mark,
1573
3002
  pointerX: pointer?.x,
1574
3003
  pointerY: pointer?.y
@@ -1579,7 +3008,7 @@ var tooltipEl = null;
1579
3008
  function chartTooltip() {
1580
3009
  if (!tooltipEl) {
1581
3010
  tooltipEl = document.createElement("div");
1582
- tooltipEl.className = "chart-tooltip";
3011
+ tooltipEl.className = "icen-chart-tooltip";
1583
3012
  tooltipEl.setAttribute("role", "tooltip");
1584
3013
  tooltipEl.hidden = true;
1585
3014
  document.body.appendChild(tooltipEl);
@@ -1588,14 +3017,14 @@ function chartTooltip() {
1588
3017
  }
1589
3018
  function applyTooltipSizing(host) {
1590
3019
  const tip = chartTooltip();
1591
- const raw = host?.closest(".chart, [data-chart-root]")?.getAttribute("data-panel-max") ?? null;
3020
+ const raw = host?.closest(".icen-chart, [data-icen-chart-root]")?.getAttribute("data-icen-panel-max") ?? null;
1592
3021
  const n = raw != null ? Number(raw) : NaN;
1593
3022
  tip.style.maxWidth = Number.isFinite(n) && n > 0 ? `${n}px` : "";
1594
3023
  }
1595
3024
  function tooltipText(mark) {
1596
- const label = mark.getAttribute("data-chart-label") ?? "";
1597
- const series = mark.getAttribute("data-chart-series-name");
1598
- const value = mark.getAttribute("data-chart-value");
3025
+ const label = mark.getAttribute("data-icen-chart-label") ?? "";
3026
+ const series = mark.getAttribute("data-icen-chart-series-name");
3027
+ const value = mark.getAttribute("data-icen-chart-value");
1599
3028
  const head = series ? label ? `${series} \xB7 ${label}` : series : label;
1600
3029
  return value != null ? `${head}\uFF1A${value}` : head;
1601
3030
  }
@@ -1627,8 +3056,8 @@ function bindChartEvents(root) {
1627
3056
  const typeNow = () => chartDelegateMeta.get(root)?.type() ?? "vbar";
1628
3057
  const tipEnabled = () => chartDelegateMeta.get(root)?.tooltip() ?? true;
1629
3058
  const markOf = (e) => {
1630
- const t = e.target;
1631
- return t instanceof Element ? t.closest("[data-chart-mark]") : null;
3059
+ const t2 = e.target;
3060
+ return t2 instanceof Element ? t2.closest("[data-icen-chart-mark]") : null;
1632
3061
  };
1633
3062
  let hoverMark = null;
1634
3063
  const enterMark = (mark, x, y) => {
@@ -1725,70 +3154,277 @@ function hasLegendOf(spec) {
1725
3154
  function renderChart(el, raw) {
1726
3155
  const noop = () => void 0;
1727
3156
  if (typeof document === "undefined") {
1728
- return { el, spec: {}, update: noop, on: () => noop, destroy: noop };
3157
+ return {
3158
+ el,
3159
+ spec: {},
3160
+ update: noop,
3161
+ append: noop,
3162
+ toDataURL: () => void 0,
3163
+ on: () => noop,
3164
+ destroy: noop
3165
+ };
1729
3166
  }
1730
3167
  const root = el;
1731
- let current = normalizeChartSpec(raw);
1732
- const typeOf = () => current.type ?? "vbar";
3168
+ let current2 = normalizeChartSpec(raw);
3169
+ const typeOf = () => current2.type ?? "vbar";
1733
3170
  const legendState = { hidden: null };
3171
+ let zoomWindow = null;
3172
+ const warnedMarks = /* @__PURE__ */ new Set();
3173
+ let warnedAppend = false;
3174
+ let zoomDragCleanup = null;
3175
+ const fullLength = () => {
3176
+ if (current2.type === "scatter") return current2.points?.length ?? 0;
3177
+ if (current2.series && current2.series.length > 0) {
3178
+ return Math.max(current2.labels?.length ?? 0, ...current2.series.map((s) => s.values.length));
3179
+ }
3180
+ return Math.max(current2.labels?.length ?? 0, current2.values?.length ?? 0);
3181
+ };
3182
+ const viewOf = () => {
3183
+ if (!zoomWindow) return current2;
3184
+ const { from, to } = zoomWindow;
3185
+ return {
3186
+ ...current2,
3187
+ labels: current2.labels?.slice(from, to + 1),
3188
+ values: current2.values?.slice(from, to + 1),
3189
+ series: current2.series?.map((s) => ({ ...s, values: s.values.slice(from, to + 1) })),
3190
+ points: current2.points?.slice(from, to + 1)
3191
+ };
3192
+ };
3193
+ const applyZoomSelection = (x0, x1) => {
3194
+ const len = fullLength();
3195
+ if (len < 2) return;
3196
+ const base = zoomWindow?.from ?? 0;
3197
+ const viewLen = zoomWindow ? zoomWindow.to - zoomWindow.from + 1 : len;
3198
+ const lo = Math.min(x0, x1);
3199
+ const hi = Math.max(x0, x1);
3200
+ let i0;
3201
+ let i1;
3202
+ const vbar = root.querySelector(".icen-chart-vbar");
3203
+ if (vbar) {
3204
+ const rect = vbar.getBoundingClientRect();
3205
+ const rel0 = rect.width > 0 ? Math.floor((lo - rect.left) / rect.width * viewLen) : 0;
3206
+ const rel1 = rect.width > 0 ? Math.max(rel0, Math.floor((hi - rect.left) / rect.width * viewLen)) : viewLen - 1;
3207
+ i0 = clampNum(rel0 + base, 0, len - 1);
3208
+ i1 = clampNum(rel1 + base, 0, len - 1);
3209
+ } else {
3210
+ const svg = root.querySelector(".icen-chart-line svg");
3211
+ if (!svg) return;
3212
+ const dims = svgDimsOf(svg);
3213
+ const p0 = svgClientPoint(svg, lo, 0, dims.w, dims.h);
3214
+ const p1 = svgClientPoint(svg, hi, 0, dims.w, dims.h);
3215
+ const rel0 = Math.round((p0.x - CHART_LINE_PAD) / (dims.w - CHART_LINE_PAD * 2) * (viewLen - 1));
3216
+ const rel1 = Math.round((p1.x - CHART_LINE_PAD) / (dims.w - CHART_LINE_PAD * 2) * (viewLen - 1));
3217
+ i0 = clampNum(rel0 + base, 0, len - 1);
3218
+ i1 = clampNum(rel1 + base, 0, len - 1);
3219
+ }
3220
+ if (i0 > i1) [i0, i1] = [i1, i0];
3221
+ if (i0 <= 0 && i1 >= len - 1) zoomWindow = null;
3222
+ else zoomWindow = { from: i0, to: Math.max(i1, i0) };
3223
+ render();
3224
+ emitIcen(root, "icen:chart-zoom", { chart: String(current2.type ?? ""), from: i0, to: Math.max(i1, i0) });
3225
+ };
3226
+ const bindZoomGesture = () => {
3227
+ if (current2.zoom !== "x" && current2.zoom !== "brush") return;
3228
+ const t2 = typeOf();
3229
+ if (!isZoomableType(t2) || fullLength() < 2) return;
3230
+ const plot = t2 === "vbar" ? root.querySelector(".icen-chart-vbar") : root.querySelector(".icen-chart-line svg");
3231
+ if (!plot) return;
3232
+ const on = (type, fn) => {
3233
+ plot.addEventListener(type, fn);
3234
+ };
3235
+ let drag = null;
3236
+ const onMove = (e) => {
3237
+ if (!drag) return;
3238
+ drag.x1 = e.clientX;
3239
+ drag.y1 = e.clientY;
3240
+ if (!drag.active && Math.hypot(drag.x1 - drag.x0, drag.y1 - drag.y0) >= CHART_ZOOM_DRAG_MIN_PX) {
3241
+ drag.active = true;
3242
+ const rect = document.createElement("div");
3243
+ rect.className = "icen-chart-zoom-rect";
3244
+ document.body.appendChild(rect);
3245
+ drag.rect = rect;
3246
+ }
3247
+ if (drag.active && drag.rect) {
3248
+ drag.rect.style.left = `${Math.round(Math.min(drag.x0, drag.x1))}px`;
3249
+ drag.rect.style.width = `${Math.round(Math.abs(drag.x1 - drag.x0))}px`;
3250
+ if (current2.zoom === "brush") {
3251
+ drag.rect.style.top = `${Math.round(Math.min(drag.y0, drag.y1))}px`;
3252
+ drag.rect.style.height = `${Math.round(Math.abs(drag.y1 - drag.y0))}px`;
3253
+ } else {
3254
+ const pr = plot.getBoundingClientRect();
3255
+ drag.rect.style.top = `${Math.round(pr.top)}px`;
3256
+ drag.rect.style.height = `${Math.round(pr.height)}px`;
3257
+ }
3258
+ }
3259
+ };
3260
+ const onUp = () => {
3261
+ window.removeEventListener("pointermove", onMove);
3262
+ window.removeEventListener("pointerup", onUp);
3263
+ window.removeEventListener("pointercancel", onUp);
3264
+ zoomDragCleanup = null;
3265
+ if (!drag) return;
3266
+ const d = drag;
3267
+ drag = null;
3268
+ d.rect?.remove();
3269
+ if (!d.active) return;
3270
+ applyZoomSelection(d.x0, d.x1);
3271
+ };
3272
+ const onDown = (e) => {
3273
+ if (e.button !== 0) return;
3274
+ drag = { x0: e.clientX, y0: e.clientY, x1: e.clientX, y1: e.clientY, active: false, rect: null };
3275
+ window.addEventListener("pointermove", onMove);
3276
+ window.addEventListener("pointerup", onUp);
3277
+ window.addEventListener("pointercancel", onUp);
3278
+ zoomDragCleanup = onUp;
3279
+ };
3280
+ on("pointerdown", onDown);
3281
+ on("dblclick", () => {
3282
+ if (!zoomWindow) return;
3283
+ zoomWindow = null;
3284
+ render();
3285
+ });
3286
+ };
1734
3287
  const render = () => {
1735
- if (!current.type) current = { ...current, type: inferChartType(current) };
1736
- const spec = current;
3288
+ if (!current2.type) current2 = { ...current2, type: inferChartType(current2) };
3289
+ const spec = current2;
3290
+ const type = spec.type;
1737
3291
  const formatValue = chartFormatValue(spec.format);
1738
3292
  if (spec.legend !== void 0) legendState.hidden = spec.legend === false;
3293
+ const loading = spec.loading === true;
3294
+ root.classList.toggle("is-loading", loading);
3295
+ let view = viewOf();
3296
+ let note = null;
3297
+ if (!loading && isDownsampleableType(type)) {
3298
+ const r = downsampleView(view, type);
3299
+ view = r.view;
3300
+ note = r.note;
3301
+ }
3302
+ const zoomReset = zoomWindow && (spec.zoom === "x" || spec.zoom === "brush") ? () => {
3303
+ zoomWindow = null;
3304
+ render();
3305
+ } : null;
1739
3306
  withChrome(root, spec, hasLegendOf(spec), (body) => {
1740
- switch (spec.type) {
3307
+ if (loading) {
3308
+ renderChartSkeleton(body);
3309
+ return;
3310
+ }
3311
+ switch (type) {
1741
3312
  case "line":
1742
- 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 });
3313
+ 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 });
1743
3314
  break;
1744
3315
  case "area":
1745
- 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 });
3316
+ 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 });
1746
3317
  break;
1747
3318
  case "vbar":
1748
- 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 });
3319
+ 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 });
1749
3320
  break;
1750
3321
  case "hbar":
1751
- renderHBar(body, { labels: spec.labels ?? [], values: spec.values ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3322
+ renderHBar(body, { labels: view.labels ?? [], values: view.values ?? [], tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1752
3323
  break;
1753
3324
  case "stack":
1754
- renderStack(body, { segments: spec.segments ?? [], formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3325
+ renderStack(body, { segments: view.segments ?? [], formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1755
3326
  break;
1756
3327
  case "donut":
1757
- renderDonut(body, { segments: spec.segments ?? [], formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3328
+ renderDonut(body, { segments: view.segments ?? [], formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1758
3329
  break;
1759
3330
  case "radar": {
1760
- 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 }] : [];
1761
- renderRadar(body, { axes: spec.axes ?? spec.labels ?? [], series, max: spec.max, levels: spec.levels, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3331
+ 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 }] : [];
3332
+ renderRadar(body, { axes: view.axes ?? view.labels ?? [], series, max: view.max, levels: view.levels, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1762
3333
  break;
1763
3334
  }
1764
3335
  case "heatmap":
1765
- 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 });
3336
+ 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 });
1766
3337
  break;
1767
3338
  case "calendar":
1768
- 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 });
3339
+ 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 });
1769
3340
  break;
1770
3341
  case "sparkline":
1771
- renderSparkline(body, { labels: spec.labels ?? [], values: spec.values ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3342
+ renderSparkline(body, { labels: view.labels ?? [], values: view.values ?? [], tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1772
3343
  break;
1773
3344
  case "gauge":
1774
- renderGauge(body, { value: spec.values?.[0] ?? 0, max: spec.max, tone: spec.tone, label: spec.labels?.[0], formatValue });
3345
+ renderGauge(body, { value: view.values?.[0] ?? 0, max: view.max, tone: view.tone, label: view.labels?.[0], formatValue });
1775
3346
  break;
1776
3347
  case "scatter":
1777
- renderScatter(body, { points: spec.points ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
3348
+ renderScatter(body, { points: view.points ?? [], tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
1778
3349
  break;
1779
3350
  default:
1780
- renderEmpty(body, spec.emptyLabel);
3351
+ renderEmpty(body, view.emptyLabel);
1781
3352
  }
1782
- });
3353
+ }, legendState, { note, onZoomReset: zoomReset });
3354
+ if (!loading) {
3355
+ const graphic = root.querySelector('[role="img"]');
3356
+ if (graphic && !graphic.getAttribute("aria-label")) {
3357
+ graphic.setAttribute("aria-label", spec.title ? `${spec.title}\uFF08${type}\uFF09` : type);
3358
+ }
3359
+ }
3360
+ if (loading) return;
3361
+ paintChartMarks(root, view, spec, formatValue, warnedMarks);
3362
+ bindZoomGesture();
1783
3363
  };
1784
- chartDelegateMeta.set(root, { type: typeOf, tooltip: () => current.tooltip !== false });
3364
+ chartDelegateMeta.set(root, { type: typeOf, tooltip: () => current2.tooltip !== false });
1785
3365
  const unbindEvents = bindChartEvents(root);
1786
3366
  render();
3367
+ observeFluidWidth(root, render);
1787
3368
  return {
1788
3369
  el: root,
1789
- spec: current,
3370
+ /** 归一 + 推断后的当前规格(getter:update/append 换引用后永远读到最新) */
3371
+ get spec() {
3372
+ return current2;
3373
+ },
1790
3374
  update(next) {
1791
- current = normalizeChartSpec(next);
3375
+ const prev = current2;
3376
+ current2 = normalizeChartSpec(next);
3377
+ if (zoomWindow) {
3378
+ const sameLabels = JSON.stringify(prev.labels ?? null) === JSON.stringify(current2.labels ?? null);
3379
+ const t2 = current2.type ?? inferChartType(current2);
3380
+ const zoomable = isZoomableType(t2) && (current2.zoom === "x" || current2.zoom === "brush");
3381
+ if (!sameLabels || !zoomable) zoomWindow = null;
3382
+ else {
3383
+ const len = fullLength();
3384
+ if (len < 2) zoomWindow = null;
3385
+ else {
3386
+ const from = clampNum(zoomWindow.from, 0, len - 1);
3387
+ const to = clampNum(zoomWindow.to, 0, len - 1);
3388
+ zoomWindow = from <= to ? { from, to } : null;
3389
+ }
3390
+ }
3391
+ }
3392
+ render();
3393
+ },
3394
+ append(label, values, opts) {
3395
+ const t2 = current2.type ?? inferChartType(current2);
3396
+ if (!isStreamableType(t2)) {
3397
+ if (!warnedAppend) {
3398
+ warnedAppend = true;
3399
+ console.warn(`[icen:chart] append \u4EC5\u652F\u6301 line/area/vbar\uFF08\u5F53\u524D ${String(current2.type ?? t2)}\uFF09\uFF0C\u5DF2\u5FFD\u7565`);
3400
+ }
3401
+ return;
3402
+ }
3403
+ const labels = current2.labels ?? (current2.labels = []);
3404
+ labels.push(String(label ?? ""));
3405
+ const vals = Array.isArray(values) ? values : [];
3406
+ if (current2.series && current2.series.length > 0) {
3407
+ for (const [i, s] of current2.series.entries()) s.values.push(toNum(vals[i]) ?? 0);
3408
+ } else {
3409
+ const v = current2.values ?? (current2.values = []);
3410
+ v.push(toNum(vals[0]) ?? 0);
3411
+ }
3412
+ const maxPoints = opts?.maxPoints != null ? Math.max(1, Math.floor(toNum(opts.maxPoints) ?? 0)) : 0;
3413
+ let dropped = 0;
3414
+ if (maxPoints > 0) {
3415
+ while (labels.length > maxPoints) {
3416
+ labels.shift();
3417
+ if (current2.series) for (const s of current2.series) s.values.shift();
3418
+ else current2.values?.shift();
3419
+ dropped += 1;
3420
+ }
3421
+ }
3422
+ if (zoomWindow) {
3423
+ const len = Math.max(1, labels.length);
3424
+ const size = zoomWindow.to - zoomWindow.from + 1;
3425
+ const to = clampNum(zoomWindow.to - dropped + 1, 0, len - 1);
3426
+ zoomWindow = { from: Math.max(0, to - size + 1), to };
3427
+ }
1792
3428
  render();
1793
3429
  },
1794
3430
  on(name, listener) {
@@ -1798,13 +3434,29 @@ function renderChart(el, raw) {
1798
3434
  root.addEventListener(`icen:chart-${name}`, handler);
1799
3435
  return () => root.removeEventListener(`icen:chart-${name}`, handler);
1800
3436
  },
3437
+ toDataURL: createChartToDataURL(root),
1801
3438
  destroy() {
3439
+ releaseFluidWidth(root);
3440
+ hiddenLegendKeys.delete(root);
3441
+ zoomDragCleanup?.();
1802
3442
  root.textContent = "";
3443
+ root.classList.remove("is-loading");
1803
3444
  unbindEvents();
1804
3445
  chartTooltip().hidden = true;
1805
3446
  }
1806
3447
  };
1807
3448
  }
3449
+ function svgClientPoint(svg, clientX, clientY, viewBoxWidth, viewBoxHeight) {
3450
+ const rect = svg.getBoundingClientRect();
3451
+ const scale = Math.min(rect.width / viewBoxWidth, rect.height / viewBoxHeight);
3452
+ if (!Number.isFinite(scale) || scale <= 0) {
3453
+ return { x: viewBoxWidth / 2, y: viewBoxHeight / 2 };
3454
+ }
3455
+ return {
3456
+ x: (clientX - rect.left - (rect.width - viewBoxWidth * scale) / 2) / scale,
3457
+ y: (clientY - rect.top - (rect.height - viewBoxHeight * scale) / 2) / scale
3458
+ };
3459
+ }
1808
3460
  var GRAPH_W = 360;
1809
3461
  var GRAPH_H = 260;
1810
3462
  var GRAPH_PAD = 28;
@@ -1813,24 +3465,24 @@ var GRAPH_LH = GRAPH_H - GRAPH_PAD * 2;
1813
3465
 
1814
3466
  // src/behaviors/kb-answer.ts
1815
3467
  var ASSET_TYPE_LABELS = {
1816
- "parameterized-query": "\u53C2\u6570\u5316\u67E5\u8BE2",
1817
- function: "\u51FD\u6570",
1818
- curated: "\u4EBA\u5DE5\u7B56\u5C55"
3468
+ "parameterized-query": t("kbAnswer.kindParameterized"),
3469
+ function: t("kbAnswer.kindFunction"),
3470
+ curated: t("kbAnswer.kindCurated")
1819
3471
  };
1820
3472
  var ICON_TRUSTED = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.7 9a.6.6 0 0 1-.6 0C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.2-2.7a1.2 1.2 0 0 1 1.6 0C14.5 3.8 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/></svg>';
1821
3473
  var ICON_SHIELD = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.7 9a.6.6 0 0 1-.6 0C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.2-2.7a1.2 1.2 0 0 1 1.6 0C14.5 3.8 17 5 19 5a1 1 0 0 1 1 1z"/></svg>';
1822
3474
  function renderVerifiedBadge(via, opts) {
1823
- const btn = h("button", "kb-verified");
3475
+ const btn = h("button", "icen-kb-verified");
1824
3476
  btn.type = "button";
1825
- if (via.assetId) btn.dataset.assetId = via.assetId;
1826
- const badge = h("span", "kb-badge kb-badge--trusted");
1827
- const svg2 = svgIcon(ICON_TRUSTED);
1828
- if (svg2) badge.appendChild(svg2);
1829
- badge.appendChild(document.createTextNode("\u8BA4\u8BC1\u7B54\u6848"));
3477
+ if (via.assetId) btn.dataset.icenAssetId = via.assetId;
3478
+ const badge = h("span", "icen-kb-badge icen-kb-badge--trusted");
3479
+ const svg = svgIcon(ICON_TRUSTED);
3480
+ if (svg) badge.appendChild(svg);
3481
+ badge.appendChild(document.createTextNode(t("kbAnswer.verifiedBadge")));
1830
3482
  btn.appendChild(badge);
1831
- btn.appendChild(h("span", "kb-verified-via", `\u547D\u4E2D\u8D44\u4EA7\uFF1A${ASSET_TYPE_LABELS[via.assetType] ?? via.assetType}`));
1832
- if (via.note) btn.appendChild(h("span", "kb-verified-note", via.note));
1833
- btn.appendChild(h("span", "kb-verified-hint", "\u53C2\u6570\u53EF\u6539\u91CD\u8DD1"));
3483
+ btn.appendChild(h("span", "icen-kb-verified-via", t("kbAnswer.hitAssets", { p0: ASSET_TYPE_LABELS[via.assetType] ?? via.assetType })));
3484
+ if (via.note) btn.appendChild(h("span", "icen-kb-verified-note", via.note));
3485
+ btn.appendChild(h("span", "icen-kb-verified-hint", t("kbAnswer.rerunTip")));
1834
3486
  btn.addEventListener("click", () => {
1835
3487
  emitIcen(btn, "icen:kb-verified-open", { assetId: via.assetId });
1836
3488
  opts?.onVerifiedOpen?.(via.assetId);
@@ -1838,26 +3490,26 @@ function renderVerifiedBadge(via, opts) {
1838
3490
  return btn;
1839
3491
  }
1840
3492
  function renderPermissionNotice(p) {
1841
- const box = h("div", "kb-perm");
1842
- const iconBox = h("span", "kb-perm-icon");
1843
- const svg2 = svgIcon(ICON_SHIELD);
1844
- if (svg2) iconBox.appendChild(svg2);
3493
+ const box = h("div", "icen-kb-perm");
3494
+ const iconBox = h("span", "icen-kb-perm-icon");
3495
+ const svg = svgIcon(ICON_SHIELD);
3496
+ if (svg) iconBox.appendChild(svg);
1845
3497
  box.appendChild(iconBox);
1846
- box.appendChild(h("span", "kb-perm-text", p.rowLevelPolicy ? `\u5DF2\u6309\u4F60\u7684\u6570\u636E\u6743\u9650\u8FC7\u6EE4\uFF1A${p.rowLevelPolicy}` : "\u5DF2\u6309\u4F60\u7684\u6570\u636E\u6743\u9650\u8FC7\u6EE4"));
3498
+ box.appendChild(h("span", "kb-perm-text", p.rowLevelPolicy ? t("kbAnswer.filteredDetail", { p0: p.rowLevelPolicy }) : t("kbAnswer.filtered")));
1847
3499
  if (p.maskedColumns?.length) {
1848
- const mask = h("span", "kb-perm-mask");
1849
- mask.appendChild(h("span", "kb-perm-mask-label", "\u5DF2\u8131\u654F\u5217\uFF1A"));
1850
- for (const col of p.maskedColumns) mask.appendChild(h("span", "kb-chip", col));
3500
+ const mask = h("span", "icen-kb-perm-mask");
3501
+ mask.appendChild(h("span", "icen-kb-perm-mask-label", t("kbAnswer.maskedPrefix")));
3502
+ for (const col of p.maskedColumns) mask.appendChild(h("span", "icen-kb-chip", col));
1851
3503
  box.appendChild(mask);
1852
3504
  }
1853
3505
  return box;
1854
3506
  }
1855
3507
  function renderBody(text, citations) {
1856
- const box = h("div", "kb-answer-body");
3508
+ const box = h("div", "icen-kb-answer-body");
1857
3509
  for (const seg of parseInlineCitations(text)) {
1858
3510
  if (typeof seg.n === "number" && citations && seg.n >= 1 && seg.n <= citations.length) {
1859
- const sup = h("sup", "kb-citation", `[${seg.n}]`);
1860
- sup.dataset.cite = String(seg.n);
3511
+ const sup = h("sup", "icen-kb-citation", `[${seg.n}]`);
3512
+ sup.dataset.icenCite = String(seg.n);
1861
3513
  const c = citations[seg.n - 1];
1862
3514
  if (c?.title) sup.title = c.title;
1863
3515
  box.appendChild(sup);
@@ -1890,18 +3542,18 @@ function isNumericColumn(col, colIndex, rows) {
1890
3542
  }
1891
3543
  function renderResult(result, opts) {
1892
3544
  if (!result.columns.length) return null;
1893
- const wrap = h("div", "kb-answer-results");
1894
- const meta = h("div", "kb-result-meta");
1895
- meta.appendChild(h("span", "kb-num", `${formatCount(result.rowCount)} \u884C`));
3545
+ const wrap = h("div", "icen-kb-answer-results");
3546
+ const meta = h("div", "icen-kb-result-meta");
3547
+ meta.appendChild(h("span", "icen-kb-num", t("common.rows", { p0: formatCount(result.rowCount) })));
1896
3548
  if (result.durationMs != null) {
1897
- meta.appendChild(h("span", "kb-num kb-result-dur", `${Math.round(result.durationMs)} ms`));
3549
+ meta.appendChild(h("span", "icen-kb-num kb-result-dur", `${Math.round(result.durationMs)} ms`));
1898
3550
  }
1899
3551
  if (result.exportFormats?.length) {
1900
- const exports = h("span", "kb-result-export");
3552
+ const exports = h("span", "icen-kb-result-export");
1901
3553
  for (const fmt2 of result.exportFormats) {
1902
- const btn = h("button", "kb-result-export-btn", `\u5BFC\u51FA ${fmt2}`);
3554
+ const btn = h("button", "icen-kb-result-export-btn", t("kbAnswer.exportNoun", { p0: fmt2 }));
1903
3555
  btn.type = "button";
1904
- btn.dataset.format = fmt2;
3556
+ btn.dataset.icenFormat = fmt2;
1905
3557
  if (!opts?.onExport) btn.disabled = true;
1906
3558
  else btn.addEventListener("click", () => opts.onExport?.(fmt2));
1907
3559
  exports.appendChild(btn);
@@ -1909,13 +3561,13 @@ function renderResult(result, opts) {
1909
3561
  meta.appendChild(exports);
1910
3562
  }
1911
3563
  wrap.appendChild(meta);
1912
- const scroll = h("div", "kb-result-scroll");
1913
- const table = h("table", "kb-result-table");
3564
+ const scroll = h("div", "icen-kb-result-scroll");
3565
+ const table = h("table", "icen-kb-result-table");
1914
3566
  const numCols = result.columns.map((c, i) => isNumericColumn(c, i, result.rows));
1915
3567
  const thead = document.createElement("thead");
1916
3568
  const headRow = document.createElement("tr");
1917
3569
  result.columns.forEach((c, i) => {
1918
- headRow.appendChild(h("th", numCols[i] ? "kb-num" : "", c.name));
3570
+ headRow.appendChild(h("th", numCols[i] ? "icen-kb-num" : "", c.name));
1919
3571
  });
1920
3572
  thead.appendChild(headRow);
1921
3573
  table.appendChild(thead);
@@ -1925,10 +3577,10 @@ function renderResult(result, opts) {
1925
3577
  result.columns.forEach((_, i) => {
1926
3578
  const v = row[i];
1927
3579
  if (v == null) {
1928
- const td = h("td", "kb-result-null", "\u2014");
3580
+ const td = h("td", "icen-kb-result-null", "\u2014");
1929
3581
  tr.appendChild(td);
1930
3582
  } else {
1931
- tr.appendChild(h("td", typeof v === "number" ? "kb-num" : "", String(v)));
3583
+ tr.appendChild(h("td", typeof v === "number" ? "icen-kb-num" : "", String(v)));
1932
3584
  }
1933
3585
  });
1934
3586
  tbody.appendChild(tr);
@@ -1939,14 +3591,14 @@ function renderResult(result, opts) {
1939
3591
  if (result.truncated) {
1940
3592
  const total = result.totalRows != null ? formatCount(result.totalRows) : "\u2014";
1941
3593
  const limitPart = result.limit != null ? `\uFF08LIMIT ${formatCount(result.limit)}\uFF09` : "";
1942
- wrap.appendChild(h("div", "kb-notice kb-result-truncated", `\u5DF2\u622A\u65AD\uFF1A\u663E\u793A ${formatCount(result.rowCount)}/${total} \u884C${limitPart}`));
3594
+ wrap.appendChild(h("div", "icen-kb-notice icen-kb-result-truncated", t("kbAnswer.truncated", { p0: formatCount(result.rowCount), p1: total, p2: limitPart })));
1943
3595
  }
1944
3596
  return wrap;
1945
3597
  }
1946
3598
  var CHART_SWITCHES = [
1947
- { type: "line", label: "\u6298\u7EBF" },
1948
- { type: "vbar", label: "\u67F1\u72B6" },
1949
- { type: "donut", label: "\u997C\u56FE" }
3599
+ { type: "line", label: t("kbAnswer.chartLine") },
3600
+ { type: "vbar", label: t("kbAnswer.chartBar") },
3601
+ { type: "donut", label: t("kbAnswer.chartPie") }
1950
3602
  ];
1951
3603
  function switchable(spec) {
1952
3604
  const hasCat = (spec.labels?.length ?? 0) > 0 && ((spec.values?.length ?? 0) > 0 || (spec.series?.length ?? 0) > 0);
@@ -1977,14 +3629,14 @@ function specAsType(base, target) {
1977
3629
  function renderChartArea(raw) {
1978
3630
  if (raw == null) return null;
1979
3631
  const norm = normalizeChartSpec(raw);
1980
- const area = h("div", "kb-answer-chart");
1981
- const mount = h("div", "chart kb-chart-mount");
3632
+ const area = h("div", "icen-kb-answer-chart");
3633
+ const mount = h("div", "icen-chart icen-kb-chart-mount");
1982
3634
  const handle = renderChart(mount, norm);
1983
3635
  if (switchable(handle.spec)) {
1984
- const switchRow = h("div", "kb-chart-switch");
3636
+ const switchRow = h("div", "icen-kb-chart-switch");
1985
3637
  const chips = /* @__PURE__ */ new Map();
1986
3638
  for (const s of CHART_SWITCHES) {
1987
- const chip = h("button", "kb-chart-chip", s.label);
3639
+ const chip = h("button", "icen-kb-chart-chip", s.label);
1988
3640
  chip.type = "button";
1989
3641
  chip.dataset.chartType = s.type;
1990
3642
  if (handle.spec.type === s.type) chip.classList.add("is-active");
@@ -2006,9 +3658,9 @@ function renderKbAnswer(el, model, opts) {
2006
3658
  if (typeof document === "undefined") return el;
2007
3659
  const m = model ?? {};
2008
3660
  el.textContent = "";
2009
- const root = h("article", "kb-answer");
2010
- root.dataset.kind = m.kind === "verified" ? "verified" : "generated";
2011
- const badges = h("div", "kb-answer-badges");
3661
+ const root = h("article", "icen-kb-answer");
3662
+ root.dataset.icenKind = m.kind === "verified" ? "verified" : "generated";
3663
+ const badges = h("div", "icen-kb-answer-badges");
2012
3664
  if (m.kind === "verified" && m.verifiedVia) badges.appendChild(renderVerifiedBadge(m.verifiedVia, opts));
2013
3665
  if (m.permissionNotice && (m.permissionNotice.rowLevelPolicy || m.permissionNotice.maskedColumns?.length)) {
2014
3666
  badges.appendChild(renderPermissionNotice(m.permissionNotice));
@@ -2016,7 +3668,7 @@ function renderKbAnswer(el, model, opts) {
2016
3668
  if (badges.children.length > 0) root.appendChild(badges);
2017
3669
  if (typeof m.body === "string" && m.body) root.appendChild(renderBody(m.body, m.citations));
2018
3670
  if (m.clarify && (m.clarify.question || m.clarify.options?.length)) {
2019
- const slot = h("div", "kb-answer-clarify");
3671
+ const slot = h("div", "icen-kb-answer-clarify");
2020
3672
  renderKbClarify(slot, m.clarify);
2021
3673
  root.appendChild(slot);
2022
3674
  }
@@ -2024,7 +3676,7 @@ function renderKbAnswer(el, model, opts) {
2024
3676
  const f = m.explain;
2025
3677
  const hasAny = f.tables?.length || f.columns?.length || f.filters?.length || f.aggregates?.length || f.metrics?.length;
2026
3678
  if (hasAny) {
2027
- const slot = h("div", "kb-answer-explain");
3679
+ const slot = h("div", "icen-kb-answer-explain");
2028
3680
  renderKbExplain(slot, f);
2029
3681
  root.appendChild(slot);
2030
3682
  }
@@ -2037,10 +3689,10 @@ function renderKbAnswer(el, model, opts) {
2037
3689
  if (chartArea) root.appendChild(chartArea);
2038
3690
  const followUps = (m.followUps ?? []).map((s) => String(s ?? "").trim()).filter(Boolean);
2039
3691
  if (followUps.length > 0) {
2040
- const row = h("div", "kb-answer-followups");
2041
- row.appendChild(h("span", "kb-followups-label", "\u8FFD\u95EE\u5EFA\u8BAE"));
3692
+ const row = h("div", "icen-kb-answer-followups");
3693
+ row.appendChild(h("span", "icen-kb-followups-label", t("kbAnswer.followups")));
2042
3694
  for (const text of followUps) {
2043
- const chip = h("button", "kb-chip kb-followup", text);
3695
+ const chip = h("button", "icen-kb-chip icen-kb-followup", text);
2044
3696
  chip.type = "button";
2045
3697
  chip.addEventListener("click", () => opts?.onFollowUp?.(text));
2046
3698
  row.appendChild(chip);