@icen.ai/ui 0.8.1 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (335) hide show
  1. package/AGENTS.md +176 -75
  2. package/README.md +66 -88
  3. package/dist/base.css +208 -179
  4. package/dist/behaviors/accordion.mjs +8 -8
  5. package/dist/behaviors/ai-branch.d.mts +19 -0
  6. package/dist/behaviors/ai-branch.mjs +1133 -0
  7. package/dist/behaviors/ai-chat.d.mts +12 -10
  8. package/dist/behaviors/ai-chat.mjs +1125 -155
  9. package/dist/behaviors/ai-composer.d.mts +44 -12
  10. package/dist/behaviors/ai-composer.mjs +2084 -523
  11. package/dist/behaviors/ai-core.d.mts +1 -1
  12. package/dist/behaviors/ai-core.mjs +1016 -107
  13. package/dist/behaviors/ai-diff.mjs +1019 -127
  14. package/dist/behaviors/ai-feedback.d.mts +26 -0
  15. package/dist/behaviors/ai-feedback.mjs +1212 -0
  16. package/dist/behaviors/ai-panel.d.mts +21 -7
  17. package/dist/behaviors/ai-panel.mjs +1308 -264
  18. package/dist/behaviors/ai-provider.mjs +1003 -101
  19. package/dist/behaviors/ai-threads.d.mts +38 -0
  20. package/dist/behaviors/ai-threads.mjs +1233 -0
  21. package/dist/behaviors/ai-tool.d.mts +2 -2
  22. package/dist/behaviors/ai-tool.mjs +2042 -396
  23. package/dist/behaviors/ai-tools.d.mts +89 -16
  24. package/dist/behaviors/ai-tools.mjs +4453 -372
  25. package/dist/behaviors/autocomplete.d.mts +53 -0
  26. package/dist/behaviors/autocomplete.mjs +1600 -0
  27. package/dist/behaviors/back-top.d.mts +1 -1
  28. package/dist/behaviors/back-top.mjs +951 -14
  29. package/dist/behaviors/carousel.d.mts +1 -1
  30. package/dist/behaviors/carousel.mjs +906 -11
  31. package/dist/behaviors/cascader.d.mts +48 -0
  32. package/dist/behaviors/cascader.mjs +1853 -0
  33. package/dist/behaviors/charts.d.mts +174 -8
  34. package/dist/behaviors/charts.mjs +2632 -192
  35. package/dist/behaviors/code-block.d.mts +36 -0
  36. package/dist/behaviors/code-block.mjs +1611 -0
  37. package/dist/behaviors/command-palette.d.mts +9 -6
  38. package/dist/behaviors/command-palette.mjs +161 -59
  39. package/dist/behaviors/context-menu.d.mts +9 -8
  40. package/dist/behaviors/context-menu.mjs +1112 -44
  41. package/dist/behaviors/controls.d.mts +13 -13
  42. package/dist/behaviors/controls.mjs +989 -130
  43. package/dist/behaviors/copy.d.mts +21 -2
  44. package/dist/behaviors/copy.mjs +1206 -42
  45. package/dist/behaviors/datatable.d.mts +53 -3
  46. package/dist/behaviors/datatable.mjs +1963 -207
  47. package/dist/behaviors/date-picker.d.mts +0 -1
  48. package/dist/behaviors/date-picker.mjs +1340 -293
  49. package/dist/behaviors/design.d.mts +54 -0
  50. package/dist/behaviors/design.mjs +109 -0
  51. package/dist/behaviors/dropdown.d.mts +5 -6
  52. package/dist/behaviors/dropdown.mjs +262 -110
  53. package/dist/behaviors/events.d.mts +313 -3
  54. package/dist/behaviors/events.mjs +47 -2
  55. package/dist/behaviors/feedback.d.mts +30 -0
  56. package/dist/behaviors/feedback.mjs +296 -0
  57. package/dist/behaviors/focus.d.mts +8 -0
  58. package/dist/behaviors/focus.mjs +24 -0
  59. package/dist/behaviors/form-validate.d.mts +102 -0
  60. package/dist/behaviors/form-validate.mjs +6091 -0
  61. package/dist/behaviors/i18n.d.mts +789 -0
  62. package/dist/behaviors/i18n.mjs +1711 -0
  63. package/dist/behaviors/icons.d.mts +93 -0
  64. package/dist/behaviors/icons.mjs +113 -0
  65. package/dist/behaviors/input.d.mts +9 -1
  66. package/dist/behaviors/input.mjs +1056 -60
  67. package/dist/behaviors/kb-access.d.mts +34 -0
  68. package/dist/behaviors/kb-access.mjs +2459 -0
  69. package/dist/behaviors/kb-acl.d.mts +50 -0
  70. package/dist/behaviors/kb-acl.mjs +1345 -0
  71. package/dist/behaviors/kb-answer.d.mts +38 -0
  72. package/dist/behaviors/kb-answer.mjs +3707 -0
  73. package/dist/behaviors/kb-audit.d.mts +23 -0
  74. package/dist/behaviors/kb-audit.mjs +1297 -0
  75. package/dist/behaviors/kb-canvas.d.mts +44 -0
  76. package/dist/behaviors/kb-canvas.mjs +1512 -0
  77. package/dist/behaviors/kb-chain.d.mts +15 -0
  78. package/dist/behaviors/kb-chain.mjs +1192 -0
  79. package/dist/behaviors/kb-checkpoint.d.mts +16 -0
  80. package/dist/behaviors/kb-checkpoint.mjs +1492 -0
  81. package/dist/behaviors/kb-chunks.d.mts +31 -0
  82. package/dist/behaviors/kb-chunks.mjs +1414 -0
  83. package/dist/behaviors/kb-citation.d.mts +38 -0
  84. package/dist/behaviors/kb-citation.mjs +1699 -0
  85. package/dist/behaviors/kb-clarify.d.mts +20 -0
  86. package/dist/behaviors/kb-clarify.mjs +1176 -0
  87. package/dist/behaviors/kb-conflict.d.mts +22 -0
  88. package/dist/behaviors/kb-conflict.mjs +1206 -0
  89. package/dist/behaviors/kb-connector.d.mts +17 -0
  90. package/dist/behaviors/kb-connector.mjs +1303 -0
  91. package/dist/behaviors/kb-core.d.mts +534 -0
  92. package/dist/behaviors/kb-core.mjs +1805 -0
  93. package/dist/behaviors/kb-eval.d.mts +22 -0
  94. package/dist/behaviors/kb-eval.mjs +2235 -0
  95. package/dist/behaviors/kb-explain.d.mts +11 -0
  96. package/dist/behaviors/kb-explain.mjs +1165 -0
  97. package/dist/behaviors/kb-filter.d.mts +31 -0
  98. package/dist/behaviors/kb-filter.mjs +2417 -0
  99. package/dist/behaviors/kb-gap.d.mts +27 -0
  100. package/dist/behaviors/kb-gap.mjs +1195 -0
  101. package/dist/behaviors/kb-hittest.d.mts +34 -0
  102. package/dist/behaviors/kb-hittest.mjs +1352 -0
  103. package/dist/behaviors/kb-metadata.d.mts +29 -0
  104. package/dist/behaviors/kb-metadata.mjs +1248 -0
  105. package/dist/behaviors/kb-passage.d.mts +41 -0
  106. package/dist/behaviors/kb-passage.mjs +1469 -0
  107. package/dist/behaviors/kb-pipeline.d.mts +18 -0
  108. package/dist/behaviors/kb-pipeline.mjs +1301 -0
  109. package/dist/behaviors/kb-qa.d.mts +26 -0
  110. package/dist/behaviors/kb-qa.mjs +1260 -0
  111. package/dist/behaviors/kb-rerank.d.mts +20 -0
  112. package/dist/behaviors/kb-rerank.mjs +1248 -0
  113. package/dist/behaviors/kb-retrieval.d.mts +49 -0
  114. package/dist/behaviors/kb-retrieval.mjs +2585 -0
  115. package/dist/behaviors/kb-review.d.mts +44 -0
  116. package/dist/behaviors/kb-review.mjs +2282 -0
  117. package/dist/behaviors/kb-sandbox.d.mts +33 -0
  118. package/dist/behaviors/kb-sandbox.mjs +1247 -0
  119. package/dist/behaviors/kb-segment.d.mts +32 -0
  120. package/dist/behaviors/kb-segment.mjs +1431 -0
  121. package/dist/behaviors/kb-select.d.mts +24 -0
  122. package/dist/behaviors/kb-select.mjs +1989 -0
  123. package/dist/behaviors/kb-sources.d.mts +27 -0
  124. package/dist/behaviors/kb-sources.mjs +1287 -0
  125. package/dist/behaviors/kb-sql.d.mts +43 -0
  126. package/dist/behaviors/kb-sql.mjs +1865 -0
  127. package/dist/behaviors/kb-trace.d.mts +16 -0
  128. package/dist/behaviors/kb-trace.mjs +1328 -0
  129. package/dist/behaviors/kb-visibility.d.mts +44 -0
  130. package/dist/behaviors/kb-visibility.mjs +1331 -0
  131. package/dist/behaviors/kb-who-can.d.mts +32 -0
  132. package/dist/behaviors/kb-who-can.mjs +2216 -0
  133. package/dist/behaviors/mentions.d.mts +93 -0
  134. package/dist/behaviors/mentions.mjs +2152 -0
  135. package/dist/behaviors/modal.d.mts +4 -3
  136. package/dist/behaviors/modal.mjs +1055 -33
  137. package/dist/behaviors/nav.d.mts +1 -1
  138. package/dist/behaviors/nav.mjs +6 -6
  139. package/dist/behaviors/notification.d.mts +16 -8
  140. package/dist/behaviors/notification.mjs +1615 -265
  141. package/dist/behaviors/panel.d.mts +4 -0
  142. package/dist/behaviors/panel.mjs +994 -0
  143. package/dist/behaviors/popover.d.mts +28 -5
  144. package/dist/behaviors/popover.mjs +225 -24
  145. package/dist/behaviors/select.d.mts +5 -2
  146. package/dist/behaviors/select.mjs +1410 -117
  147. package/dist/behaviors/sidebar.d.mts +1 -1
  148. package/dist/behaviors/sidebar.mjs +13 -13
  149. package/dist/behaviors/slider.d.mts +1 -1
  150. package/dist/behaviors/slider.mjs +9 -9
  151. package/dist/behaviors/split-pane.d.mts +1 -1
  152. package/dist/behaviors/split-pane.mjs +7 -7
  153. package/dist/behaviors/suggestions.d.mts +27 -0
  154. package/dist/behaviors/suggestions.mjs +1089 -0
  155. package/dist/behaviors/svg-parse.d.mts +14 -0
  156. package/dist/behaviors/svg-parse.mjs +37 -0
  157. package/dist/behaviors/tabs.d.mts +2 -2
  158. package/dist/behaviors/tabs.mjs +25 -25
  159. package/dist/behaviors/tag-input.d.mts +23 -5
  160. package/dist/behaviors/tag-input.mjs +1359 -104
  161. package/dist/behaviors/theme-switcher.d.mts +24 -0
  162. package/dist/behaviors/theme-switcher.mjs +1981 -0
  163. package/dist/behaviors/theme.d.mts +15 -8
  164. package/dist/behaviors/theme.mjs +390 -28
  165. package/dist/behaviors/toast.d.mts +24 -25
  166. package/dist/behaviors/toast.mjs +1266 -88
  167. package/dist/behaviors/tooltip.d.mts +4 -0
  168. package/dist/behaviors/tooltip.mjs +378 -0
  169. package/dist/behaviors/tree-select.d.mts +69 -0
  170. package/dist/behaviors/tree-select.mjs +1907 -0
  171. package/dist/behaviors/tree.d.mts +1 -1
  172. package/dist/behaviors/tree.mjs +15 -15
  173. package/dist/behaviors/upload.d.mts +3 -2
  174. package/dist/behaviors/upload.mjs +1076 -66
  175. package/dist/cli.mjs +5 -6
  176. package/dist/components/ai-branch.mjs +3 -0
  177. package/dist/components/ai-chat.css +846 -458
  178. package/dist/components/ai-diff.css +153 -132
  179. package/dist/components/ai-feedback.mjs +3 -0
  180. package/dist/components/ai-panel.css +495 -270
  181. package/dist/components/ai-threads.mjs +3 -0
  182. package/dist/components/ai-tool.css +393 -238
  183. package/dist/components/autocomplete.css +61 -0
  184. package/dist/components/autocomplete.mjs +3 -0
  185. package/dist/components/back-top.css +34 -31
  186. package/dist/components/badge.css +44 -41
  187. package/dist/components/breadcrumb.css +67 -46
  188. package/dist/components/btn.css +80 -74
  189. package/dist/components/card.css +117 -106
  190. package/dist/components/cascader.css +311 -0
  191. package/dist/components/cascader.mjs +3 -0
  192. package/dist/components/chart-area.css +8 -2
  193. package/dist/components/chart-bar.css +95 -33
  194. package/dist/components/chart-gauge.css +14 -11
  195. package/dist/components/chart-graph.css +60 -0
  196. package/dist/components/chart-graph.mjs +4 -0
  197. package/dist/components/chart-heatmap.css +96 -90
  198. package/dist/components/chart-line.css +8 -3
  199. package/dist/components/chart-map.mjs +3 -0
  200. package/dist/components/chart-pie.css +9 -6
  201. package/dist/components/chart-radar.css +8 -5
  202. package/dist/components/chart-scatter.css +9 -6
  203. package/dist/components/chart-scatter.mjs +1 -1
  204. package/dist/components/chart-sparkline.css +5 -2
  205. package/dist/components/chart-stack.css +14 -11
  206. package/dist/components/charts.css +190 -67
  207. package/dist/components/charts.mjs +1 -0
  208. package/dist/components/code-block.css +418 -0
  209. package/dist/components/code-block.mjs +4 -0
  210. package/dist/components/command-palette.css +138 -117
  211. package/dist/components/content.css +257 -251
  212. package/dist/components/copy.css +72 -44
  213. package/dist/components/datatable.css +423 -181
  214. package/dist/components/date-picker.css +135 -104
  215. package/dist/components/empty.css +68 -65
  216. package/dist/components/feedback.css +201 -183
  217. package/dist/components/form-validate.css +120 -0
  218. package/dist/components/form-validate.mjs +3 -0
  219. package/dist/components/form.css +150 -127
  220. package/dist/components/icon.css +102 -0
  221. package/dist/components/icon.mjs +3 -0
  222. package/dist/components/input.css +159 -104
  223. package/dist/components/kb-access.mjs +4 -0
  224. package/dist/components/kb-acl.mjs +4 -0
  225. package/dist/components/kb-agent.css +496 -0
  226. package/dist/components/kb-answer.mjs +4 -0
  227. package/dist/components/kb-audit.mjs +4 -0
  228. package/dist/components/kb-canvas.mjs +4 -0
  229. package/dist/components/kb-chain.mjs +4 -0
  230. package/dist/components/kb-checkpoint.mjs +4 -0
  231. package/dist/components/kb-chunks.mjs +4 -0
  232. package/dist/components/kb-citation.mjs +4 -0
  233. package/dist/components/kb-clarify.mjs +4 -0
  234. package/dist/components/kb-conflict.mjs +4 -0
  235. package/dist/components/kb-connector.mjs +4 -0
  236. package/dist/components/kb-data.css +653 -0
  237. package/dist/components/kb-eval.mjs +4 -0
  238. package/dist/components/kb-explain.mjs +4 -0
  239. package/dist/components/kb-filter.mjs +4 -0
  240. package/dist/components/kb-gap.mjs +4 -0
  241. package/dist/components/kb-ground.css +578 -0
  242. package/dist/components/kb-hittest.mjs +4 -0
  243. package/dist/components/kb-ingest.css +882 -0
  244. package/dist/components/kb-metadata.mjs +4 -0
  245. package/dist/components/kb-ops.css +554 -0
  246. package/dist/components/kb-passage.mjs +4 -0
  247. package/dist/components/kb-perm.css +433 -0
  248. package/dist/components/kb-pipeline.mjs +4 -0
  249. package/dist/components/kb-qa.mjs +4 -0
  250. package/dist/components/kb-rerank.mjs +4 -0
  251. package/dist/components/kb-retrieval.mjs +4 -0
  252. package/dist/components/kb-review.mjs +4 -0
  253. package/dist/components/kb-sandbox.mjs +4 -0
  254. package/dist/components/kb-search.css +897 -0
  255. package/dist/components/kb-segment.mjs +4 -0
  256. package/dist/components/kb-sources.mjs +4 -0
  257. package/dist/components/kb-sql.mjs +4 -0
  258. package/dist/components/kb-trace.mjs +4 -0
  259. package/dist/components/kb-visibility.mjs +4 -0
  260. package/dist/components/kb-who-can.mjs +4 -0
  261. package/dist/components/kb.css +263 -0
  262. package/dist/components/kb.mjs +9 -0
  263. package/dist/components/layout.css +85 -82
  264. package/dist/components/material-chinese.css +73 -0
  265. package/dist/components/material-soviet.css +76 -0
  266. package/dist/components/material.css +275 -0
  267. package/dist/components/media.css +220 -166
  268. package/dist/components/mentions.css +189 -0
  269. package/dist/components/mentions.mjs +3 -0
  270. package/dist/components/menu.css +113 -105
  271. package/dist/components/modal.css +107 -112
  272. package/dist/components/nav.css +118 -115
  273. package/dist/components/notification.css +238 -174
  274. package/dist/components/pagination.css +56 -53
  275. package/dist/components/panel.css +66 -63
  276. package/dist/components/panel.mjs +1 -0
  277. package/dist/components/pill.css +81 -71
  278. package/dist/components/popover.css +67 -53
  279. package/dist/components/scroll-area.css +113 -110
  280. package/dist/components/segmented.css +146 -143
  281. package/dist/components/select.css +206 -186
  282. package/dist/components/sidebar.css +137 -127
  283. package/dist/components/slider.css +117 -114
  284. package/dist/components/split-pane.css +112 -109
  285. package/dist/components/stat.css +112 -106
  286. package/dist/components/steps.css +109 -103
  287. package/dist/components/switch.css +138 -134
  288. package/dist/components/table.css +131 -107
  289. package/dist/components/tabs.css +114 -108
  290. package/dist/components/tag-input.css +48 -133
  291. package/dist/components/tag.css +81 -71
  292. package/dist/components/theme-switcher.css +105 -0
  293. package/dist/components/theme-switcher.mjs +3 -0
  294. package/dist/components/toast.css +100 -86
  295. package/dist/components/toolbar.css +45 -42
  296. package/dist/components/tooltip.css +102 -43
  297. package/dist/components/tooltip.mjs +1 -0
  298. package/dist/components/tree-select.css +416 -0
  299. package/dist/components/tree-select.mjs +3 -0
  300. package/dist/components/upload.css +135 -92
  301. package/dist/fonts/LICENSES/ark-pixel/OFL.txt +96 -0
  302. package/dist/fonts/LICENSES/cubic-11/OFL.txt +108 -0
  303. package/dist/fonts/LICENSES/galmuri/LICENSE.txt +93 -0
  304. package/dist/fonts/OFL.txt +96 -0
  305. package/dist/fonts/README.md +14 -0
  306. package/dist/fonts/fusion-pixel-12px-zh-hans.woff2 +0 -0
  307. package/dist/index.d.mts +64 -15
  308. package/dist/index.mjs +28049 -8595
  309. package/dist/react.d.mts +91 -0
  310. package/dist/react.mjs +8208 -0
  311. package/dist/registry.json +454 -15
  312. package/dist/tokens/chinese.css +44 -0
  313. package/dist/tokens/colors.css +365 -1222
  314. package/dist/tokens/design.css +109 -0
  315. package/dist/tokens/dtcg.json +11198 -0
  316. package/dist/tokens/figma.variables.json +7017 -0
  317. package/dist/tokens/icon-masks.css +78 -0
  318. package/dist/tokens/pixel.css +50 -0
  319. package/dist/tokens/soviet.css +44 -0
  320. package/dist/tokens/style-profiles.css +36 -547
  321. package/dist/tokens/themes.json +308 -0
  322. package/dist/tokens/tokens-studio.json +6728 -0
  323. package/dist/tokens/typography.css +21 -18
  324. package/dist/tokens.css +1555 -1801
  325. package/dist/ui.css +16147 -8597
  326. package/dist/vue.d.mts +344 -0
  327. package/dist/vue.mjs +7781 -0
  328. package/docs/spec/ai-input-experience.md +75 -0
  329. package/docs/spec/ai-native.md +238 -147
  330. package/docs/spec/design-philosophy.md +189 -0
  331. package/docs/spec/design-primitives.md +196 -0
  332. package/docs/spec/kb-family.md +475 -0
  333. package/package.json +42 -7
  334. package/dist/retro-effects.css +0 -255
  335. package/dist/tokens/retro-effects.css +0 -255
@@ -0,0 +1,3707 @@
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
+
987
+ // src/behaviors/ai-core.ts
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");
1005
+ function isObj(v) {
1006
+ return typeof v === "object" && v !== null;
1007
+ }
1008
+ function pickStr(input, keys) {
1009
+ if (!isObj(input)) return void 0;
1010
+ for (const k of keys) {
1011
+ const v = input[k];
1012
+ if (typeof v === "string" && v) return v;
1013
+ }
1014
+ return void 0;
1015
+ }
1016
+ function pickNum(src, keys) {
1017
+ if (!isObj(src)) return void 0;
1018
+ for (const k of keys) {
1019
+ const v = src[k];
1020
+ if (typeof v === "number" && Number.isFinite(v)) return v;
1021
+ }
1022
+ return void 0;
1023
+ }
1024
+ function firstLine(s, max = 80) {
1025
+ const line = s.split("\n", 1)[0]?.trim() ?? "";
1026
+ return line.length > max ? `${line.slice(0, max)}\u2026` : line;
1027
+ }
1028
+ function filePathOf(input) {
1029
+ return pickStr(input, ["file_path", "path", "file", "filename"]);
1030
+ }
1031
+ var summarizePath = (input) => filePathOf(input);
1032
+ function summarizeRead(input) {
1033
+ const p = filePathOf(input);
1034
+ if (!p) return void 0;
1035
+ const offset = pickNum(input, ["offset", "start_line"]);
1036
+ if (offset == null) return p;
1037
+ const limit = pickNum(input, ["limit", "end_line"]);
1038
+ return limit != null ? `${p}:${offset}-${offset + limit}` : `${p}:${offset}`;
1039
+ }
1040
+ function summarizeShell(input) {
1041
+ const cmd = typeof input === "string" ? input : pickStr(input, ["command", "cmd", "script"]);
1042
+ return cmd ? firstLine(cmd) : void 0;
1043
+ }
1044
+ function summarizeGrep(input) {
1045
+ const pattern = pickStr(input, ["pattern", "regex", "query"]);
1046
+ const p = filePathOf(input);
1047
+ if (pattern) return p ? `${firstLine(pattern, 40)} \xB7 ${p}` : firstLine(pattern);
1048
+ return p;
1049
+ }
1050
+ var sum = (fn) => {
1051
+ return (input) => fn(input) ?? "";
1052
+ };
1053
+ var BUILTIN_KINDS = {
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"])) },
1064
+ mcp: { label: "MCP", icon: ICON_BLOCKS, tint: "muted", summarize: sum((i) => pickStr(i, ["tool", "method", "name"])) },
1065
+ skill: { label: t("aiCore.kindSkill"), icon: ICON_ZAP, tint: "accent", summarize: sum((i) => pickStr(i, ["skill", "name", "command"])) },
1066
+ todo: {
1067
+ label: t("aiCore.kindTodo"),
1068
+ icon: ICON_LIST,
1069
+ tint: "info",
1070
+ summarize: sum((input) => {
1071
+ const items = input && typeof input === "object" && Array.isArray(input.items) ? input.items : Array.isArray(input) ? input : [];
1072
+ const done = items.filter((it) => it?.status === "done").length;
1073
+ return items.length > 0 ? t("aiCore.todoProgress", { p0: done, p1: items.length }) : "";
1074
+ })
1075
+ },
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"])) }
1080
+ };
1081
+ var kindRegistry = new Map(Object.entries(BUILTIN_KINDS));
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");
1084
+ const node = document.createElement(tag);
1085
+ if (className) node.className = className;
1086
+ if (text != null) node.textContent = text;
1087
+ return node;
1088
+ }
1089
+ function svgIcon(svg) {
1090
+ return parseSvg(svg);
1091
+ }
1092
+
1093
+ // src/behaviors/kb-core.ts
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>` });
1095
+ var BUILTIN_SOURCE_TYPES = {
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"/>')
1106
+ };
1107
+ var sourceTypeRegistry = new Map(Object.entries(BUILTIN_SOURCE_TYPES));
1108
+ var CITE_RE = /\[(\d{1,3})\]/g;
1109
+ function parseInlineCitations(text) {
1110
+ const out = [];
1111
+ if (!text) return out;
1112
+ let last = 0;
1113
+ CITE_RE.lastIndex = 0;
1114
+ let m;
1115
+ while ((m = CITE_RE.exec(text)) !== null) {
1116
+ if (m.index > last) out.push({ text: text.slice(last, m.index) });
1117
+ out.push({ text: m[0], n: Number(m[1]) });
1118
+ last = m.index + m[0].length;
1119
+ }
1120
+ if (last < text.length) out.push({ text: text.slice(last) });
1121
+ return out;
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
+ };
1131
+ function normalizeQueryResult(raw) {
1132
+ const columns = Array.isArray(raw.columns) ? raw.columns.map((c) => typeof c === "string" ? { name: c } : { name: String(c.name ?? ""), type: c.type }) : [];
1133
+ return {
1134
+ columns,
1135
+ rows: Array.isArray(raw.rows) ? raw.rows.map((r) => Array.isArray(r) ? r.map((v) => typeof v === "number" || typeof v === "string" ? v : null) : []) : [],
1136
+ totalRows: typeof raw.totalRows === "number" ? raw.totalRows : void 0,
1137
+ truncated: raw.truncated === true,
1138
+ limit: typeof raw.limit === "number" ? raw.limit : void 0,
1139
+ rowCount: typeof raw.rowCount === "number" ? raw.rowCount : Array.isArray(raw.rows) ? raw.rows.length : 0,
1140
+ durationMs: typeof raw.durationMs === "number" ? raw.durationMs : void 0,
1141
+ exportFormats: Array.isArray(raw.exportFormats) ? raw.exportFormats.map(String) : void 0
1142
+ };
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
+ };
1155
+ function formatCount(n) {
1156
+ if (n == null || !Number.isFinite(n)) return "\u2014";
1157
+ if (Math.abs(n) >= 1e4) return `${(n / 1e3).toFixed(1)}k`;
1158
+ return n.toLocaleString("en-US");
1159
+ }
1160
+
1161
+ // src/behaviors/kb-clarify.ts
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>';
1163
+ function toClarification(raw) {
1164
+ const r = raw ?? {};
1165
+ const options = Array.isArray(r.options) ? r.options.filter((o) => !!o && typeof o === "object").map((o) => ({ label: String(o.label ?? ""), value: String(o.value ?? o.label ?? "") })).filter((o) => o.label) : [];
1166
+ return {
1167
+ id: typeof r.id === "string" && r.id ? r.id : void 0,
1168
+ question: String(r.question ?? ""),
1169
+ options,
1170
+ reason: typeof r.reason === "string" && r.reason ? r.reason : void 0
1171
+ };
1172
+ }
1173
+ function renderKbClarify(el, c, opts) {
1174
+ if (typeof document === "undefined") return el;
1175
+ const clar = toClarification(c);
1176
+ el.textContent = "";
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);
1183
+ q.appendChild(iconBox);
1184
+ q.appendChild(h("span", "icen-kb-clarify-qtext", clar.question));
1185
+ root.appendChild(q);
1186
+ const optsWrap = h("div", "icen-kb-clarify-opts");
1187
+ const resolved = h("div", "icen-kb-clarify-resolved");
1188
+ resolved.hidden = true;
1189
+ const pick = (btn) => {
1190
+ if (root.classList.contains("is-answered")) return;
1191
+ root.classList.add("is-answered");
1192
+ btn.classList.add("is-picked");
1193
+ Array.from(optsWrap.querySelectorAll(".icen-kb-clarify-opt")).forEach((b) => {
1194
+ if (b !== btn) b.disabled = true;
1195
+ });
1196
+ resolved.textContent = "";
1197
+ resolved.appendChild(document.createTextNode(t("kbClarify.parsedPrefix")));
1198
+ resolved.appendChild(h("b", "icen-kb-clarify-picked", btn.dataset.icenLabel ?? ""));
1199
+ resolved.hidden = false;
1200
+ const answer = { id: clar.id, value: btn.dataset.icenValue ?? "", label: btn.dataset.icenLabel ?? "" };
1201
+ emitIcen(btn, "icen:kb-clarify-answer", answer);
1202
+ opts?.onAnswer?.(answer);
1203
+ };
1204
+ for (const o of clar.options) {
1205
+ const btn = h("button", "icen-kb-clarify-opt", o.label);
1206
+ btn.type = "button";
1207
+ btn.dataset.icenValue = o.value;
1208
+ btn.dataset.icenLabel = o.label;
1209
+ btn.addEventListener("click", () => pick(btn));
1210
+ optsWrap.appendChild(btn);
1211
+ }
1212
+ if (optsWrap.children.length > 0) root.appendChild(optsWrap);
1213
+ root.appendChild(resolved);
1214
+ if (clar.reason) root.appendChild(h("div", "icen-kb-clarify-reason", clar.reason));
1215
+ el.appendChild(root);
1216
+ return el;
1217
+ }
1218
+
1219
+ // src/behaviors/kb-explain.ts
1220
+ function toExplainFact(raw) {
1221
+ const r = raw ?? {};
1222
+ const list = (v) => {
1223
+ if (!Array.isArray(v)) return void 0;
1224
+ const out = v.map((s) => String(s ?? "").trim()).filter(Boolean);
1225
+ return out.length ? out : void 0;
1226
+ };
1227
+ const metrics = Array.isArray(r.metrics) ? r.metrics.filter((m) => !!m && typeof m === "object").map((m) => ({
1228
+ name: String(m.name ?? ""),
1229
+ ref: typeof m.ref === "string" && m.ref ? m.ref : void 0
1230
+ })).filter((m) => m.name) : void 0;
1231
+ return {
1232
+ tables: list(r.tables),
1233
+ columns: list(r.columns),
1234
+ filters: list(r.filters),
1235
+ aggregates: list(r.aggregates),
1236
+ metrics: metrics?.length ? metrics : void 0
1237
+ };
1238
+ }
1239
+ function renderGroup(label, items) {
1240
+ if (!items?.length) return null;
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));
1244
+ return group;
1245
+ }
1246
+ function renderKbExplain(el, fact) {
1247
+ if (typeof document === "undefined") return el;
1248
+ const f = toExplainFact(fact);
1249
+ el.textContent = "";
1250
+ const root = h("div", "icen-kb-explain");
1251
+ root.dataset.icenKbExplain = "";
1252
+ const count = (f.tables?.length ?? 0) + (f.columns?.length ?? 0) + (f.filters?.length ?? 0) + (f.aggregates?.length ?? 0) + (f.metrics?.length ?? 0);
1253
+ const head = h("button", "icen-kb-fold-head icen-kb-explain-head");
1254
+ head.type = "button";
1255
+ head.setAttribute("aria-expanded", "false");
1256
+ head.appendChild(h("span", "icen-kb-fold-caret", "\u25B8"));
1257
+ head.appendChild(h("span", "icen-kb-explain-title", t("kbExplain.trigger")));
1258
+ if (count > 0) {
1259
+ const countBox = h("span", "icen-kb-num icen-kb-explain-count", String(count));
1260
+ countBox.classList.add("icen-kb-fold-count");
1261
+ head.appendChild(countBox);
1262
+ }
1263
+ root.appendChild(head);
1264
+ const body = h("div", "icen-kb-explain-body");
1265
+ body.hidden = true;
1266
+ const groups = [
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)
1271
+ ];
1272
+ for (const g of groups) if (g) body.appendChild(g);
1273
+ if (f.metrics?.length) {
1274
+ const metrics = h("div", "icen-kb-explain-metrics");
1275
+ for (const m of f.metrics) {
1276
+ const row = h("div", "icen-kb-explain-metric");
1277
+ row.appendChild(h("span", "icen-kb-explain-metric-name", m.name));
1278
+ if (m.ref) {
1279
+ if (!/^\s*(javascript|data|vbscript)\s*:/i.test(m.ref)) {
1280
+ const a = h("a", "icen-kb-explain-metric-ref", t("kbExplain.viewDefinition"));
1281
+ a.href = m.ref;
1282
+ a.rel = "noopener";
1283
+ row.appendChild(a);
1284
+ }
1285
+ }
1286
+ metrics.appendChild(row);
1287
+ }
1288
+ body.appendChild(metrics);
1289
+ }
1290
+ if (body.children.length === 0) {
1291
+ body.appendChild(h("div", "icen-kb-empty", t("kbExplain.empty")));
1292
+ }
1293
+ root.appendChild(body);
1294
+ head.addEventListener("click", () => {
1295
+ const open = body.hidden;
1296
+ body.hidden = !open;
1297
+ head.setAttribute("aria-expanded", String(open));
1298
+ emitIcen(head, "icen:kb-explain-toggle", { el: root, open });
1299
+ });
1300
+ el.appendChild(root);
1301
+ return el;
1302
+ }
1303
+
1304
+ // src/behaviors/charts.ts
1305
+ var SVG_NS = "http://www.w3.org/2000/svg";
1306
+ var REGISTERED_TONES = /* @__PURE__ */ new Map();
1307
+ function toneVar(tone = "accent") {
1308
+ const registered = REGISTERED_TONES.get(tone);
1309
+ if (registered) return registered;
1310
+ switch (tone) {
1311
+ case "success":
1312
+ return "var(--icen-token-success)";
1313
+ case "warning":
1314
+ return "var(--icen-token-warning)";
1315
+ case "error":
1316
+ return "var(--icen-token-error)";
1317
+ case "muted":
1318
+ return "var(--icen-token-text-faint)";
1319
+ case "accent":
1320
+ default:
1321
+ return "var(--icen-token-accent)";
1322
+ }
1323
+ }
1324
+ function fmt(opts, value) {
1325
+ return opts.formatValue ? opts.formatValue(value) : String(value);
1326
+ }
1327
+ function maxOf(values) {
1328
+ let max = 1;
1329
+ for (const v of values) if (v > max) max = v;
1330
+ return max;
1331
+ }
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);
1348
+ }
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;
1392
+ }
1393
+ function svgEl(tag, attrs) {
1394
+ const el = document.createElementNS(SVG_NS, tag);
1395
+ for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, v);
1396
+ return el;
1397
+ }
1398
+ function renderEmpty(el, label) {
1399
+ const box = document.createElement("div");
1400
+ box.className = "icen-chart-empty";
1401
+ box.textContent = label ?? t("charts.noData");
1402
+ el.appendChild(box);
1403
+ }
1404
+ function legendItem(label, valueText, tone) {
1405
+ const row = document.createElement("div");
1406
+ row.className = "icen-chart-legend-item";
1407
+ const dot = document.createElement("span");
1408
+ dot.className = "icen-chart-legend-dot";
1409
+ if (tone) dot.style.setProperty("--chart-tone", toneVar(tone));
1410
+ const name = document.createElement("span");
1411
+ name.className = "icen-chart-legend-label";
1412
+ name.textContent = label;
1413
+ const val = document.createElement("span");
1414
+ val.className = "icen-chart-legend-value";
1415
+ val.textContent = valueText;
1416
+ row.append(dot, name, val);
1417
+ return row;
1418
+ }
1419
+ function labelsRow(labels) {
1420
+ const row = document.createElement("div");
1421
+ row.className = "icen-chart-labels";
1422
+ for (const label of labels) {
1423
+ const span = document.createElement("span");
1424
+ span.textContent = label;
1425
+ row.appendChild(span);
1426
+ }
1427
+ return row;
1428
+ }
1429
+ function normalizeSeriesInput(opts) {
1430
+ if (opts.series && opts.series.length > 0) return opts.series;
1431
+ return [{ name: "", values: opts.values ?? [], tone: opts.tone }];
1432
+ }
1433
+ function toneOf(tone, seriesIndex) {
1434
+ if (typeof tone === "string") return toneVar(tone);
1435
+ if (typeof tone === "number") return paletteVar(tone);
1436
+ return seriesIndex > 0 ? paletteVar(seriesIndex % 6 + 1) : void 0;
1437
+ }
1438
+ var PALETTE_VARS = [
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)"
1445
+ ];
1446
+ function paletteVar(index) {
1447
+ const i = ((Math.floor(index) - 1) % 6 + 6) % 6;
1448
+ return PALETTE_VARS[i];
1449
+ }
1450
+ function setMarkData(node, data) {
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));
1457
+ }
1458
+ function copyMarkData(target, source) {
1459
+ for (const attr of Array.from(source.attributes)) {
1460
+ if (attr.name.startsWith("data-icen-chart-")) target.setAttribute(attr.name, attr.value);
1461
+ }
1462
+ }
1463
+ function withChrome(el, opts, hasLegend, paint, state, extras) {
1464
+ if (el.dataset.icenChartBody != null) {
1465
+ el.textContent = "";
1466
+ paint(el);
1467
+ return;
1468
+ }
1469
+ el.textContent = "";
1470
+ const body = document.createElement("div");
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) {
1476
+ el.appendChild(body);
1477
+ paint(body);
1478
+ return;
1479
+ }
1480
+ let hidden = state && state.hidden != null ? state.hidden : opts.legend === false;
1481
+ if (state) state.hidden = hidden;
1482
+ el.setAttribute("data-icen-legend-hidden", String(hidden));
1483
+ const head = document.createElement("div");
1484
+ head.className = "icen-chart-head";
1485
+ const title = document.createElement("div");
1486
+ title.className = "icen-chart-title";
1487
+ title.textContent = opts.title ?? "";
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
+ }
1503
+ const eye = document.createElement("button");
1504
+ eye.type = "button";
1505
+ eye.className = "icen-chart-legend-eye";
1506
+ eye.dataset.icenChartLegendEye = "";
1507
+ const syncEye = () => {
1508
+ const nowHidden = el.getAttribute("data-icen-legend-hidden") === "true";
1509
+ eye.setAttribute("aria-pressed", String(!nowHidden));
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");
1513
+ if (icon) eye.replaceChildren(icon);
1514
+ };
1515
+ eye.addEventListener("click", () => {
1516
+ const nowHidden = el.getAttribute("data-icen-legend-hidden") === "true";
1517
+ hidden = !nowHidden;
1518
+ if (state) state.hidden = hidden;
1519
+ el.setAttribute("data-icen-legend-hidden", String(hidden));
1520
+ syncEye();
1521
+ emitIcen(el, "icen:chart-legend-visibility", { visible: hidden ? false : true });
1522
+ });
1523
+ syncEye();
1524
+ head.append(title, eye);
1525
+ el.append(head, body);
1526
+ paint(body);
1527
+ }
1528
+ var hiddenLegendKeys = /* @__PURE__ */ new WeakMap();
1529
+ function toggleLegend(items, scope) {
1530
+ const legend = document.createElement("div");
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);
1535
+ for (const item of items) {
1536
+ const row = legendItem(item.label, "", item.tone);
1537
+ row.classList.add("icen-chart-legend-toggle-item");
1538
+ row.setAttribute("role", "button");
1539
+ row.tabIndex = 0;
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);
1545
+ if (dot && toneColor) dot.style.setProperty("--chart-tone", toneColor);
1546
+ const toggle = () => {
1547
+ const off = row.classList.toggle("is-off");
1548
+ if (off) hidden.add(item.key);
1549
+ else hidden.delete(item.key);
1550
+ row.setAttribute("aria-pressed", String(!off));
1551
+ scope.querySelectorAll(`[data-icen-chart-key="${CSS.escape(item.key)}"]`).forEach((m) => {
1552
+ m.classList.toggle("is-off", off);
1553
+ });
1554
+ emitIcen(legend, "icen:chart-legend-toggle", { key: item.key, seriesIndex: item.si, hidden: off });
1555
+ };
1556
+ row.addEventListener("click", toggle);
1557
+ row.addEventListener("keydown", (e) => {
1558
+ if (e.key === "Enter" || e.key === " ") {
1559
+ e.preventDefault();
1560
+ toggle();
1561
+ }
1562
+ });
1563
+ legend.appendChild(row);
1564
+ }
1565
+ for (const item of items) {
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));
1569
+ });
1570
+ }
1571
+ return legend;
1572
+ }
1573
+ function renderVBar(el, opts) {
1574
+ if (typeof document === "undefined") return el;
1575
+ const seriesList = normalizeSeriesInput(opts);
1576
+ const labels = opts.labels ?? [];
1577
+ const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
1578
+ withChrome(el, opts, seriesList.length > 1, (body) => {
1579
+ if (n === 0 || seriesList.length === 0) {
1580
+ renderEmpty(body, opts.emptyLabel);
1581
+ return;
1582
+ }
1583
+ const labelsUsed = labels.slice(0, n);
1584
+ let max = 1;
1585
+ if (opts.stacked && seriesList.length > 1) {
1586
+ for (let i = 0; i < n; i++) {
1587
+ let sum2 = 0;
1588
+ for (const s of seriesList) sum2 += Math.max(0, s.values[i] ?? 0);
1589
+ if (sum2 > max) max = sum2;
1590
+ }
1591
+ } else {
1592
+ for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
1593
+ }
1594
+ const wrap = document.createElement("div");
1595
+ wrap.className = "icen-chart-vbar";
1596
+ if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
1597
+ for (let i = 0; i < n; i++) {
1598
+ const track = document.createElement("div");
1599
+ track.className = "icen-chart-vbar-track";
1600
+ if (seriesList.length > 1) {
1601
+ track.classList.add("icen-chart-vbar-track--multi");
1602
+ if (opts.stacked) track.classList.add("icen-chart-vbar-track--stacked");
1603
+ }
1604
+ seriesList.forEach((s, si) => {
1605
+ const value = s.values[i] ?? 0;
1606
+ const bar = document.createElement("div");
1607
+ bar.className = "icen-chart-vbar-bar";
1608
+ const height = value > 0 ? Math.max(6, value / max * 100) : 0;
1609
+ bar.style.height = `${height}%`;
1610
+ const toneColor = toneOf(s.tone, si);
1611
+ if (toneColor) bar.style.setProperty("--chart-tone", toneColor);
1612
+ bar.setAttribute("title", seriesList.length > 1 ? `${labelsUsed[i]} \xB7 ${s.name}: ${fmt(opts, value)}` : `${labelsUsed[i]}: ${fmt(opts, value)}`);
1613
+ setMarkData(bar, {
1614
+ index: i,
1615
+ seriesIndex: seriesList.length > 1 ? si : void 0,
1616
+ seriesName: seriesList.length > 1 ? s.name : void 0,
1617
+ label: labelsUsed[i] ?? "",
1618
+ value
1619
+ });
1620
+ track.appendChild(bar);
1621
+ });
1622
+ wrap.appendChild(track);
1623
+ }
1624
+ body.append(wrap, labelsRow(labelsUsed));
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));
1626
+ });
1627
+ attachChartInteraction(el, "vbar");
1628
+ return el;
1629
+ }
1630
+ function renderHBar(el, opts) {
1631
+ if (typeof document === "undefined") return el;
1632
+ withChrome(el, opts, false, (body) => {
1633
+ const n = Math.min(opts.labels.length, opts.values.length);
1634
+ if (n === 0) {
1635
+ renderEmpty(body, opts.emptyLabel);
1636
+ return;
1637
+ }
1638
+ const labels = opts.labels.slice(0, n);
1639
+ const values = opts.values.slice(0, n);
1640
+ const max = maxOf(values);
1641
+ const wrap = document.createElement("div");
1642
+ wrap.className = "icen-chart-hbar";
1643
+ if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
1644
+ values.forEach((value, i) => {
1645
+ const row = document.createElement("div");
1646
+ row.className = "icen-chart-hbar-row";
1647
+ const label = document.createElement("span");
1648
+ label.className = "icen-chart-hbar-label";
1649
+ label.textContent = labels[i] ?? "";
1650
+ const track = document.createElement("div");
1651
+ track.className = "icen-chart-hbar-track";
1652
+ const bar = document.createElement("div");
1653
+ bar.className = "icen-chart-hbar-bar";
1654
+ bar.style.width = value > 0 ? `${Math.max(2, value / max * 100)}%` : "0%";
1655
+ setMarkData(bar, { index: i, label: labels[i] ?? "", value });
1656
+ const val = document.createElement("span");
1657
+ val.className = "icen-chart-hbar-value";
1658
+ val.textContent = fmt(opts, value);
1659
+ track.appendChild(bar);
1660
+ row.append(label, track, val);
1661
+ wrap.appendChild(row);
1662
+ });
1663
+ body.appendChild(wrap);
1664
+ });
1665
+ attachChartInteraction(el, "hbar");
1666
+ return el;
1667
+ }
1668
+ function renderStack(el, opts) {
1669
+ if (typeof document === "undefined") return el;
1670
+ withChrome(el, opts, opts.segments.length > 0, (body) => {
1671
+ const total = opts.segments.reduce((sum2, s) => sum2 + s.value, 0);
1672
+ if (opts.segments.length === 0 || total <= 0) {
1673
+ renderEmpty(body, opts.emptyLabel);
1674
+ return;
1675
+ }
1676
+ const bar = document.createElement("div");
1677
+ bar.className = "icen-chart-stack";
1678
+ for (const [segIndex, seg] of opts.segments.entries()) {
1679
+ const s = document.createElement("div");
1680
+ s.className = "icen-chart-stack-seg";
1681
+ s.style.width = `${seg.value / total * 100}%`;
1682
+ if (seg.tone) s.style.setProperty("--chart-tone", toneVar(seg.tone));
1683
+ s.setAttribute("title", `${seg.label}: ${fmt(opts, seg.value)}`);
1684
+ setMarkData(s, { index: segIndex, label: seg.label, value: seg.value });
1685
+ bar.appendChild(s);
1686
+ }
1687
+ const legend = document.createElement("div");
1688
+ legend.className = "icen-chart-legend";
1689
+ for (const seg of opts.segments) {
1690
+ legend.appendChild(legendItem(seg.label, fmt(opts, seg.value), seg.tone));
1691
+ }
1692
+ body.append(bar, legend);
1693
+ });
1694
+ attachChartInteraction(el, "stack");
1695
+ return el;
1696
+ }
1697
+ function renderDonut(el, opts) {
1698
+ if (typeof document === "undefined") return el;
1699
+ withChrome(el, opts, opts.segments.length > 0, (body) => {
1700
+ const total = opts.segments.reduce((sum2, s) => sum2 + s.value, 0);
1701
+ if (opts.segments.length === 0 || total <= 0) {
1702
+ renderEmpty(body, opts.emptyLabel);
1703
+ return;
1704
+ }
1705
+ const radius = 42;
1706
+ const circumference = 2 * Math.PI * radius;
1707
+ const wrap = document.createElement("div");
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", {
1712
+ cx: "60",
1713
+ cy: "60",
1714
+ r: String(radius),
1715
+ fill: "none",
1716
+ stroke: "var(--icen-token-line-soft)",
1717
+ "stroke-width": "14"
1718
+ }));
1719
+ let offset = 0;
1720
+ opts.segments.forEach((seg, segIndex) => {
1721
+ const dash = seg.value / total * circumference;
1722
+ const arc = svgEl("circle", {
1723
+ cx: "60",
1724
+ cy: "60",
1725
+ r: String(radius),
1726
+ fill: "none",
1727
+ stroke: toneVar(seg.tone),
1728
+ "stroke-width": "14",
1729
+ "stroke-dasharray": `${dash} ${circumference - dash}`,
1730
+ "stroke-dashoffset": String(-offset),
1731
+ "stroke-linecap": "butt"
1732
+ });
1733
+ setMarkData(arc, { index: segIndex, label: seg.label, value: seg.value });
1734
+ svg.appendChild(arc);
1735
+ offset += dash;
1736
+ });
1737
+ const legend = document.createElement("div");
1738
+ legend.className = "icen-chart-legend";
1739
+ for (const seg of opts.segments) {
1740
+ legend.appendChild(legendItem(seg.label, `${Math.round(seg.value / total * 100)}%`, seg.tone));
1741
+ }
1742
+ wrap.append(svg, legend);
1743
+ body.appendChild(wrap);
1744
+ });
1745
+ attachChartInteraction(el, "donut");
1746
+ return el;
1747
+ }
1748
+ var lineGradientSeq = 0;
1749
+ function renderLine(el, opts) {
1750
+ if (typeof document === "undefined") return el;
1751
+ const seriesList = normalizeSeriesInput(opts);
1752
+ const labels = opts.labels ?? [];
1753
+ const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
1754
+ withChrome(el, opts, seriesList.length > 1, (body) => {
1755
+ if (n === 0 || seriesList.length === 0) {
1756
+ renderEmpty(body, opts.emptyLabel);
1757
+ return;
1758
+ }
1759
+ const labelsUsed = labels.slice(0, n);
1760
+ const width = fluidWidth(body);
1761
+ const height = 150;
1762
+ const pad = 14;
1763
+ let max = 1;
1764
+ for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
1765
+ const wrap = document.createElement("div");
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")) });
1768
+ const defs = svgEl("defs", {});
1769
+ for (let i = 0; i < 4; i++) {
1770
+ const y = pad + i * (height - pad * 2) / 3;
1771
+ svg.appendChild(svgEl("line", {
1772
+ x1: String(pad),
1773
+ x2: String(width - pad),
1774
+ y1: String(y),
1775
+ y2: String(y),
1776
+ stroke: "var(--icen-token-line-soft)",
1777
+ "stroke-width": "1"
1778
+ }));
1779
+ }
1780
+ seriesList.forEach((s, si) => {
1781
+ const tone = toneOf(s.tone, si) ?? toneVar(opts.tone);
1782
+ const values = s.values.slice(0, n);
1783
+ const points = values.map((value, i) => ({
1784
+ i,
1785
+ x: n <= 1 ? width / 2 : pad + i / (n - 1) * (width - pad * 2),
1786
+ y: height - pad - Math.max(0, value) / max * (height - pad * 2),
1787
+ value,
1788
+ label: labelsUsed[i] ?? ""
1789
+ }));
1790
+ const line = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(" ");
1791
+ const last = points[points.length - 1];
1792
+ const first = points[0];
1793
+ const fillArea = seriesList.length === 1 || opts.fill;
1794
+ if (fillArea) {
1795
+ const area = `${line} L${last.x.toFixed(1)},${height - pad} L${first.x.toFixed(1)},${height - pad} Z`;
1796
+ const gradientId = `icen-chart-line-${++lineGradientSeq}`;
1797
+ const gradient = svgEl("linearGradient", { id: gradientId, x1: "0", x2: "0", y1: "0", y2: "1" });
1798
+ const strong = seriesList.length > 1 ? "0.20" : "0.32";
1799
+ gradient.appendChild(svgEl("stop", { offset: "0%", "stop-color": tone, "stop-opacity": strong }));
1800
+ gradient.appendChild(svgEl("stop", { offset: "100%", "stop-color": tone, "stop-opacity": "0.02" }));
1801
+ defs.appendChild(gradient);
1802
+ const areaPath = svgEl("path", { d: area, fill: `url(#${gradientId})` });
1803
+ if (seriesList.length > 1) {
1804
+ setMarkData(areaPath, { seriesIndex: si, seriesName: s.name, label: s.name, value: values.reduce((a, b) => a + b, 0) });
1805
+ }
1806
+ svg.appendChild(areaPath);
1807
+ }
1808
+ const linePath = svgEl("path", {
1809
+ d: line,
1810
+ fill: "none",
1811
+ stroke: tone,
1812
+ "stroke-width": seriesList.length > 1 ? "2" : "2.5",
1813
+ "stroke-linecap": "round",
1814
+ "stroke-linejoin": "round"
1815
+ });
1816
+ const lineTitle = svgEl("title", {});
1817
+ lineTitle.textContent = s.name || t("charts.value");
1818
+ linePath.appendChild(lineTitle);
1819
+ setMarkData(linePath, { seriesIndex: si, seriesName: s.name, label: s.name, value: values[values.length - 1] ?? 0 });
1820
+ svg.appendChild(linePath);
1821
+ for (const p of points) {
1822
+ const dot = svgEl("circle", {
1823
+ cx: p.x.toFixed(1),
1824
+ cy: p.y.toFixed(1),
1825
+ r: "3",
1826
+ fill: "var(--icen-token-card)",
1827
+ stroke: tone,
1828
+ "stroke-width": "2"
1829
+ });
1830
+ const title = svgEl("title", {});
1831
+ title.textContent = seriesList.length > 1 ? `${p.label} \xB7 ${s.name}: ${fmt(opts, p.value)}` : `${p.label}: ${fmt(opts, p.value)}`;
1832
+ dot.appendChild(title);
1833
+ setMarkData(dot, {
1834
+ index: p.i,
1835
+ seriesIndex: seriesList.length > 1 ? si : void 0,
1836
+ seriesName: seriesList.length > 1 ? s.name : void 0,
1837
+ label: p.label,
1838
+ value: p.value
1839
+ });
1840
+ svg.appendChild(dot);
1841
+ const hit = svgEl("circle", {
1842
+ cx: p.x.toFixed(1),
1843
+ cy: p.y.toFixed(1),
1844
+ r: "8",
1845
+ fill: "transparent",
1846
+ "data-icen-chart-hit": "1"
1847
+ });
1848
+ copyMarkData(hit, dot);
1849
+ svg.appendChild(hit);
1850
+ }
1851
+ });
1852
+ if (defs.firstChild) svg.insertBefore(defs, svg.firstChild);
1853
+ wrap.append(svg, pointLabelsRow(labelsUsed, width, pad));
1854
+ body.appendChild(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));
1856
+ });
1857
+ if (el.dataset.icenChartBody == null) observeFluidWidth(el, () => renderLine(el, opts));
1858
+ attachChartInteraction(el, "line");
1859
+ return el;
1860
+ }
1861
+ function renderRadar(el, opts) {
1862
+ if (typeof document === "undefined") return el;
1863
+ withChrome(el, opts, opts.series.length > 1, (body) => {
1864
+ const axes = opts.axes;
1865
+ const n = axes.length;
1866
+ if (n < 3 || opts.series.length === 0) {
1867
+ renderEmpty(body, opts.emptyLabel);
1868
+ return;
1869
+ }
1870
+ let maxVal = opts.max ?? 0;
1871
+ if (opts.max == null) {
1872
+ for (const s of opts.series) {
1873
+ for (let i = 0; i < n; i++) maxVal = Math.max(maxVal, s.values[i] ?? 0);
1874
+ }
1875
+ maxVal = niceCeil(maxVal);
1876
+ }
1877
+ if (maxVal <= 0) maxVal = 1;
1878
+ const size = 200;
1879
+ const cx = size / 2;
1880
+ const cy = size / 2;
1881
+ const radius = 78;
1882
+ const levels = Math.max(2, opts.levels ?? 4);
1883
+ const angleOf = (i) => -Math.PI / 2 + i * 2 * Math.PI / n;
1884
+ const pointAt = (i, r) => ({
1885
+ x: cx + r * Math.cos(angleOf(i)),
1886
+ y: cy + r * Math.sin(angleOf(i))
1887
+ });
1888
+ const wrap = document.createElement("div");
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")) });
1891
+ for (let lv = 1; lv <= levels; lv++) {
1892
+ const r = radius * lv / levels;
1893
+ const pts = Array.from({ length: n }, (_, i) => {
1894
+ const p = pointAt(i, r);
1895
+ return `${p.x.toFixed(1)},${p.y.toFixed(1)}`;
1896
+ }).join(" ");
1897
+ svg.appendChild(svgEl("polygon", {
1898
+ points: pts,
1899
+ fill: "none",
1900
+ stroke: "var(--icen-token-line-soft)",
1901
+ "stroke-width": "1",
1902
+ "stroke-dasharray": lv === levels ? "0" : "3 2"
1903
+ }));
1904
+ }
1905
+ for (let i = 0; i < n; i++) {
1906
+ const edge = pointAt(i, radius);
1907
+ svg.appendChild(svgEl("line", {
1908
+ x1: String(cx),
1909
+ y1: String(cy),
1910
+ x2: edge.x.toFixed(1),
1911
+ y2: edge.y.toFixed(1),
1912
+ stroke: "var(--icen-token-line-soft)",
1913
+ "stroke-width": "1"
1914
+ }));
1915
+ const labelP = pointAt(i, radius + 14);
1916
+ const txt = svgEl("text", {
1917
+ x: labelP.x.toFixed(1),
1918
+ y: labelP.y.toFixed(1),
1919
+ "text-anchor": "middle",
1920
+ "dominant-baseline": "middle",
1921
+ "font-size": "9",
1922
+ fill: "var(--icen-token-text-muted)"
1923
+ });
1924
+ txt.textContent = axes[i] ?? "";
1925
+ svg.appendChild(txt);
1926
+ }
1927
+ const legendItems = [];
1928
+ opts.series.forEach((s, si) => {
1929
+ const tone = toneOf(s.tone, si) ?? toneVar(s.tone);
1930
+ const pts = Array.from({ length: n }, (_, i) => {
1931
+ const v = Math.max(0, Math.min(maxVal, s.values[i] ?? 0));
1932
+ const p = pointAt(i, v / maxVal * radius);
1933
+ return `${p.x.toFixed(1)},${p.y.toFixed(1)}`;
1934
+ }).join(" ");
1935
+ const polyFill = svgEl("polygon", {
1936
+ points: pts,
1937
+ fill: tone,
1938
+ "fill-opacity": "0.16",
1939
+ stroke: tone,
1940
+ "stroke-width": "2",
1941
+ "stroke-linejoin": "round"
1942
+ });
1943
+ const title = svgEl("title", {});
1944
+ title.textContent = `${s.name}: ${s.values.map((v) => fmt(opts, v)).join(" / ")}`;
1945
+ polyFill.appendChild(title);
1946
+ setMarkData(polyFill, { seriesIndex: si, seriesName: s.name || void 0, label: s.name, value: s.values.reduce((a, b) => a + (b || 0), 0) });
1947
+ svg.appendChild(polyFill);
1948
+ for (let i = 0; i < n; i++) {
1949
+ const v = Math.max(0, Math.min(maxVal, s.values[i] ?? 0));
1950
+ const p = pointAt(i, v / maxVal * radius);
1951
+ const dot = svgEl("circle", {
1952
+ cx: p.x.toFixed(1),
1953
+ cy: p.y.toFixed(1),
1954
+ r: "2.5",
1955
+ fill: "var(--icen-token-card)",
1956
+ stroke: tone,
1957
+ "stroke-width": "1.5"
1958
+ });
1959
+ setMarkData(dot, { index: i, seriesIndex: si, seriesName: s.name || void 0, label: axes[i] ?? "", value: s.values[i] ?? 0 });
1960
+ svg.appendChild(dot);
1961
+ }
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 });
1963
+ });
1964
+ wrap.appendChild(svg);
1965
+ body.appendChild(wrap);
1966
+ const legend = opts.series.length > 1 ? toggleLegend(legendItems, wrap) : null;
1967
+ if (legend) wrap.appendChild(legend);
1968
+ });
1969
+ attachChartInteraction(el, "radar");
1970
+ return el;
1971
+ }
1972
+ function niceCeil(v) {
1973
+ if (v <= 0) return 1;
1974
+ const exp = Math.floor(Math.log10(v));
1975
+ const base = Math.pow(10, exp);
1976
+ const frac = v / base;
1977
+ let nice;
1978
+ if (frac <= 1) nice = 1;
1979
+ else if (frac <= 2) nice = 2;
1980
+ else if (frac <= 5) nice = 5;
1981
+ else nice = 10;
1982
+ return nice * base;
1983
+ }
1984
+ function isoDate(d) {
1985
+ const m = String(d.getMonth() + 1).padStart(2, "0");
1986
+ const day = String(d.getDate()).padStart(2, "0");
1987
+ return `${d.getFullYear()}-${m}-${day}`;
1988
+ }
1989
+ function weekdayLabelAt(defaults, day) {
1990
+ return defaults[(day + 6) % 7] ?? "";
1991
+ }
1992
+ function levelOf(v, max) {
1993
+ return v <= 0 ? 0 : Math.min(4, Math.ceil(v / max * 4));
1994
+ }
1995
+ function padFirstWeek(grid, firstDate, weekStart) {
1996
+ const first = /* @__PURE__ */ new Date(`${firstDate}T00:00:00`);
1997
+ const pad = (first.getDay() + 7 - weekStart) % 7;
1998
+ for (let i = 0; i < pad; i++) {
1999
+ const blank = document.createElement("span");
2000
+ blank.className = "icen-chart-heatmap-cell is-blank";
2001
+ grid.appendChild(blank);
2002
+ }
2003
+ return pad;
2004
+ }
2005
+ function heatmapLegend(labels) {
2006
+ const legend = document.createElement("div");
2007
+ legend.className = "icen-chart-heatmap-legend";
2008
+ const less = document.createElement("span");
2009
+ less.textContent = labels?.less ?? t("charts.few");
2010
+ const more = document.createElement("span");
2011
+ more.textContent = labels?.more ?? t("charts.many");
2012
+ legend.appendChild(less);
2013
+ for (let lv = 0; lv <= 4; lv++) {
2014
+ const cell = document.createElement("span");
2015
+ cell.className = "icen-chart-heatmap-cell";
2016
+ cell.dataset.icenLevel = String(lv);
2017
+ legend.appendChild(cell);
2018
+ }
2019
+ legend.appendChild(more);
2020
+ return legend;
2021
+ }
2022
+ function renderHeatmap(el, opts) {
2023
+ if (typeof document === "undefined") return el;
2024
+ withChrome(el, opts, true, (body) => {
2025
+ let entries;
2026
+ if (opts.data && opts.data.length > 0) {
2027
+ entries = opts.data;
2028
+ } else if (opts.values && opts.values.length > 0) {
2029
+ const today = /* @__PURE__ */ new Date();
2030
+ entries = opts.values.map((value, i) => {
2031
+ const d = new Date(today);
2032
+ d.setDate(d.getDate() - (opts.values.length - 1 - i));
2033
+ return { date: isoDate(d), value };
2034
+ });
2035
+ } else {
2036
+ renderEmpty(body, opts.emptyLabel);
2037
+ return;
2038
+ }
2039
+ const weeks = opts.weeks ?? Math.ceil(entries.length / 7);
2040
+ if (entries.length > weeks * 7) entries = entries.slice(entries.length - weeks * 7);
2041
+ const max = maxOf(entries.map((e) => e.value));
2042
+ const wrap = document.createElement("div");
2043
+ wrap.className = "icen-chart-heatmap";
2044
+ if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
2045
+ const grid = document.createElement("div");
2046
+ grid.className = "icen-chart-heatmap-grid";
2047
+ grid.setAttribute("role", "img");
2048
+ grid.setAttribute("aria-label", chartAriaLabel(opts.title, t("charts.type.heatmap")));
2049
+ padFirstWeek(grid, entries[0].date, opts.weekStart === 0 ? 0 : 1);
2050
+ for (const [i, entry] of entries.entries()) {
2051
+ const cell = document.createElement("span");
2052
+ cell.className = "icen-chart-heatmap-cell";
2053
+ cell.dataset.icenLevel = String(levelOf(entry.value, max));
2054
+ cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
2055
+ setMarkData(cell, { index: i, label: entry.date, value: entry.value });
2056
+ grid.appendChild(cell);
2057
+ }
2058
+ wrap.append(grid, heatmapLegend(opts.labels));
2059
+ body.appendChild(wrap);
2060
+ });
2061
+ attachChartInteraction(el, "heatmap");
2062
+ return el;
2063
+ }
2064
+ function renderSparkline(el, opts) {
2065
+ if (typeof document === "undefined") return el;
2066
+ withChrome(el, opts, false, (body) => {
2067
+ const values = opts.values;
2068
+ if (values.length === 0) {
2069
+ renderEmpty(body, opts.emptyLabel);
2070
+ return;
2071
+ }
2072
+ const width = 96;
2073
+ const height = 28;
2074
+ const pad = 3;
2075
+ const max = maxOf(values);
2076
+ const tone = toneVar(opts.tone);
2077
+ const points = values.map((value, i) => ({
2078
+ x: values.length <= 1 ? width / 2 : pad + i / (values.length - 1) * (width - pad * 2),
2079
+ y: height - pad - value / max * (height - pad * 2),
2080
+ value
2081
+ }));
2082
+ const line = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(" ");
2083
+ const last = points[points.length - 1];
2084
+ const first = points[0];
2085
+ const area = `${line} L${last.x.toFixed(1)},${height - pad} L${first.x.toFixed(1)},${height - pad} Z`;
2086
+ const gradientId = `icen-chart-line-${++lineGradientSeq}`;
2087
+ const wrap = document.createElement("div");
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")) });
2090
+ const defs = svgEl("defs", {});
2091
+ const gradient = svgEl("linearGradient", { id: gradientId, x1: "0", x2: "0", y1: "0", y2: "1" });
2092
+ gradient.appendChild(svgEl("stop", { offset: "0%", "stop-color": tone, "stop-opacity": "0.28" }));
2093
+ gradient.appendChild(svgEl("stop", { offset: "100%", "stop-color": tone, "stop-opacity": "0.02" }));
2094
+ defs.appendChild(gradient);
2095
+ svg.appendChild(defs);
2096
+ svg.appendChild(svgEl("path", { d: area, fill: `url(#${gradientId})` }));
2097
+ svg.appendChild(svgEl("path", {
2098
+ d: line,
2099
+ fill: "none",
2100
+ stroke: tone,
2101
+ "stroke-width": "1.8",
2102
+ "stroke-linecap": "round",
2103
+ "stroke-linejoin": "round"
2104
+ }));
2105
+ const dot = svgEl("circle", {
2106
+ cx: last.x.toFixed(1),
2107
+ cy: last.y.toFixed(1),
2108
+ r: "2.4",
2109
+ fill: "var(--icen-token-card)",
2110
+ stroke: tone,
2111
+ "stroke-width": "1.6"
2112
+ });
2113
+ const title = svgEl("title", {});
2114
+ title.textContent = fmt(opts, last.value);
2115
+ dot.appendChild(title);
2116
+ svg.appendChild(dot);
2117
+ wrap.appendChild(svg);
2118
+ body.appendChild(wrap);
2119
+ });
2120
+ attachChartInteraction(el, "sparkline");
2121
+ return el;
2122
+ }
2123
+ function renderGauge(el, opts) {
2124
+ if (typeof document === "undefined") return el;
2125
+ el.textContent = "";
2126
+ const max = opts.max ?? 100;
2127
+ const ratio = max > 0 ? Math.min(1, Math.max(0, opts.value / max)) : 0;
2128
+ const radius = 42;
2129
+ const circumference = 2 * Math.PI * radius;
2130
+ const wrap = document.createElement("div");
2131
+ wrap.className = "icen-chart-gauge";
2132
+ if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
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", {
2136
+ cx: "60",
2137
+ cy: "60",
2138
+ r: String(radius),
2139
+ fill: "none",
2140
+ stroke: "var(--icen-token-line-soft)",
2141
+ "stroke-width": "10"
2142
+ }));
2143
+ const dash = ratio * circumference;
2144
+ svg.appendChild(svgEl("circle", {
2145
+ cx: "60",
2146
+ cy: "60",
2147
+ r: String(radius),
2148
+ fill: "none",
2149
+ stroke: toneVar(opts.tone),
2150
+ "stroke-width": "10",
2151
+ "stroke-dasharray": `${dash} ${circumference - dash}`,
2152
+ "stroke-linecap": "round"
2153
+ }));
2154
+ const center = document.createElement("div");
2155
+ center.className = "icen-chart-gauge-center";
2156
+ const val = document.createElement("span");
2157
+ val.className = "icen-chart-gauge-value";
2158
+ val.textContent = opts.formatValue ? opts.formatValue(opts.value) : `${Math.round(ratio * 100)}%`;
2159
+ center.appendChild(val);
2160
+ if (opts.label) {
2161
+ const lab = document.createElement("span");
2162
+ lab.className = "icen-chart-gauge-label";
2163
+ lab.textContent = opts.label;
2164
+ center.appendChild(lab);
2165
+ }
2166
+ wrap.append(svg, center);
2167
+ el.appendChild(wrap);
2168
+ attachChartInteraction(el, "gauge");
2169
+ return el;
2170
+ }
2171
+ function renderCalendar(el, opts) {
2172
+ if (typeof document === "undefined") return el;
2173
+ withChrome(el, opts, true, (body) => {
2174
+ let entries;
2175
+ if (opts.data && opts.data.length > 0) {
2176
+ entries = opts.data;
2177
+ } else if (opts.dates && opts.dates.length > 0 && opts.values) {
2178
+ entries = opts.dates.slice(0, opts.values.length).map((date, i) => ({ date, value: opts.values[i] ?? 0 }));
2179
+ } else {
2180
+ renderEmpty(body, opts.emptyLabel);
2181
+ return;
2182
+ }
2183
+ const wrap = document.createElement("div");
2184
+ wrap.className = "icen-chart-calendar";
2185
+ if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
2186
+ const weekStart = opts.weekStart === 0 ? 0 : 1;
2187
+ const weekdayDefaults = t("charts.calendarWeekdays");
2188
+ const weekdayLabels = opts.labels?.weekdays ?? weekdayDefaults.map((_, i) => weekdayLabelAt(weekdayDefaults, (weekStart + i) % 7));
2189
+ const monthLabel = (m) => opts.labels?.months?.[m] ?? t("charts.monthLabel", { p0: m + 1 });
2190
+ const grid = document.createElement("div");
2191
+ grid.className = "icen-chart-heatmap-grid";
2192
+ grid.setAttribute("role", "img");
2193
+ grid.setAttribute("aria-label", chartAriaLabel(opts.title, t("charts.type.heatmap")));
2194
+ const months = document.createElement("div");
2195
+ months.className = "icen-chart-calendar-months";
2196
+ const weekdayRow = document.createElement("div");
2197
+ weekdayRow.className = "icen-chart-calendar-weekdays";
2198
+ weekdayLabels.forEach((w) => {
2199
+ const s = document.createElement("span");
2200
+ s.textContent = w;
2201
+ weekdayRow.appendChild(s);
2202
+ });
2203
+ const max = maxOf(entries.map((e) => e.value));
2204
+ const pad = padFirstWeek(grid, entries[0].date, weekStart);
2205
+ let lastMonth = -1;
2206
+ entries.forEach((entry, i) => {
2207
+ const d = /* @__PURE__ */ new Date(`${entry.date}T00:00:00`);
2208
+ const col = Math.floor((pad + i) / 7);
2209
+ if (d.getMonth() !== lastMonth) {
2210
+ lastMonth = d.getMonth();
2211
+ const label = document.createElement("span");
2212
+ label.className = "icen-chart-calendar-month";
2213
+ label.textContent = monthLabel(d.getMonth());
2214
+ label.style.setProperty("--col", String(col));
2215
+ months.appendChild(label);
2216
+ }
2217
+ const cell = document.createElement("span");
2218
+ cell.className = "icen-chart-heatmap-cell";
2219
+ cell.dataset.icenLevel = String(levelOf(entry.value, max));
2220
+ cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
2221
+ setMarkData(cell, { index: i, label: entry.date, value: entry.value });
2222
+ grid.appendChild(cell);
2223
+ });
2224
+ const legend = heatmapLegend(opts.labels);
2225
+ const columns = Math.max(1, Math.ceil((pad + entries.length) / 7));
2226
+ months.style.setProperty("--columns", String(columns));
2227
+ wrap.append(months, weekdayRow, grid, legend);
2228
+ body.appendChild(wrap);
2229
+ });
2230
+ attachChartInteraction(el, "calendar");
2231
+ return el;
2232
+ }
2233
+ function renderScatter(el, opts) {
2234
+ if (typeof document === "undefined") return el;
2235
+ withChrome(el, opts, false, (body) => {
2236
+ const pts = opts.points.filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y));
2237
+ if (pts.length === 0) {
2238
+ renderEmpty(body, opts.emptyLabel);
2239
+ return;
2240
+ }
2241
+ const width = fluidWidth(body);
2242
+ const height = 220;
2243
+ const pad = 30;
2244
+ let xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, sMax = 0;
2245
+ for (const p of pts) {
2246
+ if (p.x < xMin) xMin = p.x;
2247
+ if (p.x > xMax) xMax = p.x;
2248
+ if (p.y < yMin) yMin = p.y;
2249
+ if (p.y > yMax) yMax = p.y;
2250
+ if ((p.size ?? 0) > sMax) sMax = p.size ?? 0;
2251
+ }
2252
+ if (xMax === xMin) xMax = xMin + 1;
2253
+ if (yMax === yMin) yMax = yMin + 1;
2254
+ const rOf = (size) => size && sMax > 0 ? 3 + size / sMax * 7 : 4;
2255
+ const wrap = document.createElement("div");
2256
+ wrap.className = "icen-chart-scatter";
2257
+ const tone = toneVar(opts.tone);
2258
+ const svg = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img", "aria-label": chartAriaLabel(opts.title, t("charts.type.scatter")) });
2259
+ const xAt = (x) => pad + (x - xMin) / (xMax - xMin) * (width - pad * 2);
2260
+ const yAt = (y) => height - pad - (y - yMin) / (yMax - yMin) * (height - pad * 2);
2261
+ for (let i = 0; i <= 3; i++) {
2262
+ const y = pad + i * (height - pad * 2) / 3;
2263
+ svg.appendChild(svgEl("line", {
2264
+ x1: String(pad),
2265
+ x2: String(width - pad),
2266
+ y1: String(y),
2267
+ y2: String(y),
2268
+ stroke: "var(--icen-token-line-soft)",
2269
+ "stroke-width": "1"
2270
+ }));
2271
+ const tv = yMin + (yMax - yMin) * (3 - i) / 3;
2272
+ const txt = svgEl("text", {
2273
+ x: String(pad - 4),
2274
+ y: String(y + 3),
2275
+ "text-anchor": "end",
2276
+ "font-size": "8",
2277
+ fill: "var(--icen-token-text-faint)"
2278
+ });
2279
+ txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
2280
+ svg.appendChild(txt);
2281
+ }
2282
+ for (let i = 0; i <= 4; i++) {
2283
+ const x = pad + i * (width - pad * 2) / 4;
2284
+ svg.appendChild(svgEl("line", {
2285
+ x1: String(x),
2286
+ x2: String(x),
2287
+ y1: String(pad),
2288
+ y2: String(height - pad),
2289
+ stroke: "var(--icen-token-line-soft)",
2290
+ "stroke-width": "1",
2291
+ "stroke-dasharray": "2 3"
2292
+ }));
2293
+ const tv = xMin + (xMax - xMin) * i / 4;
2294
+ const txt = svgEl("text", {
2295
+ x: String(x),
2296
+ y: String(height - pad + 12),
2297
+ "text-anchor": "middle",
2298
+ "font-size": "8",
2299
+ fill: "var(--icen-token-text-faint)"
2300
+ });
2301
+ txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
2302
+ svg.appendChild(txt);
2303
+ }
2304
+ if (opts.yLabel) {
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);
2308
+ }
2309
+ if (opts.xLabel) {
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);
2313
+ }
2314
+ pts.forEach((p, i) => {
2315
+ const c = svgEl("circle", {
2316
+ cx: xAt(p.x).toFixed(1),
2317
+ cy: yAt(p.y).toFixed(1),
2318
+ r: rOf(p.size).toFixed(1),
2319
+ fill: tone,
2320
+ "fill-opacity": "0.55",
2321
+ stroke: tone,
2322
+ "stroke-width": "1"
2323
+ });
2324
+ const title = svgEl("title", {});
2325
+ title.textContent = `${p.label ?? `#${i + 1}`}: (${fmt(opts, p.x)}, ${fmt(opts, p.y)}${p.size != null ? `, size ${fmt(opts, p.size)}` : ""})`;
2326
+ c.appendChild(title);
2327
+ setMarkData(c, { index: i, label: p.label ?? `#${i + 1}`, value: p.y });
2328
+ c.dataset.chartX = String(p.x);
2329
+ svg.appendChild(c);
2330
+ });
2331
+ wrap.appendChild(svg);
2332
+ body.appendChild(wrap);
2333
+ });
2334
+ if (el.dataset.icenChartBody == null) observeFluidWidth(el, () => renderScatter(el, opts));
2335
+ attachChartInteraction(el, "scatter");
2336
+ return el;
2337
+ }
2338
+ function chartFormatValue(format) {
2339
+ if (!format) return (v) => String(v);
2340
+ const notation = format.notation ?? "raw";
2341
+ return (v) => {
2342
+ let text;
2343
+ if (notation === "compact") {
2344
+ const abs = Math.abs(v);
2345
+ text = abs >= 1e6 ? `${Math.round(v / 1e5) / 10}M` : abs >= 1e3 ? `${Math.round(v / 100) / 10}k` : String(v);
2346
+ } else if (notation === "percent") {
2347
+ text = `${Math.round(v * 10) / 10}%`;
2348
+ } else if (notation === "ms") {
2349
+ if (v <= 0) text = "0ms";
2350
+ else if (v < 1e3) text = `${Math.round(v)}ms`;
2351
+ else if (v < 6e4) text = `${Math.round(v / 100) / 10}s`;
2352
+ else text = `${Math.floor(v / 6e4)}m`;
2353
+ } else {
2354
+ text = String(Math.round(v * 100) / 100);
2355
+ }
2356
+ return format.unit ? `${text} ${format.unit}` : text;
2357
+ };
2358
+ }
2359
+ function pivotData(spec) {
2360
+ const data = spec.data;
2361
+ if (!Array.isArray(data) || data.length === 0) return {};
2362
+ const d = spec.dims ?? {};
2363
+ const labelField = d.label ?? "label";
2364
+ const seriesField = d.series ?? "series";
2365
+ const valueField = d.value ?? "value";
2366
+ const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : Number(v) || 0;
2367
+ const str = (v) => typeof v === "string" ? v : v == null ? "" : String(v);
2368
+ if (d.x || data.some((r) => typeof r.x === "number")) {
2369
+ const xf = d.x ?? "x";
2370
+ const yf = d.y ?? "y";
2371
+ const sf = d.size ?? "size";
2372
+ const lf = d.label ?? "label";
2373
+ const points = data.filter((r) => r[xf] != null && r[yf] != null).map((r) => ({
2374
+ x: num(r[xf]),
2375
+ y: num(r[yf]),
2376
+ size: r[sf] != null ? num(r[sf]) : void 0,
2377
+ label: r[lf] != null ? str(r[lf]) : void 0
2378
+ }));
2379
+ return { points };
2380
+ }
2381
+ const labels = [];
2382
+ const seenLabel = /* @__PURE__ */ new Set();
2383
+ const hasSeries = data.some((r) => r[seriesField] != null && r[seriesField] !== "");
2384
+ const seriesNames = hasSeries ? [] : [""];
2385
+ const seenSeries = /* @__PURE__ */ new Set();
2386
+ for (const r of data) {
2387
+ const l = str(r[labelField]);
2388
+ if (!seenLabel.has(l)) {
2389
+ seenLabel.add(l);
2390
+ labels.push(l);
2391
+ }
2392
+ if (hasSeries) {
2393
+ const sn = str(r[seriesField]);
2394
+ if (!seenSeries.has(sn)) {
2395
+ seenSeries.add(sn);
2396
+ seriesNames.push(sn);
2397
+ }
2398
+ }
2399
+ }
2400
+ const labelIndex = new Map(labels.map((l, i) => [l, i]));
2401
+ const seriesIndex = new Map(seriesNames.map((s, i) => [s, i]));
2402
+ const matrix = seriesNames.map(() => labels.map(() => NaN));
2403
+ for (const r of data) {
2404
+ const li = labelIndex.get(str(r[labelField])) ?? 0;
2405
+ const si = hasSeries ? seriesIndex.get(str(r[seriesField])) ?? 0 : 0;
2406
+ (matrix[si] ?? [])[li] = num(r[valueField]);
2407
+ }
2408
+ for (const row of matrix) for (let i = 0; i < row.length; i++) if (Number.isNaN(row[i])) row[i] = 0;
2409
+ if (hasSeries) {
2410
+ return {
2411
+ labels,
2412
+ series: seriesNames.map((name, si) => ({ name, values: matrix[si] ?? [] }))
2413
+ };
2414
+ }
2415
+ return { labels, values: matrix[0] ?? [] };
2416
+ }
2417
+ var CHART_TYPE_ALIASES = {
2418
+ bar: "vbar",
2419
+ column: "vbar",
2420
+ "vertical-bar": "vbar",
2421
+ "bar-vertical": "vbar",
2422
+ "horizontal-bar": "hbar",
2423
+ "bar-horizontal": "hbar",
2424
+ ranking: "hbar",
2425
+ pie: "donut",
2426
+ ring: "donut",
2427
+ "pie-chart": "donut",
2428
+ "stacked-bar": "stack",
2429
+ "stacked-bar-100": "stack",
2430
+ ratio: "stack",
2431
+ time: "line",
2432
+ timeline: "line",
2433
+ trend: "line",
2434
+ "trend-line": "line",
2435
+ contribution: "calendar",
2436
+ "contribution-graph": "calendar",
2437
+ "calendar-heatmap": "calendar",
2438
+ heat: "heatmap",
2439
+ matrix: "heatmap",
2440
+ bubble: "scatter",
2441
+ "scatter-plot": "scatter",
2442
+ dial: "gauge",
2443
+ progress: "gauge",
2444
+ mini: "sparkline",
2445
+ "trend-sparkline": "sparkline",
2446
+ spider: "radar",
2447
+ "radar-chart": "radar"
2448
+ };
2449
+ function toNum(v) {
2450
+ if (typeof v === "number" && Number.isFinite(v)) return v;
2451
+ if (typeof v === "string" && v.trim() !== "") {
2452
+ const n = Number(v.replace(/[,\s%]/g, ""));
2453
+ if (Number.isFinite(n)) return n;
2454
+ }
2455
+ return void 0;
2456
+ }
2457
+ function normalizeChartSpec(raw) {
2458
+ let src = raw;
2459
+ if (typeof src === "string") {
2460
+ try {
2461
+ src = JSON.parse(src);
2462
+ } catch {
2463
+ return {};
2464
+ }
2465
+ }
2466
+ if (!src || typeof src !== "object" || Array.isArray(src)) {
2467
+ if (Array.isArray(src)) src = { data: src };
2468
+ else return {};
2469
+ }
2470
+ const s = src;
2471
+ const spec = {};
2472
+ if (typeof s.type === "string") {
2473
+ const t2 = s.type.trim().toLowerCase();
2474
+ spec.type = CHART_TYPE_ALIASES[t2] ?? t2;
2475
+ }
2476
+ for (const k of ["title", "tone", "emptyLabel"]) {
2477
+ if (typeof s[k] === "string") spec[k] = s[k];
2478
+ }
2479
+ for (const k of ["stacked", "fill", "tooltip", "legend"]) {
2480
+ if (typeof s[k] === "boolean") spec[k] = s[k];
2481
+ }
2482
+ for (const k of ["weeks", "max", "levels"]) {
2483
+ const n = toNum(s[k]);
2484
+ if (n != null) spec[k] = n;
2485
+ }
2486
+ if (Array.isArray(s.labels)) spec.labels = s.labels.map((v) => String(v ?? ""));
2487
+ if (Array.isArray(s.values)) {
2488
+ const values = s.values.map(toNum).filter((v) => v != null);
2489
+ if (values.length > 0) spec.values = values;
2490
+ }
2491
+ if (Array.isArray(s.dates)) spec.dates = s.dates.map((v) => String(v ?? ""));
2492
+ if (Array.isArray(s.axes)) spec.axes = s.axes.map((v) => String(v ?? ""));
2493
+ if (Array.isArray(s.series)) {
2494
+ const series = s.series.filter((v) => !!v && typeof v === "object").map((v, i) => {
2495
+ const values = Array.isArray(v.values) ? v.values.map(toNum).filter((n) => n != null) : [];
2496
+ const sv = { name: String(v.name ?? t("charts.seriesName", { p0: i + 1 })), values };
2497
+ const tone = v.tone;
2498
+ if (typeof tone === "string" || typeof tone === "number") sv.tone = tone;
2499
+ return sv;
2500
+ }).filter((v) => v.values.length > 0);
2501
+ if (series.length > 0) spec.series = series;
2502
+ }
2503
+ if (Array.isArray(s.segments)) {
2504
+ const segments = s.segments.filter((v) => !!v && typeof v === "object").map((v) => {
2505
+ const seg = { label: String(v.label ?? v.name ?? ""), value: toNum(v.value) ?? 0 };
2506
+ if (typeof v.tone === "string") seg.tone = v.tone;
2507
+ return seg;
2508
+ }).filter((v) => v.label !== "" || v.value !== 0);
2509
+ if (segments.length > 0) spec.segments = segments;
2510
+ }
2511
+ if (Array.isArray(s.points)) {
2512
+ const points = s.points.filter((v) => !!v && typeof v === "object").map((v) => ({
2513
+ x: toNum(v.x) ?? 0,
2514
+ y: toNum(v.y) ?? 0,
2515
+ size: toNum(v.size),
2516
+ label: typeof v.label === "string" ? v.label : void 0
2517
+ }));
2518
+ if (points.length > 0) spec.points = points;
2519
+ }
2520
+ if (Array.isArray(s.data)) spec.data = s.data.filter((v) => !!v && typeof v === "object");
2521
+ if (s.dims && typeof s.dims === "object" && !Array.isArray(s.dims)) spec.dims = s.dims;
2522
+ if (s.format && typeof s.format === "object") {
2523
+ const f = s.format;
2524
+ const format = {};
2525
+ if (typeof f.unit === "string") format.unit = f.unit;
2526
+ if (typeof f.notation === "string" && ["compact", "percent", "ms", "raw"].includes(f.notation)) {
2527
+ format.notation = f.notation;
2528
+ }
2529
+ spec.format = format;
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
+ }
2565
+ const isDateRecords = spec.data?.length && spec.data[0] && "date" in spec.data[0];
2566
+ if (spec.data && spec.data.length > 0 && !isDateRecords && !spec.series && !spec.points && !spec.values && !spec.segments) {
2567
+ Object.assign(spec, pivotData(spec));
2568
+ }
2569
+ return spec;
2570
+ }
2571
+ var TEMPORAL_HIT_RATIO = 0.6;
2572
+ function looksTemporal(labels) {
2573
+ if (labels.length === 0) return false;
2574
+ let hits = 0;
2575
+ for (const l of labels) {
2576
+ if (/^\d{4}-\d{2}(-\d{2})?$/.test(l)) hits++;
2577
+ else if (/^\d{4}\/\d{1,2}(\/\d{1,2})?$/.test(l)) hits++;
2578
+ else if (/^\d{4}年/.test(l)) hits++;
2579
+ else if (/^[Qq][1-4]$/.test(l)) hits++;
2580
+ else if (/^(周一|周二|周三|周四|周五|周六|周日|Mon|Tue|Wed|Thu|Fri|Sat|Sun)/i.test(l)) hits++;
2581
+ else if (/^第?\d+[日时周月]/.test(l)) hits++;
2582
+ else if (/^(v|ver\.?|version[ .]?)?\d+(\.\d+)+$/i.test(l)) hits++;
2583
+ else if (/^\d{4}$/.test(l)) hits++;
2584
+ }
2585
+ return hits / labels.length >= TEMPORAL_HIT_RATIO;
2586
+ }
2587
+ function inferChartType(spec) {
2588
+ if (spec.points && spec.points.length > 0) return "scatter";
2589
+ if (spec.dates && spec.dates.length > 0) return "calendar";
2590
+ if (spec.segments && spec.segments.length > 0) return "donut";
2591
+ if (spec.axes && spec.axes.length >= 3) return "radar";
2592
+ const labels = spec.labels ?? [];
2593
+ const multiSeries = (spec.series?.length ?? 0) > 1;
2594
+ if (labels.length > 0) {
2595
+ if (looksTemporal(labels)) return "line";
2596
+ if (multiSeries) return "vbar";
2597
+ return labels.length > 8 ? "hbar" : "vbar";
2598
+ }
2599
+ if (multiSeries) return "line";
2600
+ if (spec.values && spec.values.length > 1) return "line";
2601
+ if (typeof spec.max === "number" && spec.values && spec.values.length === 1) return "gauge";
2602
+ return "vbar";
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
+ }
2989
+ function dispatchChartEvent(root, type, mark, chart, phase, pointer) {
2990
+ const read = (name) => mark.getAttribute(name) ?? void 0;
2991
+ const numOr = (v) => v == null ? void 0 : Number(v);
2992
+ const detail = {
2993
+ type,
2994
+ phase,
2995
+ chart,
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")),
3001
+ target: mark,
3002
+ pointerX: pointer?.x,
3003
+ pointerY: pointer?.y
3004
+ };
3005
+ emitIcen(root, `icen:chart-${type}`, detail);
3006
+ }
3007
+ var tooltipEl = null;
3008
+ function chartTooltip() {
3009
+ if (!tooltipEl) {
3010
+ tooltipEl = document.createElement("div");
3011
+ tooltipEl.className = "icen-chart-tooltip";
3012
+ tooltipEl.setAttribute("role", "tooltip");
3013
+ tooltipEl.hidden = true;
3014
+ document.body.appendChild(tooltipEl);
3015
+ }
3016
+ return tooltipEl;
3017
+ }
3018
+ function applyTooltipSizing(host) {
3019
+ const tip = chartTooltip();
3020
+ const raw = host?.closest(".icen-chart, [data-icen-chart-root]")?.getAttribute("data-icen-panel-max") ?? null;
3021
+ const n = raw != null ? Number(raw) : NaN;
3022
+ tip.style.maxWidth = Number.isFinite(n) && n > 0 ? `${n}px` : "";
3023
+ }
3024
+ function tooltipText(mark) {
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");
3028
+ const head = series ? label ? `${series} \xB7 ${label}` : series : label;
3029
+ return value != null ? `${head}\uFF1A${value}` : head;
3030
+ }
3031
+ function moveTooltip(x, y) {
3032
+ const tip = chartTooltip();
3033
+ const pad = 12;
3034
+ const w = tip.offsetWidth || 120;
3035
+ const h2 = tip.offsetHeight || 28;
3036
+ let left = x + pad;
3037
+ let top = y - h2 - pad;
3038
+ if (left + w > window.innerWidth - 8) left = x - w - pad;
3039
+ if (top < 8) top = y + pad;
3040
+ tip.style.left = `${Math.round(left)}px`;
3041
+ tip.style.top = `${Math.round(top)}px`;
3042
+ }
3043
+ var chartDelegateMeta = /* @__PURE__ */ new WeakMap();
3044
+ var chartDelegateUnbind = /* @__PURE__ */ new WeakMap();
3045
+ function bindChartEvents(root) {
3046
+ const noop = () => void 0;
3047
+ if (typeof document === "undefined") return noop;
3048
+ const attached = root;
3049
+ if (attached.__icenChartBound) {
3050
+ const prev = chartDelegateUnbind.get(root);
3051
+ if (prev) return prev;
3052
+ }
3053
+ for (let p = root.parentElement; p; p = p.parentElement) {
3054
+ if (p.__icenChartBound) return noop;
3055
+ }
3056
+ const typeNow = () => chartDelegateMeta.get(root)?.type() ?? "vbar";
3057
+ const tipEnabled = () => chartDelegateMeta.get(root)?.tooltip() ?? true;
3058
+ const markOf = (e) => {
3059
+ const t2 = e.target;
3060
+ return t2 instanceof Element ? t2.closest("[data-icen-chart-mark]") : null;
3061
+ };
3062
+ let hoverMark = null;
3063
+ const enterMark = (mark, x, y) => {
3064
+ hoverMark = mark;
3065
+ mark.classList.add("is-hover");
3066
+ dispatchChartEvent(root, "hover", mark, typeNow(), "enter", { x, y });
3067
+ if (tipEnabled()) {
3068
+ const tip = chartTooltip();
3069
+ tip.textContent = tooltipText(mark);
3070
+ applyTooltipSizing(mark);
3071
+ tip.hidden = false;
3072
+ moveTooltip(x, y);
3073
+ }
3074
+ };
3075
+ const leaveMark = (pointer) => {
3076
+ if (!hoverMark) return;
3077
+ hoverMark.classList.remove("is-hover");
3078
+ dispatchChartEvent(root, "hover", hoverMark, typeNow(), "leave", pointer);
3079
+ hoverMark = null;
3080
+ chartTooltip().hidden = true;
3081
+ };
3082
+ const bindings = [];
3083
+ const on = (type, fn) => {
3084
+ const listener = (evt) => fn(evt);
3085
+ root.addEventListener(type, listener);
3086
+ bindings.push([type, listener]);
3087
+ };
3088
+ on("pointerover", (e) => {
3089
+ const mark = markOf(e);
3090
+ if (!mark || mark === hoverMark) return;
3091
+ leaveMark({ x: e.clientX, y: e.clientY });
3092
+ enterMark(mark, e.clientX, e.clientY);
3093
+ });
3094
+ on("pointermove", (e) => {
3095
+ if (hoverMark && tipEnabled()) {
3096
+ dispatchChartEvent(root, "hover", hoverMark, typeNow(), "move", { x: e.clientX, y: e.clientY });
3097
+ moveTooltip(e.clientX, e.clientY);
3098
+ }
3099
+ });
3100
+ on("pointerout", (e) => {
3101
+ const mark = markOf(e);
3102
+ if (mark && mark === hoverMark) leaveMark({ x: e.clientX, y: e.clientY });
3103
+ });
3104
+ on("pointerleave", () => leaveMark());
3105
+ on("click", (e) => {
3106
+ const mark = markOf(e);
3107
+ if (mark) dispatchChartEvent(root, "click", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
3108
+ });
3109
+ on("dblclick", (e) => {
3110
+ const mark = markOf(e);
3111
+ if (mark) dispatchChartEvent(root, "dblclick", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
3112
+ });
3113
+ on("contextmenu", (e) => {
3114
+ const mark = markOf(e);
3115
+ if (!mark) return;
3116
+ e.preventDefault();
3117
+ dispatchChartEvent(root, "contextmenu", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
3118
+ });
3119
+ const unbind = () => {
3120
+ for (const [type, listener] of bindings) root.removeEventListener(type, listener);
3121
+ bindings.length = 0;
3122
+ attached.__icenChartBound = false;
3123
+ chartDelegateUnbind.delete(root);
3124
+ if (hoverMark) {
3125
+ hoverMark.classList.remove("is-hover");
3126
+ hoverMark = null;
3127
+ }
3128
+ chartTooltip().hidden = true;
3129
+ };
3130
+ attached.__icenChartBound = true;
3131
+ chartDelegateUnbind.set(root, unbind);
3132
+ return unbind;
3133
+ }
3134
+ function attachChartInteraction(el, type) {
3135
+ chartDelegateMeta.set(el, { type: () => type, tooltip: () => true });
3136
+ bindChartEvents(el);
3137
+ }
3138
+ function hasLegendOf(spec) {
3139
+ switch (spec.type) {
3140
+ case "donut":
3141
+ case "stack":
3142
+ return (spec.segments?.length ?? 0) > 0;
3143
+ case "heatmap":
3144
+ case "calendar":
3145
+ return true;
3146
+ case "line":
3147
+ case "vbar":
3148
+ case "radar":
3149
+ return (spec.series?.length ?? 0) > 1;
3150
+ default:
3151
+ return false;
3152
+ }
3153
+ }
3154
+ function renderChart(el, raw) {
3155
+ const noop = () => void 0;
3156
+ if (typeof document === "undefined") {
3157
+ return {
3158
+ el,
3159
+ spec: {},
3160
+ update: noop,
3161
+ append: noop,
3162
+ toDataURL: () => void 0,
3163
+ on: () => noop,
3164
+ destroy: noop
3165
+ };
3166
+ }
3167
+ const root = el;
3168
+ let current2 = normalizeChartSpec(raw);
3169
+ const typeOf = () => current2.type ?? "vbar";
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
+ };
3287
+ const render = () => {
3288
+ if (!current2.type) current2 = { ...current2, type: inferChartType(current2) };
3289
+ const spec = current2;
3290
+ const type = spec.type;
3291
+ const formatValue = chartFormatValue(spec.format);
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;
3306
+ withChrome(root, spec, hasLegendOf(spec), (body) => {
3307
+ if (loading) {
3308
+ renderChartSkeleton(body);
3309
+ return;
3310
+ }
3311
+ switch (type) {
3312
+ case "line":
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 });
3314
+ break;
3315
+ case "area":
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 });
3317
+ break;
3318
+ case "vbar":
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 });
3320
+ break;
3321
+ case "hbar":
3322
+ renderHBar(body, { labels: view.labels ?? [], values: view.values ?? [], tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
3323
+ break;
3324
+ case "stack":
3325
+ renderStack(body, { segments: view.segments ?? [], formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
3326
+ break;
3327
+ case "donut":
3328
+ renderDonut(body, { segments: view.segments ?? [], formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
3329
+ break;
3330
+ case "radar": {
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 });
3333
+ break;
3334
+ }
3335
+ case "heatmap":
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 });
3337
+ break;
3338
+ case "calendar":
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 });
3340
+ break;
3341
+ case "sparkline":
3342
+ renderSparkline(body, { labels: view.labels ?? [], values: view.values ?? [], tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
3343
+ break;
3344
+ case "gauge":
3345
+ renderGauge(body, { value: view.values?.[0] ?? 0, max: view.max, tone: view.tone, label: view.labels?.[0], formatValue });
3346
+ break;
3347
+ case "scatter":
3348
+ renderScatter(body, { points: view.points ?? [], tone: view.tone, formatValue, emptyLabel: view.emptyLabel, title: view.title, legend: view.legend });
3349
+ break;
3350
+ default:
3351
+ renderEmpty(body, view.emptyLabel);
3352
+ }
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();
3363
+ };
3364
+ chartDelegateMeta.set(root, { type: typeOf, tooltip: () => current2.tooltip !== false });
3365
+ const unbindEvents = bindChartEvents(root);
3366
+ render();
3367
+ observeFluidWidth(root, render);
3368
+ return {
3369
+ el: root,
3370
+ /** 归一 + 推断后的当前规格(getter:update/append 换引用后永远读到最新) */
3371
+ get spec() {
3372
+ return current2;
3373
+ },
3374
+ update(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
+ }
3428
+ render();
3429
+ },
3430
+ on(name, listener) {
3431
+ const handler = (event) => {
3432
+ listener(event.detail, event);
3433
+ };
3434
+ root.addEventListener(`icen:chart-${name}`, handler);
3435
+ return () => root.removeEventListener(`icen:chart-${name}`, handler);
3436
+ },
3437
+ toDataURL: createChartToDataURL(root),
3438
+ destroy() {
3439
+ releaseFluidWidth(root);
3440
+ hiddenLegendKeys.delete(root);
3441
+ zoomDragCleanup?.();
3442
+ root.textContent = "";
3443
+ root.classList.remove("is-loading");
3444
+ unbindEvents();
3445
+ chartTooltip().hidden = true;
3446
+ }
3447
+ };
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
+ }
3460
+ var GRAPH_W = 360;
3461
+ var GRAPH_H = 260;
3462
+ var GRAPH_PAD = 28;
3463
+ var GRAPH_LW = GRAPH_W - GRAPH_PAD * 2;
3464
+ var GRAPH_LH = GRAPH_H - GRAPH_PAD * 2;
3465
+
3466
+ // src/behaviors/kb-answer.ts
3467
+ var ASSET_TYPE_LABELS = {
3468
+ "parameterized-query": t("kbAnswer.kindParameterized"),
3469
+ function: t("kbAnswer.kindFunction"),
3470
+ curated: t("kbAnswer.kindCurated")
3471
+ };
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>';
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>';
3474
+ function renderVerifiedBadge(via, opts) {
3475
+ const btn = h("button", "icen-kb-verified");
3476
+ btn.type = "button";
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")));
3482
+ btn.appendChild(badge);
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")));
3486
+ btn.addEventListener("click", () => {
3487
+ emitIcen(btn, "icen:kb-verified-open", { assetId: via.assetId });
3488
+ opts?.onVerifiedOpen?.(via.assetId);
3489
+ });
3490
+ return btn;
3491
+ }
3492
+ function renderPermissionNotice(p) {
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);
3497
+ box.appendChild(iconBox);
3498
+ box.appendChild(h("span", "kb-perm-text", p.rowLevelPolicy ? t("kbAnswer.filteredDetail", { p0: p.rowLevelPolicy }) : t("kbAnswer.filtered")));
3499
+ if (p.maskedColumns?.length) {
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));
3503
+ box.appendChild(mask);
3504
+ }
3505
+ return box;
3506
+ }
3507
+ function renderBody(text, citations) {
3508
+ const box = h("div", "icen-kb-answer-body");
3509
+ for (const seg of parseInlineCitations(text)) {
3510
+ if (typeof seg.n === "number" && citations && seg.n >= 1 && seg.n <= citations.length) {
3511
+ const sup = h("sup", "icen-kb-citation", `[${seg.n}]`);
3512
+ sup.dataset.icenCite = String(seg.n);
3513
+ const c = citations[seg.n - 1];
3514
+ if (c?.title) sup.title = c.title;
3515
+ box.appendChild(sup);
3516
+ } else {
3517
+ box.appendChild(document.createTextNode(seg.text));
3518
+ }
3519
+ }
3520
+ return box;
3521
+ }
3522
+ var NUMERIC_TYPES = /* @__PURE__ */ new Set([
3523
+ "number",
3524
+ "int",
3525
+ "integer",
3526
+ "float",
3527
+ "double",
3528
+ "decimal",
3529
+ "numeric",
3530
+ "bigint",
3531
+ "smallint",
3532
+ "real"
3533
+ ]);
3534
+ function isNumericColumn(col, colIndex, rows) {
3535
+ if (col.type) return NUMERIC_TYPES.has(col.type.toLowerCase());
3536
+ for (const row of rows) {
3537
+ const v = row[colIndex];
3538
+ if (v == null) continue;
3539
+ return typeof v === "number";
3540
+ }
3541
+ return false;
3542
+ }
3543
+ function renderResult(result, opts) {
3544
+ if (!result.columns.length) return null;
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) })));
3548
+ if (result.durationMs != null) {
3549
+ meta.appendChild(h("span", "icen-kb-num kb-result-dur", `${Math.round(result.durationMs)} ms`));
3550
+ }
3551
+ if (result.exportFormats?.length) {
3552
+ const exports = h("span", "icen-kb-result-export");
3553
+ for (const fmt2 of result.exportFormats) {
3554
+ const btn = h("button", "icen-kb-result-export-btn", t("kbAnswer.exportNoun", { p0: fmt2 }));
3555
+ btn.type = "button";
3556
+ btn.dataset.icenFormat = fmt2;
3557
+ if (!opts?.onExport) btn.disabled = true;
3558
+ else btn.addEventListener("click", () => opts.onExport?.(fmt2));
3559
+ exports.appendChild(btn);
3560
+ }
3561
+ meta.appendChild(exports);
3562
+ }
3563
+ wrap.appendChild(meta);
3564
+ const scroll = h("div", "icen-kb-result-scroll");
3565
+ const table = h("table", "icen-kb-result-table");
3566
+ const numCols = result.columns.map((c, i) => isNumericColumn(c, i, result.rows));
3567
+ const thead = document.createElement("thead");
3568
+ const headRow = document.createElement("tr");
3569
+ result.columns.forEach((c, i) => {
3570
+ headRow.appendChild(h("th", numCols[i] ? "icen-kb-num" : "", c.name));
3571
+ });
3572
+ thead.appendChild(headRow);
3573
+ table.appendChild(thead);
3574
+ const tbody = document.createElement("tbody");
3575
+ for (const row of result.rows) {
3576
+ const tr = document.createElement("tr");
3577
+ result.columns.forEach((_, i) => {
3578
+ const v = row[i];
3579
+ if (v == null) {
3580
+ const td = h("td", "icen-kb-result-null", "\u2014");
3581
+ tr.appendChild(td);
3582
+ } else {
3583
+ tr.appendChild(h("td", typeof v === "number" ? "icen-kb-num" : "", String(v)));
3584
+ }
3585
+ });
3586
+ tbody.appendChild(tr);
3587
+ }
3588
+ table.appendChild(tbody);
3589
+ scroll.appendChild(table);
3590
+ wrap.appendChild(scroll);
3591
+ if (result.truncated) {
3592
+ const total = result.totalRows != null ? formatCount(result.totalRows) : "\u2014";
3593
+ const limitPart = result.limit != null ? `\uFF08LIMIT ${formatCount(result.limit)}\uFF09` : "";
3594
+ wrap.appendChild(h("div", "icen-kb-notice icen-kb-result-truncated", t("kbAnswer.truncated", { p0: formatCount(result.rowCount), p1: total, p2: limitPart })));
3595
+ }
3596
+ return wrap;
3597
+ }
3598
+ var CHART_SWITCHES = [
3599
+ { type: "line", label: t("kbAnswer.chartLine") },
3600
+ { type: "vbar", label: t("kbAnswer.chartBar") },
3601
+ { type: "donut", label: t("kbAnswer.chartPie") }
3602
+ ];
3603
+ function switchable(spec) {
3604
+ const hasCat = (spec.labels?.length ?? 0) > 0 && ((spec.values?.length ?? 0) > 0 || (spec.series?.length ?? 0) > 0);
3605
+ return hasCat || (spec.segments?.length ?? 0) > 0;
3606
+ }
3607
+ function specAsType(base, target) {
3608
+ if (target === "donut") {
3609
+ if (base.segments?.length) return { ...base, type: "donut" };
3610
+ const labels = base.labels ?? [];
3611
+ const values = base.values ?? [];
3612
+ return {
3613
+ ...base,
3614
+ type: "donut",
3615
+ segments: labels.map((label, i) => ({ label, value: values[i] ?? 0 }))
3616
+ };
3617
+ }
3618
+ if ((!base.labels?.length || !(base.values?.length || base.series?.length)) && base.segments?.length) {
3619
+ return {
3620
+ ...base,
3621
+ type: target,
3622
+ labels: base.segments.map((s) => s.label),
3623
+ values: base.segments.map((s) => s.value),
3624
+ segments: void 0
3625
+ };
3626
+ }
3627
+ return { ...base, type: target };
3628
+ }
3629
+ function renderChartArea(raw) {
3630
+ if (raw == null) return null;
3631
+ const norm = normalizeChartSpec(raw);
3632
+ const area = h("div", "icen-kb-answer-chart");
3633
+ const mount = h("div", "icen-chart icen-kb-chart-mount");
3634
+ const handle = renderChart(mount, norm);
3635
+ if (switchable(handle.spec)) {
3636
+ const switchRow = h("div", "icen-kb-chart-switch");
3637
+ const chips = /* @__PURE__ */ new Map();
3638
+ for (const s of CHART_SWITCHES) {
3639
+ const chip = h("button", "icen-kb-chart-chip", s.label);
3640
+ chip.type = "button";
3641
+ chip.dataset.chartType = s.type;
3642
+ if (handle.spec.type === s.type) chip.classList.add("is-active");
3643
+ chip.addEventListener("click", () => {
3644
+ if (chip.classList.contains("is-active")) return;
3645
+ chips.forEach((c) => c.classList.remove("is-active"));
3646
+ chip.classList.add("is-active");
3647
+ handle.update(specAsType(handle.spec, s.type));
3648
+ });
3649
+ chips.set(s.type, chip);
3650
+ switchRow.appendChild(chip);
3651
+ }
3652
+ area.appendChild(switchRow);
3653
+ }
3654
+ area.appendChild(mount);
3655
+ return area;
3656
+ }
3657
+ function renderKbAnswer(el, model, opts) {
3658
+ if (typeof document === "undefined") return el;
3659
+ const m = model ?? {};
3660
+ el.textContent = "";
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");
3664
+ if (m.kind === "verified" && m.verifiedVia) badges.appendChild(renderVerifiedBadge(m.verifiedVia, opts));
3665
+ if (m.permissionNotice && (m.permissionNotice.rowLevelPolicy || m.permissionNotice.maskedColumns?.length)) {
3666
+ badges.appendChild(renderPermissionNotice(m.permissionNotice));
3667
+ }
3668
+ if (badges.children.length > 0) root.appendChild(badges);
3669
+ if (typeof m.body === "string" && m.body) root.appendChild(renderBody(m.body, m.citations));
3670
+ if (m.clarify && (m.clarify.question || m.clarify.options?.length)) {
3671
+ const slot = h("div", "icen-kb-answer-clarify");
3672
+ renderKbClarify(slot, m.clarify);
3673
+ root.appendChild(slot);
3674
+ }
3675
+ if (m.explain) {
3676
+ const f = m.explain;
3677
+ const hasAny = f.tables?.length || f.columns?.length || f.filters?.length || f.aggregates?.length || f.metrics?.length;
3678
+ if (hasAny) {
3679
+ const slot = h("div", "icen-kb-answer-explain");
3680
+ renderKbExplain(slot, f);
3681
+ root.appendChild(slot);
3682
+ }
3683
+ }
3684
+ if (m.result) {
3685
+ const resultBox = renderResult(normalizeQueryResult(m.result), opts);
3686
+ if (resultBox) root.appendChild(resultBox);
3687
+ }
3688
+ const chartArea = renderChartArea(m.chartSpec);
3689
+ if (chartArea) root.appendChild(chartArea);
3690
+ const followUps = (m.followUps ?? []).map((s) => String(s ?? "").trim()).filter(Boolean);
3691
+ if (followUps.length > 0) {
3692
+ const row = h("div", "icen-kb-answer-followups");
3693
+ row.appendChild(h("span", "icen-kb-followups-label", t("kbAnswer.followups")));
3694
+ for (const text of followUps) {
3695
+ const chip = h("button", "icen-kb-chip icen-kb-followup", text);
3696
+ chip.type = "button";
3697
+ chip.addEventListener("click", () => opts?.onFollowUp?.(text));
3698
+ row.appendChild(chip);
3699
+ }
3700
+ root.appendChild(row);
3701
+ }
3702
+ el.appendChild(root);
3703
+ return el;
3704
+ }
3705
+ export {
3706
+ renderKbAnswer
3707
+ };