@icen.ai/ui 0.8.0 → 0.9.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 (228) hide show
  1. package/AGENTS.md +63 -14
  2. package/README.md +17 -9
  3. package/dist/base.css +4 -3
  4. package/dist/behaviors/accordion.d.mts +5 -2
  5. package/dist/behaviors/accordion.mjs +36 -11
  6. package/dist/behaviors/ai-branch.d.mts +19 -0
  7. package/dist/behaviors/ai-branch.mjs +217 -0
  8. package/dist/behaviors/ai-chat.d.mts +11 -7
  9. package/dist/behaviors/ai-chat.mjs +134 -51
  10. package/dist/behaviors/ai-composer.d.mts +28 -3
  11. package/dist/behaviors/ai-composer.mjs +399 -229
  12. package/dist/behaviors/ai-core.d.mts +9 -4
  13. package/dist/behaviors/ai-core.mjs +21 -8
  14. package/dist/behaviors/ai-diff.d.mts +2 -2
  15. package/dist/behaviors/ai-diff.mjs +172 -41
  16. package/dist/behaviors/ai-feedback.d.mts +26 -0
  17. package/dist/behaviors/ai-feedback.mjs +303 -0
  18. package/dist/behaviors/ai-panel.d.mts +26 -9
  19. package/dist/behaviors/ai-panel.mjs +211 -110
  20. package/dist/behaviors/ai-provider.mjs +54 -48
  21. package/dist/behaviors/ai-threads.d.mts +38 -0
  22. package/dist/behaviors/ai-threads.mjs +319 -0
  23. package/dist/behaviors/ai-tool.d.mts +12 -4
  24. package/dist/behaviors/ai-tool.mjs +316 -263
  25. package/dist/behaviors/ai-tools.d.mts +9 -6
  26. package/dist/behaviors/ai-tools.mjs +264 -155
  27. package/dist/behaviors/back-top.mjs +16 -1
  28. package/dist/behaviors/carousel.d.mts +5 -2
  29. package/dist/behaviors/carousel.mjs +53 -26
  30. package/dist/behaviors/charts.d.mts +170 -22
  31. package/dist/behaviors/charts.mjs +1016 -150
  32. package/dist/behaviors/command-palette.d.mts +40 -6
  33. package/dist/behaviors/command-palette.mjs +143 -15
  34. package/dist/behaviors/context-menu.d.mts +21 -4
  35. package/dist/behaviors/context-menu.mjs +122 -38
  36. package/dist/behaviors/controls.d.mts +59 -0
  37. package/dist/behaviors/controls.mjs +601 -0
  38. package/dist/behaviors/copy.d.mts +5 -2
  39. package/dist/behaviors/copy.mjs +37 -12
  40. package/dist/behaviors/datatable.d.mts +12 -0
  41. package/dist/behaviors/datatable.mjs +226 -43
  42. package/dist/behaviors/date-picker.d.mts +2 -2
  43. package/dist/behaviors/date-picker.mjs +171 -32
  44. package/dist/behaviors/dropdown.d.mts +6 -2
  45. package/dist/behaviors/dropdown.mjs +164 -25
  46. package/dist/behaviors/events.d.mts +523 -0
  47. package/dist/behaviors/events.mjs +208 -0
  48. package/dist/behaviors/input.mjs +25 -12
  49. package/dist/behaviors/kb-access.d.mts +34 -0
  50. package/dist/behaviors/kb-access.mjs +569 -0
  51. package/dist/behaviors/kb-acl.d.mts +50 -0
  52. package/dist/behaviors/kb-acl.mjs +405 -0
  53. package/dist/behaviors/kb-answer.d.mts +38 -0
  54. package/dist/behaviors/kb-answer.mjs +2055 -0
  55. package/dist/behaviors/kb-audit.d.mts +23 -0
  56. package/dist/behaviors/kb-audit.mjs +375 -0
  57. package/dist/behaviors/kb-canvas.d.mts +44 -0
  58. package/dist/behaviors/kb-canvas.mjs +589 -0
  59. package/dist/behaviors/kb-chain.d.mts +15 -0
  60. package/dist/behaviors/kb-chain.mjs +270 -0
  61. package/dist/behaviors/kb-checkpoint.d.mts +16 -0
  62. package/dist/behaviors/kb-checkpoint.mjs +342 -0
  63. package/dist/behaviors/kb-chunks.d.mts +31 -0
  64. package/dist/behaviors/kb-chunks.mjs +475 -0
  65. package/dist/behaviors/kb-citation.d.mts +38 -0
  66. package/dist/behaviors/kb-citation.mjs +677 -0
  67. package/dist/behaviors/kb-clarify.d.mts +20 -0
  68. package/dist/behaviors/kb-clarify.mjs +248 -0
  69. package/dist/behaviors/kb-conflict.d.mts +22 -0
  70. package/dist/behaviors/kb-conflict.mjs +284 -0
  71. package/dist/behaviors/kb-connector.d.mts +17 -0
  72. package/dist/behaviors/kb-connector.mjs +375 -0
  73. package/dist/behaviors/kb-core.d.mts +529 -0
  74. package/dist/behaviors/kb-core.mjs +890 -0
  75. package/dist/behaviors/kb-eval.d.mts +22 -0
  76. package/dist/behaviors/kb-eval.mjs +408 -0
  77. package/dist/behaviors/kb-explain.d.mts +11 -0
  78. package/dist/behaviors/kb-explain.mjs +243 -0
  79. package/dist/behaviors/kb-filter.d.mts +31 -0
  80. package/dist/behaviors/kb-filter.mjs +587 -0
  81. package/dist/behaviors/kb-gap.d.mts +27 -0
  82. package/dist/behaviors/kb-gap.mjs +273 -0
  83. package/dist/behaviors/kb-hittest.d.mts +34 -0
  84. package/dist/behaviors/kb-hittest.mjs +409 -0
  85. package/dist/behaviors/kb-metadata.d.mts +29 -0
  86. package/dist/behaviors/kb-metadata.mjs +326 -0
  87. package/dist/behaviors/kb-passage.d.mts +41 -0
  88. package/dist/behaviors/kb-passage.mjs +529 -0
  89. package/dist/behaviors/kb-pipeline.d.mts +18 -0
  90. package/dist/behaviors/kb-pipeline.mjs +381 -0
  91. package/dist/behaviors/kb-qa.d.mts +26 -0
  92. package/dist/behaviors/kb-qa.mjs +332 -0
  93. package/dist/behaviors/kb-rerank.d.mts +20 -0
  94. package/dist/behaviors/kb-rerank.mjs +315 -0
  95. package/dist/behaviors/kb-retrieval.d.mts +49 -0
  96. package/dist/behaviors/kb-retrieval.mjs +746 -0
  97. package/dist/behaviors/kb-review.d.mts +44 -0
  98. package/dist/behaviors/kb-review.mjs +451 -0
  99. package/dist/behaviors/kb-sandbox.d.mts +33 -0
  100. package/dist/behaviors/kb-sandbox.mjs +325 -0
  101. package/dist/behaviors/kb-segment.d.mts +32 -0
  102. package/dist/behaviors/kb-segment.mjs +499 -0
  103. package/dist/behaviors/kb-sources.d.mts +27 -0
  104. package/dist/behaviors/kb-sources.mjs +373 -0
  105. package/dist/behaviors/kb-sql.d.mts +43 -0
  106. package/dist/behaviors/kb-sql.mjs +948 -0
  107. package/dist/behaviors/kb-trace.d.mts +16 -0
  108. package/dist/behaviors/kb-trace.mjs +414 -0
  109. package/dist/behaviors/kb-visibility.d.mts +44 -0
  110. package/dist/behaviors/kb-visibility.mjs +403 -0
  111. package/dist/behaviors/kb-who-can.d.mts +32 -0
  112. package/dist/behaviors/kb-who-can.mjs +379 -0
  113. package/dist/behaviors/modal.d.mts +24 -5
  114. package/dist/behaviors/modal.mjs +76 -10
  115. package/dist/behaviors/nav.d.mts +1 -1
  116. package/dist/behaviors/nav.mjs +33 -7
  117. package/dist/behaviors/notification.d.mts +20 -4
  118. package/dist/behaviors/notification.mjs +53 -73
  119. package/dist/behaviors/popover.d.mts +39 -3
  120. package/dist/behaviors/popover.mjs +90 -4
  121. package/dist/behaviors/select.d.mts +4 -2
  122. package/dist/behaviors/select.mjs +161 -24
  123. package/dist/behaviors/sidebar.d.mts +5 -2
  124. package/dist/behaviors/sidebar.mjs +43 -13
  125. package/dist/behaviors/slider.d.mts +5 -2
  126. package/dist/behaviors/slider.mjs +54 -43
  127. package/dist/behaviors/split-pane.d.mts +6 -2
  128. package/dist/behaviors/split-pane.mjs +62 -21
  129. package/dist/behaviors/tabs.d.mts +3 -2
  130. package/dist/behaviors/tabs.mjs +65 -14
  131. package/dist/behaviors/tag-input.d.mts +5 -2
  132. package/dist/behaviors/tag-input.mjs +61 -21
  133. package/dist/behaviors/theme.d.mts +13 -2
  134. package/dist/behaviors/theme.mjs +48 -4
  135. package/dist/behaviors/toast.d.mts +30 -8
  136. package/dist/behaviors/toast.mjs +74 -7
  137. package/dist/behaviors/tree.d.mts +5 -2
  138. package/dist/behaviors/tree.mjs +38 -15
  139. package/dist/behaviors/upload.mjs +21 -23
  140. package/dist/cli.mjs +1 -1
  141. package/dist/components/ai-branch.mjs +3 -0
  142. package/dist/components/ai-chat.css +295 -0
  143. package/dist/components/ai-feedback.mjs +3 -0
  144. package/dist/components/ai-panel.css +218 -1
  145. package/dist/components/ai-threads.mjs +3 -0
  146. package/dist/components/ai-tool-call.mjs +1 -1
  147. package/dist/components/badge.css +1 -1
  148. package/dist/components/breadcrumb.css +0 -32
  149. package/dist/components/chart-calendar.mjs +1 -2
  150. package/dist/components/chart-graph.css +57 -0
  151. package/dist/components/chart-graph.mjs +4 -0
  152. package/dist/components/chart-map.mjs +3 -0
  153. package/dist/components/chart-pie.css +0 -6
  154. package/dist/components/chart-scatter.mjs +1 -1
  155. package/dist/components/charts.css +33 -1
  156. package/dist/components/charts.mjs +1 -0
  157. package/dist/components/content.css +3 -16
  158. package/dist/components/datatable.css +1 -1
  159. package/dist/components/date-picker.css +1 -1
  160. package/dist/components/kb-access.mjs +4 -0
  161. package/dist/components/kb-acl.mjs +4 -0
  162. package/dist/components/kb-agent.css +481 -0
  163. package/dist/components/kb-answer.mjs +4 -0
  164. package/dist/components/kb-audit.mjs +4 -0
  165. package/dist/components/kb-canvas.mjs +4 -0
  166. package/dist/components/kb-chain.mjs +4 -0
  167. package/dist/components/kb-checkpoint.mjs +4 -0
  168. package/dist/components/kb-chunks.mjs +4 -0
  169. package/dist/components/kb-citation.mjs +4 -0
  170. package/dist/components/kb-clarify.mjs +4 -0
  171. package/dist/components/kb-conflict.mjs +4 -0
  172. package/dist/components/kb-connector.mjs +4 -0
  173. package/dist/components/kb-data.css +649 -0
  174. package/dist/components/kb-eval.mjs +4 -0
  175. package/dist/components/kb-explain.mjs +4 -0
  176. package/dist/components/kb-filter.mjs +4 -0
  177. package/dist/components/kb-gap.mjs +4 -0
  178. package/dist/components/kb-ground.css +566 -0
  179. package/dist/components/kb-hittest.mjs +4 -0
  180. package/dist/components/kb-ingest.css +898 -0
  181. package/dist/components/kb-metadata.mjs +4 -0
  182. package/dist/components/kb-ops.css +548 -0
  183. package/dist/components/kb-passage.mjs +4 -0
  184. package/dist/components/kb-perm.css +444 -0
  185. package/dist/components/kb-pipeline.mjs +4 -0
  186. package/dist/components/kb-qa.mjs +4 -0
  187. package/dist/components/kb-rerank.mjs +4 -0
  188. package/dist/components/kb-retrieval.mjs +4 -0
  189. package/dist/components/kb-review.mjs +4 -0
  190. package/dist/components/kb-sandbox.mjs +4 -0
  191. package/dist/components/kb-search.css +940 -0
  192. package/dist/components/kb-segment.mjs +4 -0
  193. package/dist/components/kb-sources.mjs +4 -0
  194. package/dist/components/kb-sql.mjs +4 -0
  195. package/dist/components/kb-trace.mjs +4 -0
  196. package/dist/components/kb-visibility.mjs +4 -0
  197. package/dist/components/kb-who-can.mjs +4 -0
  198. package/dist/components/kb.css +252 -0
  199. package/dist/components/kb.mjs +9 -0
  200. package/dist/components/notification.css +2 -3
  201. package/dist/components/pagination.css +3 -1
  202. package/dist/components/pagination.mjs +1 -0
  203. package/dist/components/rating.mjs +1 -0
  204. package/dist/components/segmented.css +9 -1
  205. package/dist/components/segmented.mjs +1 -1
  206. package/dist/components/select.css +67 -4
  207. package/dist/components/steps.css +13 -3
  208. package/dist/components/steps.mjs +1 -0
  209. package/dist/components/switch.css +3 -1
  210. package/dist/components/switch.mjs +1 -0
  211. package/dist/components/table.mjs +1 -0
  212. package/dist/components/tabs.css +23 -0
  213. package/dist/components/tag-input.css +1 -6
  214. package/dist/components/upload.css +0 -15
  215. package/dist/index.d.mts +48 -11
  216. package/dist/index.mjs +13205 -3615
  217. package/dist/registry.json +366 -15
  218. package/dist/retro-effects.css +3 -16
  219. package/dist/tokens/retro-effects.css +3 -16
  220. package/dist/tokens/style-profiles.css +3 -2
  221. package/dist/tokens/typography.css +2 -0
  222. package/dist/tokens.css +5 -2
  223. package/dist/ui.css +5713 -267
  224. package/docs/research/2026-10-03-ai-kb-components.md +392 -0
  225. package/docs/research/2026-10-03-kb-permissions.md +992 -0
  226. package/docs/spec/ai-native.md +7 -4
  227. package/docs/spec/kb-family.md +443 -0
  228. package/package.json +2 -2
@@ -0,0 +1,2055 @@
1
+ // src/behaviors/ai-core.ts
2
+ var svg = (body) => `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${body}</svg>`;
3
+ var ICON_TERMINAL = svg('<path d="m4 17 6-6-6-6"/><path d="M12 19h8"/>');
4
+ var ICON_FILE = svg(
5
+ '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/>'
6
+ );
7
+ var ICON_FILE_PEN = svg(
8
+ '<path d="M12.5 22H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8l5 5v3.5"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M19 6.5 16.5 4"/><path d="M21.378 16.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"/>'
9
+ );
10
+ var ICON_FILE_PLUS = svg(
11
+ '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M9 15h6"/><path d="M12 12v6"/>'
12
+ );
13
+ var ICON_TRASH = svg(
14
+ '<path d="M3 6h18"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/><path d="M10 11v6"/><path d="M14 11v6"/>'
15
+ );
16
+ var ICON_SEARCH = svg('<circle cx="11" cy="11" r="8"/><path d="m21 21-4.3-4.3"/>');
17
+ var ICON_FOLDER_SEARCH = svg(
18
+ '<path d="M11 20H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H20a2 2 0 0 1 2 2v4"/><circle cx="17" cy="17" r="3"/><path d="m21 21-1.9-1.9"/>'
19
+ );
20
+ var ICON_SCAN_SEARCH = svg(
21
+ '<path d="M3 7V5a2 2 0 0 1 2-2h2"/><path d="M17 3h2a2 2 0 0 1 2 2v2"/><path d="M21 17v2a2 2 0 0 1-2 2h-2"/><path d="M7 21H5a2 2 0 0 1-2-2v-2"/><circle cx="12" cy="12" r="3"/><path d="m16 16-1.9-1.9"/>'
22
+ );
23
+ var ICON_GLOBE = svg(
24
+ '<circle cx="12" cy="12" r="10"/><path d="M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/>'
25
+ );
26
+ var ICON_DOWNLOAD = svg(
27
+ '<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/><path d="M12 15V3"/>'
28
+ );
29
+ var ICON_BLOCKS = svg(
30
+ '<rect width="7" height="7" x="4" y="4" rx="1"/><rect width="7" height="7" x="13" y="4" rx="1"/><rect width="7" height="7" x="4" y="13" rx="1"/><rect width="7" height="7" x="13" y="13" rx="1"/>'
31
+ );
32
+ var ICON_ZAP = svg(
33
+ '<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>'
34
+ );
35
+ var ICON_LIST = svg(
36
+ '<path d="M3 12h.01"/><path d="M3 18h.01"/><path d="M3 6h.01"/><path d="M8 12h13"/><path d="M8 18h13"/><path d="M8 6h13"/>'
37
+ );
38
+ var ICON_LIST_CHECKS = svg(
39
+ '<path d="m3 17 2 2 4-4"/><path d="m3 7 2 2 4-4"/><path d="M13 6h8"/><path d="M13 12h8"/><path d="M13 18h8"/>'
40
+ );
41
+ var ICON_BOT = svg(
42
+ '<path d="M12 8V4H8"/><rect width="16" height="12" x="4" y="8" rx="2"/><path d="M2 14h2"/><path d="M20 14h2"/><path d="M15 13v2"/><path d="M9 13v2"/>'
43
+ );
44
+ var ICON_CHART = svg(
45
+ '<path d="M3 3v18h18"/><path d="M7 15l4-6 3 4 5-8"/>'
46
+ );
47
+ var ICON_FILE_TEXT = svg(
48
+ '<path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M10 9H8"/><path d="M16 13H8"/><path d="M16 17H8"/>'
49
+ );
50
+ function isObj(v) {
51
+ return typeof v === "object" && v !== null;
52
+ }
53
+ function pickStr(input, keys) {
54
+ if (!isObj(input)) return void 0;
55
+ for (const k of keys) {
56
+ const v = input[k];
57
+ if (typeof v === "string" && v) return v;
58
+ }
59
+ return void 0;
60
+ }
61
+ function pickNum(src, keys) {
62
+ if (!isObj(src)) return void 0;
63
+ for (const k of keys) {
64
+ const v = src[k];
65
+ if (typeof v === "number" && Number.isFinite(v)) return v;
66
+ }
67
+ return void 0;
68
+ }
69
+ function firstLine(s, max = 80) {
70
+ const line = s.split("\n", 1)[0]?.trim() ?? "";
71
+ return line.length > max ? `${line.slice(0, max)}\u2026` : line;
72
+ }
73
+ function filePathOf(input) {
74
+ return pickStr(input, ["file_path", "path", "file", "filename"]);
75
+ }
76
+ var summarizePath = (input) => filePathOf(input);
77
+ function summarizeRead(input) {
78
+ const p = filePathOf(input);
79
+ if (!p) return void 0;
80
+ const offset = pickNum(input, ["offset", "start_line"]);
81
+ if (offset == null) return p;
82
+ const limit = pickNum(input, ["limit", "end_line"]);
83
+ return limit != null ? `${p}:${offset}-${offset + limit}` : `${p}:${offset}`;
84
+ }
85
+ function summarizeShell(input) {
86
+ const cmd = typeof input === "string" ? input : pickStr(input, ["command", "cmd", "script"]);
87
+ return cmd ? firstLine(cmd) : void 0;
88
+ }
89
+ function summarizeGrep(input) {
90
+ const pattern = pickStr(input, ["pattern", "regex", "query"]);
91
+ const p = filePathOf(input);
92
+ if (pattern) return p ? `${firstLine(pattern, 40)} \xB7 ${p}` : firstLine(pattern);
93
+ return p;
94
+ }
95
+ var sum = (fn) => {
96
+ return (input) => fn(input) ?? "";
97
+ };
98
+ var BUILTIN_KINDS = {
99
+ shell: { label: "\u547D\u4EE4", icon: ICON_TERMINAL, tint: "accent", summarize: sum(summarizeShell) },
100
+ read: { label: "\u8BFB\u53D6", icon: ICON_FILE, tint: "info", summarize: sum(summarizeRead) },
101
+ edit: { label: "\u7F16\u8F91", icon: ICON_FILE_PEN, tint: "warning", summarize: sum(summarizePath) },
102
+ write: { label: "\u5199\u5165", icon: ICON_FILE_PLUS, tint: "warning", summarize: sum(summarizePath) },
103
+ rm: { label: "\u5220\u9664", icon: ICON_TRASH, tint: "error", summarize: sum(summarizePath) },
104
+ grep: { label: "\u641C\u7D22", icon: ICON_SEARCH, tint: "info", summarize: sum(summarizeGrep) },
105
+ glob: { label: "\u5339\u914D", icon: ICON_FOLDER_SEARCH, tint: "info", summarize: sum((i) => pickStr(i, ["pattern", "glob", "path"])) },
106
+ browser: { label: "\u6D4F\u89C8", icon: ICON_GLOBE, tint: "accent", summarize: sum((i) => pickStr(i, ["url", "uri", "target"])) },
107
+ search: { label: "\u68C0\u7D22", icon: ICON_SCAN_SEARCH, tint: "accent", summarize: sum((i) => pickStr(i, ["query", "q", "pattern"])) },
108
+ fetch: { label: "\u8BF7\u6C42", icon: ICON_DOWNLOAD, tint: "info", summarize: sum((i) => pickStr(i, ["url", "uri", "endpoint"])) },
109
+ mcp: { label: "MCP", icon: ICON_BLOCKS, tint: "muted", summarize: sum((i) => pickStr(i, ["tool", "method", "name"])) },
110
+ skill: { label: "\u6280\u80FD", icon: ICON_ZAP, tint: "accent", summarize: sum((i) => pickStr(i, ["skill", "name", "command"])) },
111
+ todo: {
112
+ label: "\u5F85\u529E",
113
+ icon: ICON_LIST,
114
+ tint: "info",
115
+ summarize: sum((input) => {
116
+ const items = input && typeof input === "object" && Array.isArray(input.items) ? input.items : Array.isArray(input) ? input : [];
117
+ const done = items.filter((it) => it?.status === "done").length;
118
+ return items.length > 0 ? `${done}/${items.length} \u5B8C\u6210` : "";
119
+ })
120
+ },
121
+ plan: { label: "\u8BA1\u5212", icon: ICON_LIST_CHECKS, tint: "info" },
122
+ subagent: { label: "\u5B50\u4EE3\u7406", icon: ICON_BOT, tint: "accent", summarize: sum((i) => pickStr(i, ["task", "description", "prompt", "message", "subagent_type"])) },
123
+ note: { label: "\u7B14\u8BB0", icon: ICON_FILE_TEXT, tint: "muted" },
124
+ chart: { label: "\u56FE\u8868", icon: ICON_CHART, tint: "info", summarize: sum((i) => pickStr(i, ["title", "name"])) }
125
+ };
126
+ var kindRegistry = new Map(Object.entries(BUILTIN_KINDS));
127
+ function h(tag, className, text) {
128
+ const node = document.createElement(tag);
129
+ if (className) node.className = className;
130
+ if (text != null) node.textContent = text;
131
+ return node;
132
+ }
133
+ function svgIcon(svg2) {
134
+ if (typeof document === "undefined" || typeof DOMParser === "undefined") return null;
135
+ let parsed;
136
+ try {
137
+ parsed = new DOMParser().parseFromString(svg2, "image/svg+xml");
138
+ } catch {
139
+ return null;
140
+ }
141
+ const root = parsed.documentElement;
142
+ if (!root || root.tagName.toLowerCase() !== "svg" || parsed.querySelector("parsererror")) {
143
+ return null;
144
+ }
145
+ const sanitize = (el) => {
146
+ for (const attr of Array.from(el.attributes)) {
147
+ const name = attr.name.toLowerCase();
148
+ if (name.startsWith("on")) el.removeAttribute(attr.name);
149
+ if ((name === "href" || name === "xlink:href") && attr.value.trim().toLowerCase().startsWith("javascript:")) {
150
+ el.removeAttribute(attr.name);
151
+ }
152
+ }
153
+ for (const child of Array.from(el.children)) {
154
+ const tag = child.tagName.toLowerCase();
155
+ if (tag === "script" || tag === "foreignobject") {
156
+ child.remove();
157
+ continue;
158
+ }
159
+ sanitize(child);
160
+ }
161
+ };
162
+ sanitize(root);
163
+ return document.importNode(root, true);
164
+ }
165
+
166
+ // src/behaviors/kb-core.ts
167
+ var ic = (label, d) => ({ label, icon: `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">${d}</svg>` });
168
+ var BUILTIN_SOURCE_TYPES = {
169
+ doc: ic("\u6587\u6863", '<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/>'),
170
+ web: ic("\u7F51\u9875", '<circle cx="12" cy="12" r="10"/><path d="M2 12h20"/><path d="M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20z"/>'),
171
+ wiki: ic("\u77E5\u8BC6\u5E93", '<path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/>'),
172
+ sheet: ic("\u8868\u683C", '<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M3 15h18M9 3v18M15 3v18"/>'),
173
+ db: ic("\u6570\u636E\u5E93", '<ellipse cx="12" cy="5" rx="9" ry="3"/><path d="M3 5v14c0 1.7 4 3 9 3s9-1.3 9-3V5"/><path d="M3 12c0 1.7 4 3 9 3s9-1.3 9-3"/>'),
174
+ ticket: ic("\u5DE5\u5355", '<path d="M3 9V7a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v2a2 2 0 0 0 0 6v2a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-2a2 2 0 0 0 0-6z"/><path d="M13 5v14"/>'),
175
+ code: ic("\u4EE3\u7801", '<path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/>'),
176
+ api: ic("\u63A5\u53E3", '<path d="M4 7a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v3a2 2 0 0 0 0 4v3a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2v-3a2 2 0 0 0 0-4z"/>'),
177
+ image: ic("\u56FE\u7247", '<rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="9" cy="9" r="2"/><path d="m21 15-4.6-4.6a2 2 0 0 0-2.8 0L3 21"/>'),
178
+ mail: ic("\u90AE\u4EF6", '<rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 7L2 7"/>')
179
+ };
180
+ var sourceTypeRegistry = new Map(Object.entries(BUILTIN_SOURCE_TYPES));
181
+ var CITE_RE = /\[(\d{1,3})\]/g;
182
+ function parseInlineCitations(text) {
183
+ const out = [];
184
+ if (!text) return out;
185
+ let last = 0;
186
+ CITE_RE.lastIndex = 0;
187
+ let m;
188
+ while ((m = CITE_RE.exec(text)) !== null) {
189
+ if (m.index > last) out.push({ text: text.slice(last, m.index) });
190
+ out.push({ text: m[0], n: Number(m[1]) });
191
+ last = m.index + m[0].length;
192
+ }
193
+ if (last < text.length) out.push({ text: text.slice(last) });
194
+ return out;
195
+ }
196
+ function normalizeQueryResult(raw) {
197
+ const columns = Array.isArray(raw.columns) ? raw.columns.map((c) => typeof c === "string" ? { name: c } : { name: String(c.name ?? ""), type: c.type }) : [];
198
+ return {
199
+ columns,
200
+ rows: Array.isArray(raw.rows) ? raw.rows.map((r) => Array.isArray(r) ? r.map((v) => typeof v === "number" || typeof v === "string" ? v : null) : []) : [],
201
+ totalRows: typeof raw.totalRows === "number" ? raw.totalRows : void 0,
202
+ truncated: raw.truncated === true,
203
+ limit: typeof raw.limit === "number" ? raw.limit : void 0,
204
+ rowCount: typeof raw.rowCount === "number" ? raw.rowCount : Array.isArray(raw.rows) ? raw.rows.length : 0,
205
+ durationMs: typeof raw.durationMs === "number" ? raw.durationMs : void 0,
206
+ exportFormats: Array.isArray(raw.exportFormats) ? raw.exportFormats.map(String) : void 0
207
+ };
208
+ }
209
+ function formatCount(n) {
210
+ if (n == null || !Number.isFinite(n)) return "\u2014";
211
+ if (Math.abs(n) >= 1e4) return `${(n / 1e3).toFixed(1)}k`;
212
+ return n.toLocaleString("en-US");
213
+ }
214
+
215
+ // src/behaviors/events.ts
216
+ function emitIcen(source, type, detail) {
217
+ return source.dispatchEvent(
218
+ new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
219
+ );
220
+ }
221
+
222
+ // src/behaviors/kb-clarify.ts
223
+ var ICON_HELP = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M9.1 9a3 3 0 0 1 5.8 1c0 2-3 2.4-3 4"/><path d="M12 17.5h.01"/></svg>';
224
+ function toClarification(raw) {
225
+ const r = raw ?? {};
226
+ 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) : [];
227
+ return {
228
+ id: typeof r.id === "string" && r.id ? r.id : void 0,
229
+ question: String(r.question ?? ""),
230
+ options,
231
+ reason: typeof r.reason === "string" && r.reason ? r.reason : void 0
232
+ };
233
+ }
234
+ function renderKbClarify(el, c, opts) {
235
+ if (typeof document === "undefined") return el;
236
+ const clar = toClarification(c);
237
+ el.textContent = "";
238
+ const root = h("div", "kb-clarify");
239
+ if (clar.id) root.dataset.clarifyId = clar.id;
240
+ const q = h("div", "kb-clarify-q");
241
+ const iconBox = h("span", "kb-clarify-qicon");
242
+ const svg2 = svgIcon(ICON_HELP);
243
+ if (svg2) iconBox.appendChild(svg2);
244
+ q.appendChild(iconBox);
245
+ q.appendChild(h("span", "kb-clarify-qtext", clar.question));
246
+ root.appendChild(q);
247
+ const optsWrap = h("div", "kb-clarify-opts");
248
+ const resolved = h("div", "kb-clarify-resolved");
249
+ resolved.hidden = true;
250
+ const pick = (btn) => {
251
+ if (root.classList.contains("is-answered")) return;
252
+ root.classList.add("is-answered");
253
+ btn.classList.add("is-picked");
254
+ Array.from(optsWrap.querySelectorAll(".kb-clarify-opt")).forEach((b) => {
255
+ if (b !== btn) b.disabled = true;
256
+ });
257
+ resolved.textContent = "";
258
+ resolved.appendChild(document.createTextNode("\u5DF2\u89E3\u6790\uFF1A"));
259
+ resolved.appendChild(h("b", "kb-clarify-picked", btn.dataset.label ?? ""));
260
+ resolved.hidden = false;
261
+ const answer = { id: clar.id, value: btn.dataset.value ?? "", label: btn.dataset.label ?? "" };
262
+ emitIcen(btn, "icen:kb-clarify-answer", answer);
263
+ opts?.onAnswer?.(answer);
264
+ };
265
+ for (const o of clar.options) {
266
+ const btn = h("button", "kb-clarify-opt", o.label);
267
+ btn.type = "button";
268
+ btn.dataset.value = o.value;
269
+ btn.dataset.label = o.label;
270
+ btn.addEventListener("click", () => pick(btn));
271
+ optsWrap.appendChild(btn);
272
+ }
273
+ if (optsWrap.children.length > 0) root.appendChild(optsWrap);
274
+ root.appendChild(resolved);
275
+ if (clar.reason) root.appendChild(h("div", "kb-clarify-reason", clar.reason));
276
+ el.appendChild(root);
277
+ return el;
278
+ }
279
+
280
+ // src/behaviors/kb-explain.ts
281
+ function toExplainFact(raw) {
282
+ const r = raw ?? {};
283
+ const list = (v) => {
284
+ if (!Array.isArray(v)) return void 0;
285
+ const out = v.map((s) => String(s ?? "").trim()).filter(Boolean);
286
+ return out.length ? out : void 0;
287
+ };
288
+ const metrics = Array.isArray(r.metrics) ? r.metrics.filter((m) => !!m && typeof m === "object").map((m) => ({
289
+ name: String(m.name ?? ""),
290
+ ref: typeof m.ref === "string" && m.ref ? m.ref : void 0
291
+ })).filter((m) => m.name) : void 0;
292
+ return {
293
+ tables: list(r.tables),
294
+ columns: list(r.columns),
295
+ filters: list(r.filters),
296
+ aggregates: list(r.aggregates),
297
+ metrics: metrics?.length ? metrics : void 0
298
+ };
299
+ }
300
+ function renderGroup(label, items) {
301
+ if (!items?.length) return null;
302
+ const group = h("div", "kb-explain-group");
303
+ group.appendChild(h("span", "kb-explain-group-label", label));
304
+ for (const item of items) group.appendChild(h("span", "kb-chip", item));
305
+ return group;
306
+ }
307
+ function renderKbExplain(el, fact) {
308
+ if (typeof document === "undefined") return el;
309
+ const f = toExplainFact(fact);
310
+ el.textContent = "";
311
+ const root = h("div", "kb-explain");
312
+ root.dataset.kbExplain = "";
313
+ const count = (f.tables?.length ?? 0) + (f.columns?.length ?? 0) + (f.filters?.length ?? 0) + (f.aggregates?.length ?? 0) + (f.metrics?.length ?? 0);
314
+ const head = h("button", "kb-fold-head kb-explain-head");
315
+ head.type = "button";
316
+ head.setAttribute("aria-expanded", "false");
317
+ head.appendChild(h("span", "kb-fold-caret", "\u25B8"));
318
+ head.appendChild(h("span", "kb-explain-title", "\u8FD9\u4E2A\u6570\u5B57\u662F\u600E\u4E48\u7B97\u51FA\u6765\u7684"));
319
+ if (count > 0) {
320
+ const countBox = h("span", "kb-num kb-explain-count", String(count));
321
+ countBox.classList.add("kb-fold-count");
322
+ head.appendChild(countBox);
323
+ }
324
+ root.appendChild(head);
325
+ const body = h("div", "kb-explain-body");
326
+ body.hidden = true;
327
+ const groups = [
328
+ renderGroup("\u8868", f.tables),
329
+ renderGroup("\u5217", f.columns),
330
+ renderGroup("\u7B5B\u9009", f.filters),
331
+ renderGroup("\u805A\u5408", f.aggregates)
332
+ ];
333
+ for (const g of groups) if (g) body.appendChild(g);
334
+ if (f.metrics?.length) {
335
+ const metrics = h("div", "kb-explain-metrics");
336
+ for (const m of f.metrics) {
337
+ const row = h("div", "kb-explain-metric");
338
+ row.appendChild(h("span", "kb-explain-metric-name", m.name));
339
+ if (m.ref) {
340
+ if (!/^\s*(javascript|data|vbscript)\s*:/i.test(m.ref)) {
341
+ const a = h("a", "kb-explain-metric-ref", "\u67E5\u770B\u53E3\u5F84");
342
+ a.href = m.ref;
343
+ a.rel = "noopener";
344
+ row.appendChild(a);
345
+ }
346
+ }
347
+ metrics.appendChild(row);
348
+ }
349
+ body.appendChild(metrics);
350
+ }
351
+ if (body.children.length === 0) {
352
+ body.appendChild(h("div", "kb-empty", "\u6682\u65E0\u53E3\u5F84\u4FE1\u606F"));
353
+ }
354
+ root.appendChild(body);
355
+ head.addEventListener("click", () => {
356
+ const open = body.hidden;
357
+ body.hidden = !open;
358
+ head.setAttribute("aria-expanded", String(open));
359
+ emitIcen(head, "icen:kb-explain-toggle", { el: root, open });
360
+ });
361
+ el.appendChild(root);
362
+ return el;
363
+ }
364
+
365
+ // src/behaviors/charts.ts
366
+ var SVG_NS = "http://www.w3.org/2000/svg";
367
+ var REGISTERED_TONES = /* @__PURE__ */ new Map();
368
+ function toneVar(tone = "accent") {
369
+ const registered = REGISTERED_TONES.get(tone);
370
+ if (registered) return registered;
371
+ switch (tone) {
372
+ case "success":
373
+ return "var(--token-success)";
374
+ case "warning":
375
+ return "var(--token-warning)";
376
+ case "error":
377
+ return "var(--token-error)";
378
+ case "muted":
379
+ return "var(--token-text-faint)";
380
+ case "accent":
381
+ default:
382
+ return "var(--token-accent)";
383
+ }
384
+ }
385
+ function fmt(opts, value) {
386
+ return opts.formatValue ? opts.formatValue(value) : String(value);
387
+ }
388
+ function maxOf(values) {
389
+ let max = 1;
390
+ for (const v of values) if (v > max) max = v;
391
+ return max;
392
+ }
393
+ function sampleLabels(labels, max = 7) {
394
+ if (labels.length <= max) return labels;
395
+ const out = [];
396
+ for (let i = 0; i < max; i++) {
397
+ out.push(labels[Math.round(i * (labels.length - 1) / (max - 1))] ?? "");
398
+ }
399
+ return out;
400
+ }
401
+ function svgEl(tag, attrs) {
402
+ const el = document.createElementNS(SVG_NS, tag);
403
+ for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, v);
404
+ return el;
405
+ }
406
+ function renderEmpty(el, label) {
407
+ const box = document.createElement("div");
408
+ box.className = "chart-empty";
409
+ box.textContent = label ?? "\u6682\u65E0\u6570\u636E";
410
+ el.appendChild(box);
411
+ }
412
+ function legendItem(label, valueText, tone) {
413
+ const row = document.createElement("div");
414
+ row.className = "chart-legend-item";
415
+ const dot = document.createElement("span");
416
+ dot.className = "chart-legend-dot";
417
+ if (tone) dot.style.setProperty("--chart-tone", toneVar(tone));
418
+ const name = document.createElement("span");
419
+ name.className = "chart-legend-label";
420
+ name.textContent = label;
421
+ const val = document.createElement("span");
422
+ val.className = "chart-legend-value";
423
+ val.textContent = valueText;
424
+ row.append(dot, name, val);
425
+ return row;
426
+ }
427
+ function labelsRow(labels) {
428
+ const row = document.createElement("div");
429
+ row.className = "chart-labels";
430
+ for (const label of labels) {
431
+ const span = document.createElement("span");
432
+ span.textContent = label;
433
+ row.appendChild(span);
434
+ }
435
+ return row;
436
+ }
437
+ function normalizeSeriesInput(opts) {
438
+ if (opts.series && opts.series.length > 0) return opts.series;
439
+ return [{ name: "", values: opts.values ?? [], tone: opts.tone }];
440
+ }
441
+ function toneOf(tone, seriesIndex) {
442
+ if (typeof tone === "string") return toneVar(tone);
443
+ if (typeof tone === "number") return paletteVar(tone);
444
+ return seriesIndex > 0 ? paletteVar((seriesIndex - 1) % 6 + 1) : void 0;
445
+ }
446
+ var PALETTE_VARS = [
447
+ "var(--token-accent)",
448
+ "var(--token-accent-cold)",
449
+ "var(--token-success)",
450
+ "var(--token-warning)",
451
+ "var(--token-info)",
452
+ "color-mix(in srgb, var(--token-text) 50%, transparent)"
453
+ ];
454
+ function paletteVar(index) {
455
+ const i = ((Math.floor(index) - 1) % 6 + 6) % 6;
456
+ return PALETTE_VARS[i];
457
+ }
458
+ function setMarkData(node, data) {
459
+ node.setAttribute("data-chart-mark", "1");
460
+ if (data.index != null) node.setAttribute("data-chart-index", String(data.index));
461
+ if (data.seriesIndex != null) node.setAttribute("data-chart-series", String(data.seriesIndex));
462
+ if (data.seriesName != null) node.setAttribute("data-chart-series-name", String(data.seriesName));
463
+ if (data.label != null) node.setAttribute("data-chart-label", String(data.label));
464
+ if (data.value != null) node.setAttribute("data-chart-value", String(data.value));
465
+ }
466
+ function copyMarkData(target, source) {
467
+ for (const attr of Array.from(source.attributes)) {
468
+ if (attr.name.startsWith("data-chart-")) target.setAttribute(attr.name, attr.value);
469
+ }
470
+ }
471
+ var CHART_EYE_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M2.06 12.35a1 1 0 0 1 0-.7 10.75 10.75 0 0 1 19.88 0 1 1 0 0 1 0 .7 10.75 10.75 0 0 1-19.88 0"/><circle cx="12" cy="12" r="3"/></svg>';
472
+ var CHART_EYE_OFF_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M10.73 5.08A10.4 10.4 0 0 1 12 5c7 0 10 7 10 7a13.2 13.2 0 0 1-1.67 2.68"/><path d="M6.61 6.61A13.5 13.5 0 0 0 2 12s3 7 10 7a9.7 9.7 0 0 0 5.39-1.61"/><path d="M2 2l20 20"/><path d="M9.9 9.9a3 3 0 0 0 4.2 4.2"/></svg>';
473
+ function parseChartSvg(svg2) {
474
+ try {
475
+ const doc = new DOMParser().parseFromString(svg2, "image/svg+xml");
476
+ const el = doc.documentElement;
477
+ return el && el.tagName.toLowerCase() === "svg" && !doc.querySelector("parsererror") ? document.importNode(el, true) : null;
478
+ } catch {
479
+ return null;
480
+ }
481
+ }
482
+ function withChrome(el, opts, hasLegend, paint, state) {
483
+ if (el.dataset.chartBody != null) {
484
+ el.textContent = "";
485
+ paint(el);
486
+ return;
487
+ }
488
+ el.textContent = "";
489
+ const body = document.createElement("div");
490
+ body.className = "chart-body";
491
+ body.dataset.chartBody = "";
492
+ if (!opts.title && !hasLegend) {
493
+ el.appendChild(body);
494
+ paint(body);
495
+ return;
496
+ }
497
+ let hidden = state && state.hidden != null ? state.hidden : opts.legend === false;
498
+ if (state) state.hidden = hidden;
499
+ el.setAttribute("data-legend-hidden", String(hidden));
500
+ const head = document.createElement("div");
501
+ head.className = "chart-head";
502
+ const title = document.createElement("div");
503
+ title.className = "chart-title";
504
+ title.textContent = opts.title ?? "";
505
+ if (!opts.title) title.setAttribute("aria-hidden", "true");
506
+ const eye = document.createElement("button");
507
+ eye.type = "button";
508
+ eye.className = "chart-legend-eye";
509
+ eye.dataset.chartLegendEye = "";
510
+ const syncEye = () => {
511
+ const nowHidden = el.getAttribute("data-legend-hidden") === "true";
512
+ eye.setAttribute("aria-pressed", String(!nowHidden));
513
+ eye.setAttribute("aria-label", nowHidden ? "\u663E\u793A\u56FE\u4F8B" : "\u9690\u85CF\u56FE\u4F8B");
514
+ eye.setAttribute("title", nowHidden ? "\u663E\u793A\u56FE\u4F8B" : "\u9690\u85CF\u56FE\u4F8B");
515
+ const icon = parseChartSvg(nowHidden ? CHART_EYE_OFF_SVG : CHART_EYE_SVG);
516
+ if (icon) eye.replaceChildren(icon);
517
+ };
518
+ eye.addEventListener("click", () => {
519
+ const nowHidden = el.getAttribute("data-legend-hidden") === "true";
520
+ hidden = !nowHidden;
521
+ if (state) state.hidden = hidden;
522
+ el.setAttribute("data-legend-hidden", String(hidden));
523
+ syncEye();
524
+ emitIcen(el, "icen:chart-legend-visibility", { visible: hidden ? false : true });
525
+ });
526
+ syncEye();
527
+ head.append(title, eye);
528
+ el.append(head, body);
529
+ paint(body);
530
+ }
531
+ function toggleLegend(items, scope) {
532
+ const legend = document.createElement("div");
533
+ legend.className = "chart-legend chart-legend--toggle";
534
+ for (const item of items) {
535
+ const row = legendItem(item.label, "", item.tone);
536
+ row.classList.add("chart-legend-toggle-item");
537
+ row.setAttribute("role", "button");
538
+ row.tabIndex = 0;
539
+ row.setAttribute("aria-pressed", "true");
540
+ row.dataset.chartKey = item.key;
541
+ const dot = row.querySelector(".chart-legend-dot");
542
+ const toneColor = toneOf(item.tone, item.si + 1);
543
+ if (dot && toneColor) dot.style.setProperty("--chart-tone", toneColor);
544
+ const toggle = () => {
545
+ const off = row.classList.toggle("is-off");
546
+ row.setAttribute("aria-pressed", String(!off));
547
+ scope.querySelectorAll(`[data-chart-key="${CSS.escape(item.key)}"]`).forEach((m) => {
548
+ m.classList.toggle("is-off", off);
549
+ });
550
+ emitIcen(legend, "icen:chart-legend-toggle", { key: item.key, seriesIndex: item.si, hidden: off });
551
+ };
552
+ row.addEventListener("click", toggle);
553
+ row.addEventListener("keydown", (e) => {
554
+ if (e.key === "Enter" || e.key === " ") {
555
+ e.preventDefault();
556
+ toggle();
557
+ }
558
+ });
559
+ legend.appendChild(row);
560
+ }
561
+ for (const item of items) {
562
+ scope.querySelectorAll(`[data-chart-series="${item.si}"]`).forEach((m) => {
563
+ if (!m.hasAttribute("data-chart-key")) m.setAttribute("data-chart-key", item.key);
564
+ });
565
+ }
566
+ return legend;
567
+ }
568
+ function renderVBar(el, opts) {
569
+ if (typeof document === "undefined") return el;
570
+ const seriesList = normalizeSeriesInput(opts);
571
+ const labels = opts.labels ?? [];
572
+ const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
573
+ withChrome(el, opts, seriesList.length > 1, (body) => {
574
+ if (n === 0 || seriesList.length === 0) {
575
+ renderEmpty(body, opts.emptyLabel);
576
+ return;
577
+ }
578
+ const labelsUsed = labels.slice(0, n);
579
+ let max = 1;
580
+ if (opts.stacked && seriesList.length > 1) {
581
+ for (let i = 0; i < n; i++) {
582
+ let sum2 = 0;
583
+ for (const s of seriesList) sum2 += Math.max(0, s.values[i] ?? 0);
584
+ if (sum2 > max) max = sum2;
585
+ }
586
+ } else {
587
+ for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
588
+ }
589
+ const wrap = document.createElement("div");
590
+ wrap.className = "chart-vbar";
591
+ if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
592
+ for (let i = 0; i < n; i++) {
593
+ const track = document.createElement("div");
594
+ track.className = "chart-vbar-track";
595
+ if (seriesList.length > 1) {
596
+ track.classList.add("chart-vbar-track--multi");
597
+ if (opts.stacked) track.classList.add("chart-vbar-track--stacked");
598
+ }
599
+ seriesList.forEach((s, si) => {
600
+ const value = s.values[i] ?? 0;
601
+ const bar = document.createElement("div");
602
+ bar.className = "chart-vbar-bar";
603
+ const height = value > 0 ? Math.max(6, value / max * 100) : 0;
604
+ bar.style.height = `${height}%`;
605
+ const toneColor = toneOf(s.tone, si);
606
+ if (toneColor) bar.style.setProperty("--chart-tone", toneColor);
607
+ bar.setAttribute("title", seriesList.length > 1 ? `${labelsUsed[i]} \xB7 ${s.name}: ${fmt(opts, value)}` : `${labelsUsed[i]}: ${fmt(opts, value)}`);
608
+ setMarkData(bar, {
609
+ index: i,
610
+ seriesIndex: seriesList.length > 1 ? si : void 0,
611
+ seriesName: seriesList.length > 1 ? s.name : void 0,
612
+ label: labelsUsed[i] ?? "",
613
+ value
614
+ });
615
+ track.appendChild(bar);
616
+ });
617
+ wrap.appendChild(track);
618
+ }
619
+ body.append(wrap, labelsRow(labelsUsed));
620
+ if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
621
+ });
622
+ attachChartInteraction(el, "vbar");
623
+ return el;
624
+ }
625
+ function renderHBar(el, opts) {
626
+ if (typeof document === "undefined") return el;
627
+ withChrome(el, opts, false, (body) => {
628
+ const n = Math.min(opts.labels.length, opts.values.length);
629
+ if (n === 0) {
630
+ renderEmpty(body, opts.emptyLabel);
631
+ return;
632
+ }
633
+ const labels = opts.labels.slice(0, n);
634
+ const values = opts.values.slice(0, n);
635
+ const max = maxOf(values);
636
+ const wrap = document.createElement("div");
637
+ wrap.className = "chart-hbar";
638
+ if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
639
+ values.forEach((value, i) => {
640
+ const row = document.createElement("div");
641
+ row.className = "chart-hbar-row";
642
+ const label = document.createElement("span");
643
+ label.className = "chart-hbar-label";
644
+ label.textContent = labels[i] ?? "";
645
+ const track = document.createElement("div");
646
+ track.className = "chart-hbar-track";
647
+ const bar = document.createElement("div");
648
+ bar.className = "chart-hbar-bar";
649
+ bar.style.width = value > 0 ? `${Math.max(2, value / max * 100)}%` : "0%";
650
+ setMarkData(bar, { index: i, label: labels[i] ?? "", value });
651
+ const val = document.createElement("span");
652
+ val.className = "chart-hbar-value";
653
+ val.textContent = fmt(opts, value);
654
+ track.appendChild(bar);
655
+ row.append(label, track, val);
656
+ wrap.appendChild(row);
657
+ });
658
+ body.appendChild(wrap);
659
+ });
660
+ attachChartInteraction(el, "hbar");
661
+ return el;
662
+ }
663
+ function renderStack(el, opts) {
664
+ if (typeof document === "undefined") return el;
665
+ withChrome(el, opts, opts.segments.length > 0, (body) => {
666
+ const total = opts.segments.reduce((sum2, s) => sum2 + s.value, 0);
667
+ if (opts.segments.length === 0 || total <= 0) {
668
+ renderEmpty(body, opts.emptyLabel);
669
+ return;
670
+ }
671
+ const bar = document.createElement("div");
672
+ bar.className = "chart-stack";
673
+ for (const [segIndex, seg] of opts.segments.entries()) {
674
+ const s = document.createElement("div");
675
+ s.className = "chart-stack-seg";
676
+ s.style.width = `${seg.value / total * 100}%`;
677
+ if (seg.tone) s.style.setProperty("--chart-tone", toneVar(seg.tone));
678
+ s.setAttribute("title", `${seg.label}: ${fmt(opts, seg.value)}`);
679
+ setMarkData(s, { index: segIndex, label: seg.label, value: seg.value });
680
+ bar.appendChild(s);
681
+ }
682
+ const legend = document.createElement("div");
683
+ legend.className = "chart-legend";
684
+ for (const seg of opts.segments) {
685
+ legend.appendChild(legendItem(seg.label, fmt(opts, seg.value), seg.tone));
686
+ }
687
+ body.append(bar, legend);
688
+ });
689
+ attachChartInteraction(el, "stack");
690
+ return el;
691
+ }
692
+ function renderDonut(el, opts) {
693
+ if (typeof document === "undefined") return el;
694
+ withChrome(el, opts, opts.segments.length > 0, (body) => {
695
+ const total = opts.segments.reduce((sum2, s) => sum2 + s.value, 0);
696
+ if (opts.segments.length === 0 || total <= 0) {
697
+ renderEmpty(body, opts.emptyLabel);
698
+ return;
699
+ }
700
+ const radius = 42;
701
+ const circumference = 2 * Math.PI * radius;
702
+ const wrap = document.createElement("div");
703
+ wrap.className = "chart-donut";
704
+ const svg2 = svgEl("svg", { viewBox: "0 0 120 120", role: "img" });
705
+ svg2.style.transform = "rotate(-90deg)";
706
+ svg2.appendChild(svgEl("circle", {
707
+ cx: "60",
708
+ cy: "60",
709
+ r: String(radius),
710
+ fill: "none",
711
+ stroke: "var(--token-line-soft)",
712
+ "stroke-width": "14"
713
+ }));
714
+ let offset = 0;
715
+ opts.segments.forEach((seg, segIndex) => {
716
+ const dash = seg.value / total * circumference;
717
+ const arc = svgEl("circle", {
718
+ cx: "60",
719
+ cy: "60",
720
+ r: String(radius),
721
+ fill: "none",
722
+ stroke: toneVar(seg.tone),
723
+ "stroke-width": "14",
724
+ "stroke-dasharray": `${dash} ${circumference - dash}`,
725
+ "stroke-dashoffset": String(-offset),
726
+ "stroke-linecap": "butt"
727
+ });
728
+ setMarkData(arc, { index: segIndex, label: seg.label, value: seg.value });
729
+ svg2.appendChild(arc);
730
+ offset += dash;
731
+ });
732
+ const legend = document.createElement("div");
733
+ legend.className = "chart-legend";
734
+ for (const seg of opts.segments) {
735
+ legend.appendChild(legendItem(seg.label, `${Math.round(seg.value / total * 100)}%`, seg.tone));
736
+ }
737
+ wrap.append(svg2, legend);
738
+ body.appendChild(wrap);
739
+ });
740
+ attachChartInteraction(el, "donut");
741
+ return el;
742
+ }
743
+ var lineGradientSeq = 0;
744
+ function renderLine(el, opts) {
745
+ if (typeof document === "undefined") return el;
746
+ const seriesList = normalizeSeriesInput(opts);
747
+ const labels = opts.labels ?? [];
748
+ const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
749
+ withChrome(el, opts, seriesList.length > 1, (body) => {
750
+ if (n === 0 || seriesList.length === 0) {
751
+ renderEmpty(body, opts.emptyLabel);
752
+ return;
753
+ }
754
+ const labelsUsed = labels.slice(0, n);
755
+ const width = 360;
756
+ const height = 150;
757
+ const pad = 14;
758
+ let max = 1;
759
+ for (const s of seriesList) max = Math.max(max, maxOf(s.values.slice(0, n)));
760
+ const wrap = document.createElement("div");
761
+ wrap.className = "chart-line";
762
+ const svg2 = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img" });
763
+ const defs = svgEl("defs", {});
764
+ for (let i = 0; i < 4; i++) {
765
+ const y = pad + i * (height - pad * 2) / 3;
766
+ svg2.appendChild(svgEl("line", {
767
+ x1: String(pad),
768
+ x2: String(width - pad),
769
+ y1: String(y),
770
+ y2: String(y),
771
+ stroke: "var(--token-line-soft)",
772
+ "stroke-width": "1"
773
+ }));
774
+ }
775
+ seriesList.forEach((s, si) => {
776
+ const tone = toneOf(s.tone, si) ?? toneVar(opts.tone);
777
+ const values = s.values.slice(0, n);
778
+ const points = values.map((value, i) => ({
779
+ i,
780
+ x: n <= 1 ? width / 2 : pad + i / (n - 1) * (width - pad * 2),
781
+ y: height - pad - Math.max(0, value) / max * (height - pad * 2),
782
+ value,
783
+ label: labelsUsed[i] ?? ""
784
+ }));
785
+ const line = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(" ");
786
+ const last = points[points.length - 1];
787
+ const first = points[0];
788
+ const fillArea = seriesList.length === 1 || opts.fill;
789
+ if (fillArea) {
790
+ const area = `${line} L${last.x.toFixed(1)},${height - pad} L${first.x.toFixed(1)},${height - pad} Z`;
791
+ const gradientId = `icen-chart-line-${++lineGradientSeq}`;
792
+ const gradient = svgEl("linearGradient", { id: gradientId, x1: "0", x2: "0", y1: "0", y2: "1" });
793
+ const strong = seriesList.length > 1 ? "0.20" : "0.32";
794
+ gradient.appendChild(svgEl("stop", { offset: "0%", "stop-color": tone, "stop-opacity": strong }));
795
+ gradient.appendChild(svgEl("stop", { offset: "100%", "stop-color": tone, "stop-opacity": "0.02" }));
796
+ defs.appendChild(gradient);
797
+ const areaPath = svgEl("path", { d: area, fill: `url(#${gradientId})` });
798
+ if (seriesList.length > 1) {
799
+ setMarkData(areaPath, { seriesIndex: si, seriesName: s.name, label: s.name, value: values.reduce((a, b) => a + b, 0) });
800
+ }
801
+ svg2.appendChild(areaPath);
802
+ }
803
+ const linePath = svgEl("path", {
804
+ d: line,
805
+ fill: "none",
806
+ stroke: tone,
807
+ "stroke-width": seriesList.length > 1 ? "2" : "2.5",
808
+ "stroke-linecap": "round",
809
+ "stroke-linejoin": "round"
810
+ });
811
+ const lineTitle = svgEl("title", {});
812
+ lineTitle.textContent = s.name || "\u6570\u503C";
813
+ linePath.appendChild(lineTitle);
814
+ setMarkData(linePath, { seriesIndex: si, seriesName: s.name, label: s.name, value: values[values.length - 1] ?? 0 });
815
+ svg2.appendChild(linePath);
816
+ for (const p of points) {
817
+ const dot = svgEl("circle", {
818
+ cx: p.x.toFixed(1),
819
+ cy: p.y.toFixed(1),
820
+ r: "3",
821
+ fill: "var(--token-card)",
822
+ stroke: tone,
823
+ "stroke-width": "2"
824
+ });
825
+ const title = svgEl("title", {});
826
+ title.textContent = seriesList.length > 1 ? `${p.label} \xB7 ${s.name}: ${fmt(opts, p.value)}` : `${p.label}: ${fmt(opts, p.value)}`;
827
+ dot.appendChild(title);
828
+ setMarkData(dot, {
829
+ index: p.i,
830
+ seriesIndex: seriesList.length > 1 ? si : void 0,
831
+ seriesName: seriesList.length > 1 ? s.name : void 0,
832
+ label: p.label,
833
+ value: p.value
834
+ });
835
+ svg2.appendChild(dot);
836
+ const hit = svgEl("circle", {
837
+ cx: p.x.toFixed(1),
838
+ cy: p.y.toFixed(1),
839
+ r: "8",
840
+ fill: "transparent",
841
+ "data-chart-hit": "1"
842
+ });
843
+ copyMarkData(hit, dot);
844
+ svg2.appendChild(hit);
845
+ }
846
+ });
847
+ if (defs.firstChild) svg2.insertBefore(defs, svg2.firstChild);
848
+ wrap.append(svg2, labelsRow(sampleLabels(labelsUsed)));
849
+ body.appendChild(wrap);
850
+ if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
851
+ });
852
+ attachChartInteraction(el, "line");
853
+ return el;
854
+ }
855
+ function renderRadar(el, opts) {
856
+ if (typeof document === "undefined") return el;
857
+ withChrome(el, opts, opts.series.length > 1, (body) => {
858
+ const axes = opts.axes;
859
+ const n = axes.length;
860
+ if (n < 3 || opts.series.length === 0) {
861
+ renderEmpty(body, opts.emptyLabel);
862
+ return;
863
+ }
864
+ let maxVal = opts.max ?? 0;
865
+ if (opts.max == null) {
866
+ for (const s of opts.series) {
867
+ for (let i = 0; i < n; i++) maxVal = Math.max(maxVal, s.values[i] ?? 0);
868
+ }
869
+ maxVal = niceCeil(maxVal);
870
+ }
871
+ if (maxVal <= 0) maxVal = 1;
872
+ const size = 200;
873
+ const cx = size / 2;
874
+ const cy = size / 2;
875
+ const radius = 78;
876
+ const levels = Math.max(2, opts.levels ?? 4);
877
+ const angleOf = (i) => -Math.PI / 2 + i * 2 * Math.PI / n;
878
+ const pointAt = (i, r) => ({
879
+ x: cx + r * Math.cos(angleOf(i)),
880
+ y: cy + r * Math.sin(angleOf(i))
881
+ });
882
+ const wrap = document.createElement("div");
883
+ wrap.className = "chart-radar";
884
+ const svg2 = svgEl("svg", { viewBox: `0 0 ${size} ${size}`, role: "img" });
885
+ for (let lv = 1; lv <= levels; lv++) {
886
+ const r = radius * lv / levels;
887
+ const pts = Array.from({ length: n }, (_, i) => {
888
+ const p = pointAt(i, r);
889
+ return `${p.x.toFixed(1)},${p.y.toFixed(1)}`;
890
+ }).join(" ");
891
+ svg2.appendChild(svgEl("polygon", {
892
+ points: pts,
893
+ fill: "none",
894
+ stroke: "var(--token-line-soft)",
895
+ "stroke-width": "1",
896
+ "stroke-dasharray": lv === levels ? "0" : "3 2"
897
+ }));
898
+ }
899
+ for (let i = 0; i < n; i++) {
900
+ const edge = pointAt(i, radius);
901
+ svg2.appendChild(svgEl("line", {
902
+ x1: String(cx),
903
+ y1: String(cy),
904
+ x2: edge.x.toFixed(1),
905
+ y2: edge.y.toFixed(1),
906
+ stroke: "var(--token-line-soft)",
907
+ "stroke-width": "1"
908
+ }));
909
+ const labelP = pointAt(i, radius + 14);
910
+ const txt = svgEl("text", {
911
+ x: labelP.x.toFixed(1),
912
+ y: labelP.y.toFixed(1),
913
+ "text-anchor": "middle",
914
+ "dominant-baseline": "middle",
915
+ "font-size": "9",
916
+ fill: "var(--token-text-muted)"
917
+ });
918
+ txt.textContent = axes[i] ?? "";
919
+ svg2.appendChild(txt);
920
+ }
921
+ const legendItems = [];
922
+ opts.series.forEach((s, si) => {
923
+ const tone = toneVar(s.tone);
924
+ const pts = Array.from({ length: n }, (_, i) => {
925
+ const v = Math.max(0, Math.min(maxVal, s.values[i] ?? 0));
926
+ const p = pointAt(i, v / maxVal * radius);
927
+ return `${p.x.toFixed(1)},${p.y.toFixed(1)}`;
928
+ }).join(" ");
929
+ const polyFill = svgEl("polygon", {
930
+ points: pts,
931
+ fill: tone,
932
+ "fill-opacity": "0.16",
933
+ stroke: tone,
934
+ "stroke-width": "2",
935
+ "stroke-linejoin": "round"
936
+ });
937
+ const title = svgEl("title", {});
938
+ title.textContent = `${s.name}: ${s.values.map((v) => fmt(opts, v)).join(" / ")}`;
939
+ polyFill.appendChild(title);
940
+ setMarkData(polyFill, { seriesIndex: si, seriesName: s.name || void 0, label: s.name, value: s.values.reduce((a, b) => a + (b || 0), 0) });
941
+ svg2.appendChild(polyFill);
942
+ for (let i = 0; i < n; i++) {
943
+ const v = Math.max(0, Math.min(maxVal, s.values[i] ?? 0));
944
+ const p = pointAt(i, v / maxVal * radius);
945
+ const dot = svgEl("circle", {
946
+ cx: p.x.toFixed(1),
947
+ cy: p.y.toFixed(1),
948
+ r: "2.5",
949
+ fill: "var(--token-card)",
950
+ stroke: tone,
951
+ "stroke-width": "1.5"
952
+ });
953
+ setMarkData(dot, { index: i, seriesIndex: si, seriesName: s.name || void 0, label: axes[i] ?? "", value: s.values[i] ?? 0 });
954
+ svg2.appendChild(dot);
955
+ }
956
+ legendItems.push({ key: s.name || `\u7CFB\u5217${si + 1}`, label: s.name || `\u7CFB\u5217${si + 1}`, tone: s.tone, si });
957
+ });
958
+ const legend = opts.series.length > 1 ? toggleLegend(legendItems, wrap) : null;
959
+ wrap.append(svg2, ...legend ? [legend] : []);
960
+ body.appendChild(wrap);
961
+ });
962
+ attachChartInteraction(el, "radar");
963
+ return el;
964
+ }
965
+ function niceCeil(v) {
966
+ if (v <= 0) return 1;
967
+ const exp = Math.floor(Math.log10(v));
968
+ const base = Math.pow(10, exp);
969
+ const frac = v / base;
970
+ let nice;
971
+ if (frac <= 1) nice = 1;
972
+ else if (frac <= 2) nice = 2;
973
+ else if (frac <= 5) nice = 5;
974
+ else nice = 10;
975
+ return nice * base;
976
+ }
977
+ function isoDate(d) {
978
+ const m = String(d.getMonth() + 1).padStart(2, "0");
979
+ const day = String(d.getDate()).padStart(2, "0");
980
+ return `${d.getFullYear()}-${m}-${day}`;
981
+ }
982
+ function weekdayLabelAt(defaults, day) {
983
+ return defaults[(day + 6) % 7] ?? "";
984
+ }
985
+ function levelOf(v, max) {
986
+ return v <= 0 ? 0 : Math.min(4, Math.ceil(v / max * 4));
987
+ }
988
+ function padFirstWeek(grid, firstDate, weekStart) {
989
+ const first = /* @__PURE__ */ new Date(`${firstDate}T00:00:00`);
990
+ const pad = (first.getDay() + 7 - weekStart) % 7;
991
+ for (let i = 0; i < pad; i++) {
992
+ const blank = document.createElement("span");
993
+ blank.className = "chart-heatmap-cell is-blank";
994
+ grid.appendChild(blank);
995
+ }
996
+ return pad;
997
+ }
998
+ function heatmapLegend(labels) {
999
+ const legend = document.createElement("div");
1000
+ legend.className = "chart-heatmap-legend";
1001
+ const less = document.createElement("span");
1002
+ less.textContent = labels?.less ?? "\u5C11";
1003
+ const more = document.createElement("span");
1004
+ more.textContent = labels?.more ?? "\u591A";
1005
+ legend.appendChild(less);
1006
+ for (let lv = 0; lv <= 4; lv++) {
1007
+ const cell = document.createElement("span");
1008
+ cell.className = "chart-heatmap-cell";
1009
+ cell.dataset.level = String(lv);
1010
+ legend.appendChild(cell);
1011
+ }
1012
+ legend.appendChild(more);
1013
+ return legend;
1014
+ }
1015
+ function renderHeatmap(el, opts) {
1016
+ if (typeof document === "undefined") return el;
1017
+ withChrome(el, opts, true, (body) => {
1018
+ let entries;
1019
+ if (opts.data && opts.data.length > 0) {
1020
+ entries = opts.data;
1021
+ } else if (opts.values && opts.values.length > 0) {
1022
+ const today = /* @__PURE__ */ new Date();
1023
+ entries = opts.values.map((value, i) => {
1024
+ const d = new Date(today);
1025
+ d.setDate(d.getDate() - (opts.values.length - 1 - i));
1026
+ return { date: isoDate(d), value };
1027
+ });
1028
+ } else {
1029
+ renderEmpty(body, opts.emptyLabel);
1030
+ return;
1031
+ }
1032
+ const weeks = opts.weeks ?? Math.ceil(entries.length / 7);
1033
+ if (entries.length > weeks * 7) entries = entries.slice(entries.length - weeks * 7);
1034
+ const max = maxOf(entries.map((e) => e.value));
1035
+ const wrap = document.createElement("div");
1036
+ wrap.className = "chart-heatmap";
1037
+ if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
1038
+ const grid = document.createElement("div");
1039
+ grid.className = "chart-heatmap-grid";
1040
+ grid.setAttribute("role", "img");
1041
+ padFirstWeek(grid, entries[0].date, opts.weekStart === 0 ? 0 : 1);
1042
+ for (const [i, entry] of entries.entries()) {
1043
+ const cell = document.createElement("span");
1044
+ cell.className = "chart-heatmap-cell";
1045
+ cell.dataset.level = String(levelOf(entry.value, max));
1046
+ cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
1047
+ setMarkData(cell, { index: i, label: entry.date, value: entry.value });
1048
+ grid.appendChild(cell);
1049
+ }
1050
+ wrap.append(grid, heatmapLegend(opts.labels));
1051
+ body.appendChild(wrap);
1052
+ });
1053
+ attachChartInteraction(el, "heatmap");
1054
+ return el;
1055
+ }
1056
+ function renderSparkline(el, opts) {
1057
+ if (typeof document === "undefined") return el;
1058
+ withChrome(el, opts, false, (body) => {
1059
+ const values = opts.values;
1060
+ if (values.length === 0) {
1061
+ renderEmpty(body, opts.emptyLabel);
1062
+ return;
1063
+ }
1064
+ const width = 96;
1065
+ const height = 28;
1066
+ const pad = 3;
1067
+ const max = maxOf(values);
1068
+ const tone = toneVar(opts.tone);
1069
+ const points = values.map((value, i) => ({
1070
+ x: values.length <= 1 ? width / 2 : pad + i / (values.length - 1) * (width - pad * 2),
1071
+ y: height - pad - value / max * (height - pad * 2),
1072
+ value
1073
+ }));
1074
+ const line = points.map((p, i) => `${i === 0 ? "M" : "L"}${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(" ");
1075
+ const last = points[points.length - 1];
1076
+ const first = points[0];
1077
+ const area = `${line} L${last.x.toFixed(1)},${height - pad} L${first.x.toFixed(1)},${height - pad} Z`;
1078
+ const gradientId = `icen-chart-line-${++lineGradientSeq}`;
1079
+ const wrap = document.createElement("div");
1080
+ wrap.className = "chart-sparkline";
1081
+ const svg2 = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img" });
1082
+ const defs = svgEl("defs", {});
1083
+ const gradient = svgEl("linearGradient", { id: gradientId, x1: "0", x2: "0", y1: "0", y2: "1" });
1084
+ gradient.appendChild(svgEl("stop", { offset: "0%", "stop-color": tone, "stop-opacity": "0.28" }));
1085
+ gradient.appendChild(svgEl("stop", { offset: "100%", "stop-color": tone, "stop-opacity": "0.02" }));
1086
+ defs.appendChild(gradient);
1087
+ svg2.appendChild(defs);
1088
+ svg2.appendChild(svgEl("path", { d: area, fill: `url(#${gradientId})` }));
1089
+ svg2.appendChild(svgEl("path", {
1090
+ d: line,
1091
+ fill: "none",
1092
+ stroke: tone,
1093
+ "stroke-width": "1.8",
1094
+ "stroke-linecap": "round",
1095
+ "stroke-linejoin": "round"
1096
+ }));
1097
+ const dot = svgEl("circle", {
1098
+ cx: last.x.toFixed(1),
1099
+ cy: last.y.toFixed(1),
1100
+ r: "2.4",
1101
+ fill: "var(--token-card)",
1102
+ stroke: tone,
1103
+ "stroke-width": "1.6"
1104
+ });
1105
+ const title = svgEl("title", {});
1106
+ title.textContent = fmt(opts, last.value);
1107
+ dot.appendChild(title);
1108
+ svg2.appendChild(dot);
1109
+ wrap.appendChild(svg2);
1110
+ body.appendChild(wrap);
1111
+ });
1112
+ attachChartInteraction(el, "sparkline");
1113
+ return el;
1114
+ }
1115
+ function renderGauge(el, opts) {
1116
+ if (typeof document === "undefined") return el;
1117
+ el.textContent = "";
1118
+ const max = opts.max ?? 100;
1119
+ const ratio = max > 0 ? Math.min(1, Math.max(0, opts.value / max)) : 0;
1120
+ const radius = 42;
1121
+ const circumference = 2 * Math.PI * radius;
1122
+ const wrap = document.createElement("div");
1123
+ wrap.className = "chart-gauge";
1124
+ if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
1125
+ const svg2 = svgEl("svg", { viewBox: "0 0 120 120", role: "img" });
1126
+ svg2.style.transform = "rotate(-90deg)";
1127
+ svg2.appendChild(svgEl("circle", {
1128
+ cx: "60",
1129
+ cy: "60",
1130
+ r: String(radius),
1131
+ fill: "none",
1132
+ stroke: "var(--token-line-soft)",
1133
+ "stroke-width": "10"
1134
+ }));
1135
+ const dash = ratio * circumference;
1136
+ svg2.appendChild(svgEl("circle", {
1137
+ cx: "60",
1138
+ cy: "60",
1139
+ r: String(radius),
1140
+ fill: "none",
1141
+ stroke: toneVar(opts.tone),
1142
+ "stroke-width": "10",
1143
+ "stroke-dasharray": `${dash} ${circumference - dash}`,
1144
+ "stroke-linecap": "round"
1145
+ }));
1146
+ const center = document.createElement("div");
1147
+ center.className = "chart-gauge-center";
1148
+ const val = document.createElement("span");
1149
+ val.className = "chart-gauge-value";
1150
+ val.textContent = opts.formatValue ? opts.formatValue(opts.value) : `${Math.round(ratio * 100)}%`;
1151
+ center.appendChild(val);
1152
+ if (opts.label) {
1153
+ const lab = document.createElement("span");
1154
+ lab.className = "chart-gauge-label";
1155
+ lab.textContent = opts.label;
1156
+ center.appendChild(lab);
1157
+ }
1158
+ wrap.append(svg2, center);
1159
+ el.appendChild(wrap);
1160
+ attachChartInteraction(el, "gauge");
1161
+ return el;
1162
+ }
1163
+ function renderCalendar(el, opts) {
1164
+ if (typeof document === "undefined") return el;
1165
+ withChrome(el, opts, true, (body) => {
1166
+ let entries;
1167
+ if (opts.data && opts.data.length > 0) {
1168
+ entries = opts.data;
1169
+ } else if (opts.dates && opts.dates.length > 0 && opts.values) {
1170
+ entries = opts.dates.slice(0, opts.values.length).map((date, i) => ({ date, value: opts.values[i] ?? 0 }));
1171
+ } else {
1172
+ renderEmpty(body, opts.emptyLabel);
1173
+ return;
1174
+ }
1175
+ const wrap = document.createElement("div");
1176
+ wrap.className = "chart-calendar";
1177
+ if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
1178
+ const weekStart = opts.weekStart === 0 ? 0 : 1;
1179
+ const weekdayDefaults = ["\u4E00", "", "\u4E09", "", "\u4E94", "", "\u65E5"];
1180
+ const weekdayLabels = opts.labels?.weekdays ?? weekdayDefaults.map((_, i) => weekdayLabelAt(weekdayDefaults, (weekStart + i) % 7));
1181
+ const monthLabel = (m) => opts.labels?.months?.[m] ?? `${m + 1}\u6708`;
1182
+ const grid = document.createElement("div");
1183
+ grid.className = "chart-heatmap-grid";
1184
+ grid.setAttribute("role", "img");
1185
+ const months = document.createElement("div");
1186
+ months.className = "chart-calendar-months";
1187
+ const weekdayRow = document.createElement("div");
1188
+ weekdayRow.className = "chart-calendar-weekdays";
1189
+ weekdayLabels.forEach((w) => {
1190
+ const s = document.createElement("span");
1191
+ s.textContent = w;
1192
+ weekdayRow.appendChild(s);
1193
+ });
1194
+ const max = maxOf(entries.map((e) => e.value));
1195
+ const pad = padFirstWeek(grid, entries[0].date, weekStart);
1196
+ let lastMonth = -1;
1197
+ entries.forEach((entry, i) => {
1198
+ const d = /* @__PURE__ */ new Date(`${entry.date}T00:00:00`);
1199
+ const col = Math.floor((pad + i) / 7);
1200
+ if (d.getMonth() !== lastMonth) {
1201
+ lastMonth = d.getMonth();
1202
+ const label = document.createElement("span");
1203
+ label.className = "chart-calendar-month";
1204
+ label.textContent = monthLabel(d.getMonth());
1205
+ label.style.setProperty("--col", String(col));
1206
+ months.appendChild(label);
1207
+ }
1208
+ const cell = document.createElement("span");
1209
+ cell.className = "chart-heatmap-cell";
1210
+ cell.dataset.level = String(levelOf(entry.value, max));
1211
+ cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
1212
+ setMarkData(cell, { index: i, label: entry.date, value: entry.value });
1213
+ grid.appendChild(cell);
1214
+ });
1215
+ const legend = heatmapLegend(opts.labels);
1216
+ const columns = Math.max(1, Math.ceil((pad + entries.length) / 7));
1217
+ months.style.setProperty("--columns", String(columns));
1218
+ wrap.append(months, weekdayRow, grid, legend);
1219
+ body.appendChild(wrap);
1220
+ });
1221
+ attachChartInteraction(el, "calendar");
1222
+ return el;
1223
+ }
1224
+ function renderScatter(el, opts) {
1225
+ if (typeof document === "undefined") return el;
1226
+ withChrome(el, opts, false, (body) => {
1227
+ const pts = opts.points.filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y));
1228
+ if (pts.length === 0) {
1229
+ renderEmpty(body, opts.emptyLabel);
1230
+ return;
1231
+ }
1232
+ const width = 360;
1233
+ const height = 220;
1234
+ const pad = 30;
1235
+ let xMin = Infinity, xMax = -Infinity, yMin = Infinity, yMax = -Infinity, sMax = 0;
1236
+ for (const p of pts) {
1237
+ if (p.x < xMin) xMin = p.x;
1238
+ if (p.x > xMax) xMax = p.x;
1239
+ if (p.y < yMin) yMin = p.y;
1240
+ if (p.y > yMax) yMax = p.y;
1241
+ if ((p.size ?? 0) > sMax) sMax = p.size ?? 0;
1242
+ }
1243
+ if (xMax === xMin) xMax = xMin + 1;
1244
+ if (yMax === yMin) yMax = yMin + 1;
1245
+ const rOf = (size) => size && sMax > 0 ? 3 + size / sMax * 7 : 4;
1246
+ const wrap = document.createElement("div");
1247
+ wrap.className = "chart-scatter";
1248
+ const tone = toneVar(opts.tone);
1249
+ const svg2 = svgEl("svg", { viewBox: `0 0 ${width} ${height}`, role: "img" });
1250
+ const xAt = (x) => pad + (x - xMin) / (xMax - xMin) * (width - pad * 2);
1251
+ const yAt = (y) => height - pad - (y - yMin) / (yMax - yMin) * (height - pad * 2);
1252
+ for (let i = 0; i <= 3; i++) {
1253
+ const y = pad + i * (height - pad * 2) / 3;
1254
+ svg2.appendChild(svgEl("line", {
1255
+ x1: String(pad),
1256
+ x2: String(width - pad),
1257
+ y1: String(y),
1258
+ y2: String(y),
1259
+ stroke: "var(--token-line-soft)",
1260
+ "stroke-width": "1"
1261
+ }));
1262
+ const tv = yMin + (yMax - yMin) * (3 - i) / 3;
1263
+ const txt = svgEl("text", {
1264
+ x: String(pad - 4),
1265
+ y: String(y + 3),
1266
+ "text-anchor": "end",
1267
+ "font-size": "8",
1268
+ fill: "var(--token-text-faint)"
1269
+ });
1270
+ txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
1271
+ svg2.appendChild(txt);
1272
+ }
1273
+ for (let i = 0; i <= 4; i++) {
1274
+ const x = pad + i * (width - pad * 2) / 4;
1275
+ svg2.appendChild(svgEl("line", {
1276
+ x1: String(x),
1277
+ x2: String(x),
1278
+ y1: String(pad),
1279
+ y2: String(height - pad),
1280
+ stroke: "var(--token-line-soft)",
1281
+ "stroke-width": "1",
1282
+ "stroke-dasharray": "2 3"
1283
+ }));
1284
+ const tv = xMin + (xMax - xMin) * i / 4;
1285
+ const txt = svgEl("text", {
1286
+ x: String(x),
1287
+ y: String(height - pad + 12),
1288
+ "text-anchor": "middle",
1289
+ "font-size": "8",
1290
+ fill: "var(--token-text-faint)"
1291
+ });
1292
+ txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
1293
+ svg2.appendChild(txt);
1294
+ }
1295
+ if (opts.yLabel) {
1296
+ const t = svgEl("text", { x: "10", y: String(pad - 10), "font-size": "8", fill: "var(--token-text-faint)" });
1297
+ t.textContent = opts.yLabel;
1298
+ svg2.appendChild(t);
1299
+ }
1300
+ if (opts.xLabel) {
1301
+ const t = svgEl("text", { x: String(width - pad), y: String(pad - 10), "text-anchor": "end", "font-size": "8", fill: "var(--token-text-faint)" });
1302
+ t.textContent = opts.xLabel;
1303
+ svg2.appendChild(t);
1304
+ }
1305
+ pts.forEach((p, i) => {
1306
+ const c = svgEl("circle", {
1307
+ cx: xAt(p.x).toFixed(1),
1308
+ cy: yAt(p.y).toFixed(1),
1309
+ r: rOf(p.size).toFixed(1),
1310
+ fill: tone,
1311
+ "fill-opacity": "0.55",
1312
+ stroke: tone,
1313
+ "stroke-width": "1"
1314
+ });
1315
+ const title = svgEl("title", {});
1316
+ title.textContent = `${p.label ?? `#${i + 1}`}: (${fmt(opts, p.x)}, ${fmt(opts, p.y)}${p.size != null ? `, size ${fmt(opts, p.size)}` : ""})`;
1317
+ c.appendChild(title);
1318
+ setMarkData(c, { index: i, label: p.label ?? `#${i + 1}`, value: p.y });
1319
+ c.dataset.chartX = String(p.x);
1320
+ svg2.appendChild(c);
1321
+ });
1322
+ wrap.appendChild(svg2);
1323
+ body.appendChild(wrap);
1324
+ });
1325
+ attachChartInteraction(el, "scatter");
1326
+ return el;
1327
+ }
1328
+ function chartFormatValue(format) {
1329
+ if (!format) return (v) => String(v);
1330
+ const notation = format.notation ?? "raw";
1331
+ return (v) => {
1332
+ let text;
1333
+ if (notation === "compact") {
1334
+ const abs = Math.abs(v);
1335
+ text = abs >= 1e6 ? `${Math.round(v / 1e5) / 10}M` : abs >= 1e3 ? `${Math.round(v / 100) / 10}k` : String(v);
1336
+ } else if (notation === "percent") {
1337
+ text = `${Math.round(v * 10) / 10}%`;
1338
+ } else if (notation === "ms") {
1339
+ if (v <= 0) text = "0ms";
1340
+ else if (v < 1e3) text = `${Math.round(v)}ms`;
1341
+ else if (v < 6e4) text = `${Math.round(v / 100) / 10}s`;
1342
+ else text = `${Math.floor(v / 6e4)}m`;
1343
+ } else {
1344
+ text = String(Math.round(v * 100) / 100);
1345
+ }
1346
+ return format.unit ? `${text} ${format.unit}` : text;
1347
+ };
1348
+ }
1349
+ function pivotData(spec) {
1350
+ const data = spec.data;
1351
+ if (!Array.isArray(data) || data.length === 0) return {};
1352
+ const d = spec.dims ?? {};
1353
+ const labelField = d.label ?? "label";
1354
+ const seriesField = d.series ?? "series";
1355
+ const valueField = d.value ?? "value";
1356
+ const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : Number(v) || 0;
1357
+ const str = (v) => typeof v === "string" ? v : v == null ? "" : String(v);
1358
+ if (d.x || data.some((r) => typeof r.x === "number")) {
1359
+ const xf = d.x ?? "x";
1360
+ const yf = d.y ?? "y";
1361
+ const sf = d.size ?? "size";
1362
+ const lf = d.label ?? "label";
1363
+ const points = data.filter((r) => r[xf] != null && r[yf] != null).map((r) => ({
1364
+ x: num(r[xf]),
1365
+ y: num(r[yf]),
1366
+ size: r[sf] != null ? num(r[sf]) : void 0,
1367
+ label: r[lf] != null ? str(r[lf]) : void 0
1368
+ }));
1369
+ return { points };
1370
+ }
1371
+ const labels = [];
1372
+ const seenLabel = /* @__PURE__ */ new Set();
1373
+ const hasSeries = data.some((r) => r[seriesField] != null && r[seriesField] !== "");
1374
+ const seriesNames = [];
1375
+ const seenSeries = /* @__PURE__ */ new Set();
1376
+ for (const r of data) {
1377
+ const l = str(r[labelField]);
1378
+ if (!seenLabel.has(l)) {
1379
+ seenLabel.add(l);
1380
+ labels.push(l);
1381
+ }
1382
+ if (hasSeries) {
1383
+ const sn = str(r[seriesField]);
1384
+ if (!seenSeries.has(sn)) {
1385
+ seenSeries.add(sn);
1386
+ seriesNames.push(sn);
1387
+ }
1388
+ }
1389
+ }
1390
+ const labelIndex = new Map(labels.map((l, i) => [l, i]));
1391
+ const seriesIndex = new Map(seriesNames.map((s, i) => [s, i]));
1392
+ const matrix = seriesNames.map(() => labels.map(() => NaN));
1393
+ for (const r of data) {
1394
+ const li = labelIndex.get(str(r[labelField])) ?? 0;
1395
+ const si = hasSeries ? seriesIndex.get(str(r[seriesField])) ?? 0 : 0;
1396
+ (matrix[si] ?? [])[li] = num(r[valueField]);
1397
+ }
1398
+ for (const row of matrix) for (let i = 0; i < row.length; i++) if (Number.isNaN(row[i])) row[i] = 0;
1399
+ if (hasSeries) {
1400
+ return {
1401
+ labels,
1402
+ series: seriesNames.map((name, si) => ({ name, values: matrix[si] ?? [] }))
1403
+ };
1404
+ }
1405
+ return { labels, values: matrix[0] ?? [] };
1406
+ }
1407
+ var CHART_TYPE_ALIASES = {
1408
+ bar: "vbar",
1409
+ column: "vbar",
1410
+ "vertical-bar": "vbar",
1411
+ "bar-vertical": "vbar",
1412
+ "horizontal-bar": "hbar",
1413
+ "bar-horizontal": "hbar",
1414
+ ranking: "hbar",
1415
+ pie: "donut",
1416
+ ring: "donut",
1417
+ "pie-chart": "donut",
1418
+ "stacked-bar": "stack",
1419
+ "stacked-bar-100": "stack",
1420
+ ratio: "stack",
1421
+ time: "line",
1422
+ timeline: "line",
1423
+ trend: "line",
1424
+ "trend-line": "line",
1425
+ contribution: "calendar",
1426
+ "contribution-graph": "calendar",
1427
+ "calendar-heatmap": "calendar",
1428
+ heat: "heatmap",
1429
+ matrix: "heatmap",
1430
+ bubble: "scatter",
1431
+ "scatter-plot": "scatter",
1432
+ dial: "gauge",
1433
+ progress: "gauge",
1434
+ mini: "sparkline",
1435
+ "trend-sparkline": "sparkline",
1436
+ spider: "radar",
1437
+ "radar-chart": "radar"
1438
+ };
1439
+ function toNum(v) {
1440
+ if (typeof v === "number" && Number.isFinite(v)) return v;
1441
+ if (typeof v === "string" && v.trim() !== "") {
1442
+ const n = Number(v.replace(/[,\s%]/g, ""));
1443
+ if (Number.isFinite(n)) return n;
1444
+ }
1445
+ return void 0;
1446
+ }
1447
+ function normalizeChartSpec(raw) {
1448
+ let src = raw;
1449
+ if (typeof src === "string") {
1450
+ try {
1451
+ src = JSON.parse(src);
1452
+ } catch {
1453
+ return {};
1454
+ }
1455
+ }
1456
+ if (!src || typeof src !== "object" || Array.isArray(src)) {
1457
+ if (Array.isArray(src)) src = { data: src };
1458
+ else return {};
1459
+ }
1460
+ const s = src;
1461
+ const spec = {};
1462
+ if (typeof s.type === "string") {
1463
+ const t = s.type.trim().toLowerCase();
1464
+ spec.type = CHART_TYPE_ALIASES[t] ?? t;
1465
+ }
1466
+ for (const k of ["title", "tone", "emptyLabel"]) {
1467
+ if (typeof s[k] === "string") spec[k] = s[k];
1468
+ }
1469
+ for (const k of ["stacked", "fill", "tooltip", "legend"]) {
1470
+ if (typeof s[k] === "boolean") spec[k] = s[k];
1471
+ }
1472
+ for (const k of ["weeks", "max", "levels"]) {
1473
+ const n = toNum(s[k]);
1474
+ if (n != null) spec[k] = n;
1475
+ }
1476
+ if (Array.isArray(s.labels)) spec.labels = s.labels.map((v) => String(v ?? ""));
1477
+ if (Array.isArray(s.values)) {
1478
+ const values = s.values.map(toNum).filter((v) => v != null);
1479
+ if (values.length > 0) spec.values = values;
1480
+ }
1481
+ if (Array.isArray(s.dates)) spec.dates = s.dates.map((v) => String(v ?? ""));
1482
+ if (Array.isArray(s.axes)) spec.axes = s.axes.map((v) => String(v ?? ""));
1483
+ if (Array.isArray(s.series)) {
1484
+ const series = s.series.filter((v) => !!v && typeof v === "object").map((v, i) => {
1485
+ const values = Array.isArray(v.values) ? v.values.map(toNum).filter((n) => n != null) : [];
1486
+ const sv = { name: String(v.name ?? `\u7CFB\u5217${i + 1}`), values };
1487
+ const tone = v.tone;
1488
+ if (typeof tone === "string" || typeof tone === "number") sv.tone = tone;
1489
+ return sv;
1490
+ }).filter((v) => v.values.length > 0);
1491
+ if (series.length > 0) spec.series = series;
1492
+ }
1493
+ if (Array.isArray(s.segments)) {
1494
+ const segments = s.segments.filter((v) => !!v && typeof v === "object").map((v) => {
1495
+ const seg = { label: String(v.label ?? v.name ?? ""), value: toNum(v.value) ?? 0 };
1496
+ if (typeof v.tone === "string") seg.tone = v.tone;
1497
+ return seg;
1498
+ }).filter((v) => v.label !== "" || v.value !== 0);
1499
+ if (segments.length > 0) spec.segments = segments;
1500
+ }
1501
+ if (Array.isArray(s.points)) {
1502
+ const points = s.points.filter((v) => !!v && typeof v === "object").map((v) => ({
1503
+ x: toNum(v.x) ?? 0,
1504
+ y: toNum(v.y) ?? 0,
1505
+ size: toNum(v.size),
1506
+ label: typeof v.label === "string" ? v.label : void 0
1507
+ }));
1508
+ if (points.length > 0) spec.points = points;
1509
+ }
1510
+ if (Array.isArray(s.data)) spec.data = s.data.filter((v) => !!v && typeof v === "object");
1511
+ if (s.dims && typeof s.dims === "object" && !Array.isArray(s.dims)) spec.dims = s.dims;
1512
+ if (s.format && typeof s.format === "object") {
1513
+ const f = s.format;
1514
+ const format = {};
1515
+ if (typeof f.unit === "string") format.unit = f.unit;
1516
+ if (typeof f.notation === "string" && ["compact", "percent", "ms", "raw"].includes(f.notation)) {
1517
+ format.notation = f.notation;
1518
+ }
1519
+ spec.format = format;
1520
+ }
1521
+ const isDateRecords = spec.data?.length && spec.data[0] && "date" in spec.data[0];
1522
+ if (spec.data && spec.data.length > 0 && !isDateRecords && !spec.series && !spec.points && !spec.values && !spec.segments) {
1523
+ Object.assign(spec, pivotData(spec));
1524
+ }
1525
+ return spec;
1526
+ }
1527
+ var TEMPORAL_HIT_RATIO = 0.6;
1528
+ function looksTemporal(labels) {
1529
+ if (labels.length === 0) return false;
1530
+ let hits = 0;
1531
+ for (const l of labels) {
1532
+ if (/^\d{4}-\d{2}(-\d{2})?$/.test(l)) hits++;
1533
+ else if (/^\d{4}\/\d{1,2}(\/\d{1,2})?$/.test(l)) hits++;
1534
+ else if (/^\d{4}年/.test(l)) hits++;
1535
+ else if (/^[Qq][1-4]$/.test(l)) hits++;
1536
+ else if (/^(周一|周二|周三|周四|周五|周六|周日|Mon|Tue|Wed|Thu|Fri|Sat|Sun)/i.test(l)) hits++;
1537
+ else if (/^第?\d+[日时周月]/.test(l)) hits++;
1538
+ else if (/^(v|ver\.?|version[ .]?)?\d+(\.\d+)+$/i.test(l)) hits++;
1539
+ else if (/^\d{4}$/.test(l)) hits++;
1540
+ }
1541
+ return hits / labels.length >= TEMPORAL_HIT_RATIO;
1542
+ }
1543
+ function inferChartType(spec) {
1544
+ if (spec.points && spec.points.length > 0) return "scatter";
1545
+ if (spec.dates && spec.dates.length > 0) return "calendar";
1546
+ if (spec.segments && spec.segments.length > 0) return "donut";
1547
+ if (spec.axes && spec.axes.length >= 3) return "radar";
1548
+ const labels = spec.labels ?? [];
1549
+ const multiSeries = (spec.series?.length ?? 0) > 1;
1550
+ if (labels.length > 0) {
1551
+ if (looksTemporal(labels)) return "line";
1552
+ if (multiSeries) return "vbar";
1553
+ return labels.length > 8 ? "hbar" : "vbar";
1554
+ }
1555
+ if (multiSeries) return "line";
1556
+ if (spec.values && spec.values.length > 1) return "line";
1557
+ if (typeof spec.max === "number" && spec.values && spec.values.length === 1) return "gauge";
1558
+ return "vbar";
1559
+ }
1560
+ function dispatchChartEvent(root, type, mark, chart, phase, pointer) {
1561
+ const read = (name) => mark.getAttribute(name) ?? void 0;
1562
+ const numOr = (v) => v == null ? void 0 : Number(v);
1563
+ const detail = {
1564
+ type,
1565
+ phase,
1566
+ chart,
1567
+ index: numOr(read("data-chart-index")),
1568
+ seriesIndex: numOr(read("data-chart-series")),
1569
+ seriesName: read("data-chart-series-name"),
1570
+ label: read("data-chart-label"),
1571
+ value: numOr(read("data-chart-value")),
1572
+ target: mark,
1573
+ pointerX: pointer?.x,
1574
+ pointerY: pointer?.y
1575
+ };
1576
+ emitIcen(root, `icen:chart-${type}`, detail);
1577
+ }
1578
+ var tooltipEl = null;
1579
+ function chartTooltip() {
1580
+ if (!tooltipEl) {
1581
+ tooltipEl = document.createElement("div");
1582
+ tooltipEl.className = "chart-tooltip";
1583
+ tooltipEl.setAttribute("role", "tooltip");
1584
+ tooltipEl.hidden = true;
1585
+ document.body.appendChild(tooltipEl);
1586
+ }
1587
+ return tooltipEl;
1588
+ }
1589
+ function applyTooltipSizing(host) {
1590
+ const tip = chartTooltip();
1591
+ const raw = host?.closest(".chart, [data-chart-root]")?.getAttribute("data-panel-max") ?? null;
1592
+ const n = raw != null ? Number(raw) : NaN;
1593
+ tip.style.maxWidth = Number.isFinite(n) && n > 0 ? `${n}px` : "";
1594
+ }
1595
+ function tooltipText(mark) {
1596
+ const label = mark.getAttribute("data-chart-label") ?? "";
1597
+ const series = mark.getAttribute("data-chart-series-name");
1598
+ const value = mark.getAttribute("data-chart-value");
1599
+ const head = series ? label ? `${series} \xB7 ${label}` : series : label;
1600
+ return value != null ? `${head}\uFF1A${value}` : head;
1601
+ }
1602
+ function moveTooltip(x, y) {
1603
+ const tip = chartTooltip();
1604
+ const pad = 12;
1605
+ const w = tip.offsetWidth || 120;
1606
+ const h2 = tip.offsetHeight || 28;
1607
+ let left = x + pad;
1608
+ let top = y - h2 - pad;
1609
+ if (left + w > window.innerWidth - 8) left = x - w - pad;
1610
+ if (top < 8) top = y + pad;
1611
+ tip.style.left = `${Math.round(left)}px`;
1612
+ tip.style.top = `${Math.round(top)}px`;
1613
+ }
1614
+ var chartDelegateMeta = /* @__PURE__ */ new WeakMap();
1615
+ var chartDelegateUnbind = /* @__PURE__ */ new WeakMap();
1616
+ function bindChartEvents(root) {
1617
+ const noop = () => void 0;
1618
+ if (typeof document === "undefined") return noop;
1619
+ const attached = root;
1620
+ if (attached.__icenChartBound) {
1621
+ const prev = chartDelegateUnbind.get(root);
1622
+ if (prev) return prev;
1623
+ }
1624
+ for (let p = root.parentElement; p; p = p.parentElement) {
1625
+ if (p.__icenChartBound) return noop;
1626
+ }
1627
+ const typeNow = () => chartDelegateMeta.get(root)?.type() ?? "vbar";
1628
+ const tipEnabled = () => chartDelegateMeta.get(root)?.tooltip() ?? true;
1629
+ const markOf = (e) => {
1630
+ const t = e.target;
1631
+ return t instanceof Element ? t.closest("[data-chart-mark]") : null;
1632
+ };
1633
+ let hoverMark = null;
1634
+ const enterMark = (mark, x, y) => {
1635
+ hoverMark = mark;
1636
+ mark.classList.add("is-hover");
1637
+ dispatchChartEvent(root, "hover", mark, typeNow(), "enter", { x, y });
1638
+ if (tipEnabled()) {
1639
+ const tip = chartTooltip();
1640
+ tip.textContent = tooltipText(mark);
1641
+ applyTooltipSizing(mark);
1642
+ tip.hidden = false;
1643
+ moveTooltip(x, y);
1644
+ }
1645
+ };
1646
+ const leaveMark = (pointer) => {
1647
+ if (!hoverMark) return;
1648
+ hoverMark.classList.remove("is-hover");
1649
+ dispatchChartEvent(root, "hover", hoverMark, typeNow(), "leave", pointer);
1650
+ hoverMark = null;
1651
+ chartTooltip().hidden = true;
1652
+ };
1653
+ const bindings = [];
1654
+ const on = (type, fn) => {
1655
+ const listener = (evt) => fn(evt);
1656
+ root.addEventListener(type, listener);
1657
+ bindings.push([type, listener]);
1658
+ };
1659
+ on("pointerover", (e) => {
1660
+ const mark = markOf(e);
1661
+ if (!mark || mark === hoverMark) return;
1662
+ leaveMark({ x: e.clientX, y: e.clientY });
1663
+ enterMark(mark, e.clientX, e.clientY);
1664
+ });
1665
+ on("pointermove", (e) => {
1666
+ if (hoverMark && tipEnabled()) {
1667
+ dispatchChartEvent(root, "hover", hoverMark, typeNow(), "move", { x: e.clientX, y: e.clientY });
1668
+ moveTooltip(e.clientX, e.clientY);
1669
+ }
1670
+ });
1671
+ on("pointerout", (e) => {
1672
+ const mark = markOf(e);
1673
+ if (mark && mark === hoverMark) leaveMark({ x: e.clientX, y: e.clientY });
1674
+ });
1675
+ on("pointerleave", () => leaveMark());
1676
+ on("click", (e) => {
1677
+ const mark = markOf(e);
1678
+ if (mark) dispatchChartEvent(root, "click", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
1679
+ });
1680
+ on("dblclick", (e) => {
1681
+ const mark = markOf(e);
1682
+ if (mark) dispatchChartEvent(root, "dblclick", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
1683
+ });
1684
+ on("contextmenu", (e) => {
1685
+ const mark = markOf(e);
1686
+ if (!mark) return;
1687
+ e.preventDefault();
1688
+ dispatchChartEvent(root, "contextmenu", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
1689
+ });
1690
+ const unbind = () => {
1691
+ for (const [type, listener] of bindings) root.removeEventListener(type, listener);
1692
+ bindings.length = 0;
1693
+ attached.__icenChartBound = false;
1694
+ chartDelegateUnbind.delete(root);
1695
+ if (hoverMark) {
1696
+ hoverMark.classList.remove("is-hover");
1697
+ hoverMark = null;
1698
+ }
1699
+ chartTooltip().hidden = true;
1700
+ };
1701
+ attached.__icenChartBound = true;
1702
+ chartDelegateUnbind.set(root, unbind);
1703
+ return unbind;
1704
+ }
1705
+ function attachChartInteraction(el, type) {
1706
+ chartDelegateMeta.set(el, { type: () => type, tooltip: () => true });
1707
+ bindChartEvents(el);
1708
+ }
1709
+ function hasLegendOf(spec) {
1710
+ switch (spec.type) {
1711
+ case "donut":
1712
+ case "stack":
1713
+ return (spec.segments?.length ?? 0) > 0;
1714
+ case "heatmap":
1715
+ case "calendar":
1716
+ return true;
1717
+ case "line":
1718
+ case "vbar":
1719
+ case "radar":
1720
+ return (spec.series?.length ?? 0) > 1;
1721
+ default:
1722
+ return false;
1723
+ }
1724
+ }
1725
+ function renderChart(el, raw) {
1726
+ const noop = () => void 0;
1727
+ if (typeof document === "undefined") {
1728
+ return { el, spec: {}, update: noop, on: () => noop, destroy: noop };
1729
+ }
1730
+ const root = el;
1731
+ let current = normalizeChartSpec(raw);
1732
+ const typeOf = () => current.type ?? "vbar";
1733
+ const legendState = { hidden: null };
1734
+ const render = () => {
1735
+ if (!current.type) current = { ...current, type: inferChartType(current) };
1736
+ const spec = current;
1737
+ const formatValue = chartFormatValue(spec.format);
1738
+ if (spec.legend !== void 0) legendState.hidden = spec.legend === false;
1739
+ withChrome(root, spec, hasLegendOf(spec), (body) => {
1740
+ switch (spec.type) {
1741
+ case "line":
1742
+ renderLine(body, { labels: spec.labels ?? [], values: spec.values ?? [], series: spec.series, tone: spec.tone, fill: spec.fill, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1743
+ break;
1744
+ case "area":
1745
+ renderLine(body, { labels: spec.labels ?? [], values: spec.values ?? [], series: spec.series, tone: spec.tone, fill: true, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1746
+ break;
1747
+ case "vbar":
1748
+ renderVBar(body, { labels: spec.labels ?? [], values: spec.values ?? [], series: spec.series, stacked: spec.stacked, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1749
+ break;
1750
+ case "hbar":
1751
+ renderHBar(body, { labels: spec.labels ?? [], values: spec.values ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1752
+ break;
1753
+ case "stack":
1754
+ renderStack(body, { segments: spec.segments ?? [], formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1755
+ break;
1756
+ case "donut":
1757
+ renderDonut(body, { segments: spec.segments ?? [], formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1758
+ break;
1759
+ case "radar": {
1760
+ const series = spec.series ? spec.series.map((s) => ({ name: s.name, values: s.values, tone: typeof s.tone === "string" ? s.tone : void 0 })) : spec.values ? [{ name: "", values: spec.values }] : [];
1761
+ renderRadar(body, { axes: spec.axes ?? spec.labels ?? [], series, max: spec.max, levels: spec.levels, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1762
+ break;
1763
+ }
1764
+ case "heatmap":
1765
+ renderHeatmap(body, { values: spec.values, data: spec.data, weeks: spec.weeks, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend, weekStart: spec.i18n?.weekStart, labels: spec.i18n });
1766
+ break;
1767
+ case "calendar":
1768
+ renderCalendar(body, { dates: spec.dates, values: spec.values, data: spec.data, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend, weekStart: spec.i18n?.weekStart, labels: spec.i18n });
1769
+ break;
1770
+ case "sparkline":
1771
+ renderSparkline(body, { labels: spec.labels ?? [], values: spec.values ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1772
+ break;
1773
+ case "gauge":
1774
+ renderGauge(body, { value: spec.values?.[0] ?? 0, max: spec.max, tone: spec.tone, label: spec.labels?.[0], formatValue });
1775
+ break;
1776
+ case "scatter":
1777
+ renderScatter(body, { points: spec.points ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1778
+ break;
1779
+ default:
1780
+ renderEmpty(body, spec.emptyLabel);
1781
+ }
1782
+ });
1783
+ };
1784
+ chartDelegateMeta.set(root, { type: typeOf, tooltip: () => current.tooltip !== false });
1785
+ const unbindEvents = bindChartEvents(root);
1786
+ render();
1787
+ return {
1788
+ el: root,
1789
+ spec: current,
1790
+ update(next) {
1791
+ current = normalizeChartSpec(next);
1792
+ render();
1793
+ },
1794
+ on(name, listener) {
1795
+ const handler = (event) => {
1796
+ listener(event.detail, event);
1797
+ };
1798
+ root.addEventListener(`icen:chart-${name}`, handler);
1799
+ return () => root.removeEventListener(`icen:chart-${name}`, handler);
1800
+ },
1801
+ destroy() {
1802
+ root.textContent = "";
1803
+ unbindEvents();
1804
+ chartTooltip().hidden = true;
1805
+ }
1806
+ };
1807
+ }
1808
+ var GRAPH_W = 360;
1809
+ var GRAPH_H = 260;
1810
+ var GRAPH_PAD = 28;
1811
+ var GRAPH_LW = GRAPH_W - GRAPH_PAD * 2;
1812
+ var GRAPH_LH = GRAPH_H - GRAPH_PAD * 2;
1813
+
1814
+ // src/behaviors/kb-answer.ts
1815
+ var ASSET_TYPE_LABELS = {
1816
+ "parameterized-query": "\u53C2\u6570\u5316\u67E5\u8BE2",
1817
+ function: "\u51FD\u6570",
1818
+ curated: "\u4EBA\u5DE5\u7B56\u5C55"
1819
+ };
1820
+ var ICON_TRUSTED = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.7 9a.6.6 0 0 1-.6 0C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.2-2.7a1.2 1.2 0 0 1 1.6 0C14.5 3.8 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/></svg>';
1821
+ var ICON_SHIELD = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.7 9a.6.6 0 0 1-.6 0C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.2-2.7a1.2 1.2 0 0 1 1.6 0C14.5 3.8 17 5 19 5a1 1 0 0 1 1 1z"/></svg>';
1822
+ function renderVerifiedBadge(via, opts) {
1823
+ const btn = h("button", "kb-verified");
1824
+ btn.type = "button";
1825
+ if (via.assetId) btn.dataset.assetId = via.assetId;
1826
+ const badge = h("span", "kb-badge kb-badge--trusted");
1827
+ const svg2 = svgIcon(ICON_TRUSTED);
1828
+ if (svg2) badge.appendChild(svg2);
1829
+ badge.appendChild(document.createTextNode("\u8BA4\u8BC1\u7B54\u6848"));
1830
+ btn.appendChild(badge);
1831
+ btn.appendChild(h("span", "kb-verified-via", `\u547D\u4E2D\u8D44\u4EA7\uFF1A${ASSET_TYPE_LABELS[via.assetType] ?? via.assetType}`));
1832
+ if (via.note) btn.appendChild(h("span", "kb-verified-note", via.note));
1833
+ btn.appendChild(h("span", "kb-verified-hint", "\u53C2\u6570\u53EF\u6539\u91CD\u8DD1"));
1834
+ btn.addEventListener("click", () => {
1835
+ emitIcen(btn, "icen:kb-verified-open", { assetId: via.assetId });
1836
+ opts?.onVerifiedOpen?.(via.assetId);
1837
+ });
1838
+ return btn;
1839
+ }
1840
+ function renderPermissionNotice(p) {
1841
+ const box = h("div", "kb-perm");
1842
+ const iconBox = h("span", "kb-perm-icon");
1843
+ const svg2 = svgIcon(ICON_SHIELD);
1844
+ if (svg2) iconBox.appendChild(svg2);
1845
+ box.appendChild(iconBox);
1846
+ box.appendChild(h("span", "kb-perm-text", p.rowLevelPolicy ? `\u5DF2\u6309\u4F60\u7684\u6570\u636E\u6743\u9650\u8FC7\u6EE4\uFF1A${p.rowLevelPolicy}` : "\u5DF2\u6309\u4F60\u7684\u6570\u636E\u6743\u9650\u8FC7\u6EE4"));
1847
+ if (p.maskedColumns?.length) {
1848
+ const mask = h("span", "kb-perm-mask");
1849
+ mask.appendChild(h("span", "kb-perm-mask-label", "\u5DF2\u8131\u654F\u5217\uFF1A"));
1850
+ for (const col of p.maskedColumns) mask.appendChild(h("span", "kb-chip", col));
1851
+ box.appendChild(mask);
1852
+ }
1853
+ return box;
1854
+ }
1855
+ function renderBody(text, citations) {
1856
+ const box = h("div", "kb-answer-body");
1857
+ for (const seg of parseInlineCitations(text)) {
1858
+ if (typeof seg.n === "number" && citations && seg.n >= 1 && seg.n <= citations.length) {
1859
+ const sup = h("sup", "kb-citation", `[${seg.n}]`);
1860
+ sup.dataset.cite = String(seg.n);
1861
+ const c = citations[seg.n - 1];
1862
+ if (c?.title) sup.title = c.title;
1863
+ box.appendChild(sup);
1864
+ } else {
1865
+ box.appendChild(document.createTextNode(seg.text));
1866
+ }
1867
+ }
1868
+ return box;
1869
+ }
1870
+ var NUMERIC_TYPES = /* @__PURE__ */ new Set([
1871
+ "number",
1872
+ "int",
1873
+ "integer",
1874
+ "float",
1875
+ "double",
1876
+ "decimal",
1877
+ "numeric",
1878
+ "bigint",
1879
+ "smallint",
1880
+ "real"
1881
+ ]);
1882
+ function isNumericColumn(col, colIndex, rows) {
1883
+ if (col.type) return NUMERIC_TYPES.has(col.type.toLowerCase());
1884
+ for (const row of rows) {
1885
+ const v = row[colIndex];
1886
+ if (v == null) continue;
1887
+ return typeof v === "number";
1888
+ }
1889
+ return false;
1890
+ }
1891
+ function renderResult(result, opts) {
1892
+ if (!result.columns.length) return null;
1893
+ const wrap = h("div", "kb-answer-results");
1894
+ const meta = h("div", "kb-result-meta");
1895
+ meta.appendChild(h("span", "kb-num", `${formatCount(result.rowCount)} \u884C`));
1896
+ if (result.durationMs != null) {
1897
+ meta.appendChild(h("span", "kb-num kb-result-dur", `${Math.round(result.durationMs)} ms`));
1898
+ }
1899
+ if (result.exportFormats?.length) {
1900
+ const exports = h("span", "kb-result-export");
1901
+ for (const fmt2 of result.exportFormats) {
1902
+ const btn = h("button", "kb-result-export-btn", `\u5BFC\u51FA ${fmt2}`);
1903
+ btn.type = "button";
1904
+ btn.dataset.format = fmt2;
1905
+ if (!opts?.onExport) btn.disabled = true;
1906
+ else btn.addEventListener("click", () => opts.onExport?.(fmt2));
1907
+ exports.appendChild(btn);
1908
+ }
1909
+ meta.appendChild(exports);
1910
+ }
1911
+ wrap.appendChild(meta);
1912
+ const scroll = h("div", "kb-result-scroll");
1913
+ const table = h("table", "kb-result-table");
1914
+ const numCols = result.columns.map((c, i) => isNumericColumn(c, i, result.rows));
1915
+ const thead = document.createElement("thead");
1916
+ const headRow = document.createElement("tr");
1917
+ result.columns.forEach((c, i) => {
1918
+ headRow.appendChild(h("th", numCols[i] ? "kb-num" : "", c.name));
1919
+ });
1920
+ thead.appendChild(headRow);
1921
+ table.appendChild(thead);
1922
+ const tbody = document.createElement("tbody");
1923
+ for (const row of result.rows) {
1924
+ const tr = document.createElement("tr");
1925
+ result.columns.forEach((_, i) => {
1926
+ const v = row[i];
1927
+ if (v == null) {
1928
+ const td = h("td", "kb-result-null", "\u2014");
1929
+ tr.appendChild(td);
1930
+ } else {
1931
+ tr.appendChild(h("td", typeof v === "number" ? "kb-num" : "", String(v)));
1932
+ }
1933
+ });
1934
+ tbody.appendChild(tr);
1935
+ }
1936
+ table.appendChild(tbody);
1937
+ scroll.appendChild(table);
1938
+ wrap.appendChild(scroll);
1939
+ if (result.truncated) {
1940
+ const total = result.totalRows != null ? formatCount(result.totalRows) : "\u2014";
1941
+ const limitPart = result.limit != null ? `\uFF08LIMIT ${formatCount(result.limit)}\uFF09` : "";
1942
+ wrap.appendChild(h("div", "kb-notice kb-result-truncated", `\u5DF2\u622A\u65AD\uFF1A\u663E\u793A ${formatCount(result.rowCount)}/${total} \u884C${limitPart}`));
1943
+ }
1944
+ return wrap;
1945
+ }
1946
+ var CHART_SWITCHES = [
1947
+ { type: "line", label: "\u6298\u7EBF" },
1948
+ { type: "vbar", label: "\u67F1\u72B6" },
1949
+ { type: "donut", label: "\u997C\u56FE" }
1950
+ ];
1951
+ function switchable(spec) {
1952
+ const hasCat = (spec.labels?.length ?? 0) > 0 && ((spec.values?.length ?? 0) > 0 || (spec.series?.length ?? 0) > 0);
1953
+ return hasCat || (spec.segments?.length ?? 0) > 0;
1954
+ }
1955
+ function specAsType(base, target) {
1956
+ if (target === "donut") {
1957
+ if (base.segments?.length) return { ...base, type: "donut" };
1958
+ const labels = base.labels ?? [];
1959
+ const values = base.values ?? [];
1960
+ return {
1961
+ ...base,
1962
+ type: "donut",
1963
+ segments: labels.map((label, i) => ({ label, value: values[i] ?? 0 }))
1964
+ };
1965
+ }
1966
+ if ((!base.labels?.length || !(base.values?.length || base.series?.length)) && base.segments?.length) {
1967
+ return {
1968
+ ...base,
1969
+ type: target,
1970
+ labels: base.segments.map((s) => s.label),
1971
+ values: base.segments.map((s) => s.value),
1972
+ segments: void 0
1973
+ };
1974
+ }
1975
+ return { ...base, type: target };
1976
+ }
1977
+ function renderChartArea(raw) {
1978
+ if (raw == null) return null;
1979
+ const norm = normalizeChartSpec(raw);
1980
+ const area = h("div", "kb-answer-chart");
1981
+ const mount = h("div", "chart kb-chart-mount");
1982
+ const handle = renderChart(mount, norm);
1983
+ if (switchable(handle.spec)) {
1984
+ const switchRow = h("div", "kb-chart-switch");
1985
+ const chips = /* @__PURE__ */ new Map();
1986
+ for (const s of CHART_SWITCHES) {
1987
+ const chip = h("button", "kb-chart-chip", s.label);
1988
+ chip.type = "button";
1989
+ chip.dataset.chartType = s.type;
1990
+ if (handle.spec.type === s.type) chip.classList.add("is-active");
1991
+ chip.addEventListener("click", () => {
1992
+ if (chip.classList.contains("is-active")) return;
1993
+ chips.forEach((c) => c.classList.remove("is-active"));
1994
+ chip.classList.add("is-active");
1995
+ handle.update(specAsType(handle.spec, s.type));
1996
+ });
1997
+ chips.set(s.type, chip);
1998
+ switchRow.appendChild(chip);
1999
+ }
2000
+ area.appendChild(switchRow);
2001
+ }
2002
+ area.appendChild(mount);
2003
+ return area;
2004
+ }
2005
+ function renderKbAnswer(el, model, opts) {
2006
+ if (typeof document === "undefined") return el;
2007
+ const m = model ?? {};
2008
+ el.textContent = "";
2009
+ const root = h("article", "kb-answer");
2010
+ root.dataset.kind = m.kind === "verified" ? "verified" : "generated";
2011
+ const badges = h("div", "kb-answer-badges");
2012
+ if (m.kind === "verified" && m.verifiedVia) badges.appendChild(renderVerifiedBadge(m.verifiedVia, opts));
2013
+ if (m.permissionNotice && (m.permissionNotice.rowLevelPolicy || m.permissionNotice.maskedColumns?.length)) {
2014
+ badges.appendChild(renderPermissionNotice(m.permissionNotice));
2015
+ }
2016
+ if (badges.children.length > 0) root.appendChild(badges);
2017
+ if (typeof m.body === "string" && m.body) root.appendChild(renderBody(m.body, m.citations));
2018
+ if (m.clarify && (m.clarify.question || m.clarify.options?.length)) {
2019
+ const slot = h("div", "kb-answer-clarify");
2020
+ renderKbClarify(slot, m.clarify);
2021
+ root.appendChild(slot);
2022
+ }
2023
+ if (m.explain) {
2024
+ const f = m.explain;
2025
+ const hasAny = f.tables?.length || f.columns?.length || f.filters?.length || f.aggregates?.length || f.metrics?.length;
2026
+ if (hasAny) {
2027
+ const slot = h("div", "kb-answer-explain");
2028
+ renderKbExplain(slot, f);
2029
+ root.appendChild(slot);
2030
+ }
2031
+ }
2032
+ if (m.result) {
2033
+ const resultBox = renderResult(normalizeQueryResult(m.result), opts);
2034
+ if (resultBox) root.appendChild(resultBox);
2035
+ }
2036
+ const chartArea = renderChartArea(m.chartSpec);
2037
+ if (chartArea) root.appendChild(chartArea);
2038
+ const followUps = (m.followUps ?? []).map((s) => String(s ?? "").trim()).filter(Boolean);
2039
+ if (followUps.length > 0) {
2040
+ const row = h("div", "kb-answer-followups");
2041
+ row.appendChild(h("span", "kb-followups-label", "\u8FFD\u95EE\u5EFA\u8BAE"));
2042
+ for (const text of followUps) {
2043
+ const chip = h("button", "kb-chip kb-followup", text);
2044
+ chip.type = "button";
2045
+ chip.addEventListener("click", () => opts?.onFollowUp?.(text));
2046
+ row.appendChild(chip);
2047
+ }
2048
+ root.appendChild(row);
2049
+ }
2050
+ el.appendChild(root);
2051
+ return el;
2052
+ }
2053
+ export {
2054
+ renderKbAnswer
2055
+ };