@icen.ai/ui 0.8.1 → 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 (141) hide show
  1. package/AGENTS.md +49 -6
  2. package/README.md +12 -9
  3. package/dist/behaviors/ai-branch.d.mts +19 -0
  4. package/dist/behaviors/ai-branch.mjs +217 -0
  5. package/dist/behaviors/ai-chat.d.mts +8 -6
  6. package/dist/behaviors/ai-chat.mjs +68 -8
  7. package/dist/behaviors/ai-composer.mjs +108 -20
  8. package/dist/behaviors/ai-feedback.d.mts +26 -0
  9. package/dist/behaviors/ai-feedback.mjs +303 -0
  10. package/dist/behaviors/ai-panel.d.mts +15 -1
  11. package/dist/behaviors/ai-panel.mjs +26 -0
  12. package/dist/behaviors/ai-threads.d.mts +38 -0
  13. package/dist/behaviors/ai-threads.mjs +319 -0
  14. package/dist/behaviors/ai-tool.mjs +5 -0
  15. package/dist/behaviors/ai-tools.mjs +5 -0
  16. package/dist/behaviors/charts.d.mts +103 -1
  17. package/dist/behaviors/charts.mjs +795 -0
  18. package/dist/behaviors/events.d.mts +193 -0
  19. package/dist/behaviors/kb-access.d.mts +34 -0
  20. package/dist/behaviors/kb-access.mjs +569 -0
  21. package/dist/behaviors/kb-acl.d.mts +50 -0
  22. package/dist/behaviors/kb-acl.mjs +405 -0
  23. package/dist/behaviors/kb-answer.d.mts +38 -0
  24. package/dist/behaviors/kb-answer.mjs +2055 -0
  25. package/dist/behaviors/kb-audit.d.mts +23 -0
  26. package/dist/behaviors/kb-audit.mjs +375 -0
  27. package/dist/behaviors/kb-canvas.d.mts +44 -0
  28. package/dist/behaviors/kb-canvas.mjs +589 -0
  29. package/dist/behaviors/kb-chain.d.mts +15 -0
  30. package/dist/behaviors/kb-chain.mjs +270 -0
  31. package/dist/behaviors/kb-checkpoint.d.mts +16 -0
  32. package/dist/behaviors/kb-checkpoint.mjs +342 -0
  33. package/dist/behaviors/kb-chunks.d.mts +31 -0
  34. package/dist/behaviors/kb-chunks.mjs +475 -0
  35. package/dist/behaviors/kb-citation.d.mts +38 -0
  36. package/dist/behaviors/kb-citation.mjs +677 -0
  37. package/dist/behaviors/kb-clarify.d.mts +20 -0
  38. package/dist/behaviors/kb-clarify.mjs +248 -0
  39. package/dist/behaviors/kb-conflict.d.mts +22 -0
  40. package/dist/behaviors/kb-conflict.mjs +284 -0
  41. package/dist/behaviors/kb-connector.d.mts +17 -0
  42. package/dist/behaviors/kb-connector.mjs +375 -0
  43. package/dist/behaviors/kb-core.d.mts +529 -0
  44. package/dist/behaviors/kb-core.mjs +890 -0
  45. package/dist/behaviors/kb-eval.d.mts +22 -0
  46. package/dist/behaviors/kb-eval.mjs +408 -0
  47. package/dist/behaviors/kb-explain.d.mts +11 -0
  48. package/dist/behaviors/kb-explain.mjs +243 -0
  49. package/dist/behaviors/kb-filter.d.mts +31 -0
  50. package/dist/behaviors/kb-filter.mjs +587 -0
  51. package/dist/behaviors/kb-gap.d.mts +27 -0
  52. package/dist/behaviors/kb-gap.mjs +273 -0
  53. package/dist/behaviors/kb-hittest.d.mts +34 -0
  54. package/dist/behaviors/kb-hittest.mjs +409 -0
  55. package/dist/behaviors/kb-metadata.d.mts +29 -0
  56. package/dist/behaviors/kb-metadata.mjs +326 -0
  57. package/dist/behaviors/kb-passage.d.mts +41 -0
  58. package/dist/behaviors/kb-passage.mjs +529 -0
  59. package/dist/behaviors/kb-pipeline.d.mts +18 -0
  60. package/dist/behaviors/kb-pipeline.mjs +381 -0
  61. package/dist/behaviors/kb-qa.d.mts +26 -0
  62. package/dist/behaviors/kb-qa.mjs +332 -0
  63. package/dist/behaviors/kb-rerank.d.mts +20 -0
  64. package/dist/behaviors/kb-rerank.mjs +315 -0
  65. package/dist/behaviors/kb-retrieval.d.mts +49 -0
  66. package/dist/behaviors/kb-retrieval.mjs +746 -0
  67. package/dist/behaviors/kb-review.d.mts +44 -0
  68. package/dist/behaviors/kb-review.mjs +451 -0
  69. package/dist/behaviors/kb-sandbox.d.mts +33 -0
  70. package/dist/behaviors/kb-sandbox.mjs +325 -0
  71. package/dist/behaviors/kb-segment.d.mts +32 -0
  72. package/dist/behaviors/kb-segment.mjs +499 -0
  73. package/dist/behaviors/kb-sources.d.mts +27 -0
  74. package/dist/behaviors/kb-sources.mjs +373 -0
  75. package/dist/behaviors/kb-sql.d.mts +43 -0
  76. package/dist/behaviors/kb-sql.mjs +948 -0
  77. package/dist/behaviors/kb-trace.d.mts +16 -0
  78. package/dist/behaviors/kb-trace.mjs +414 -0
  79. package/dist/behaviors/kb-visibility.d.mts +44 -0
  80. package/dist/behaviors/kb-visibility.mjs +403 -0
  81. package/dist/behaviors/kb-who-can.d.mts +32 -0
  82. package/dist/behaviors/kb-who-can.mjs +379 -0
  83. package/dist/components/ai-branch.mjs +3 -0
  84. package/dist/components/ai-chat.css +295 -0
  85. package/dist/components/ai-feedback.mjs +3 -0
  86. package/dist/components/ai-panel.css +217 -0
  87. package/dist/components/ai-threads.mjs +3 -0
  88. package/dist/components/chart-graph.css +57 -0
  89. package/dist/components/chart-graph.mjs +4 -0
  90. package/dist/components/chart-map.mjs +3 -0
  91. package/dist/components/chart-scatter.mjs +1 -1
  92. package/dist/components/charts.css +32 -0
  93. package/dist/components/charts.mjs +1 -0
  94. package/dist/components/kb-access.mjs +4 -0
  95. package/dist/components/kb-acl.mjs +4 -0
  96. package/dist/components/kb-agent.css +481 -0
  97. package/dist/components/kb-answer.mjs +4 -0
  98. package/dist/components/kb-audit.mjs +4 -0
  99. package/dist/components/kb-canvas.mjs +4 -0
  100. package/dist/components/kb-chain.mjs +4 -0
  101. package/dist/components/kb-checkpoint.mjs +4 -0
  102. package/dist/components/kb-chunks.mjs +4 -0
  103. package/dist/components/kb-citation.mjs +4 -0
  104. package/dist/components/kb-clarify.mjs +4 -0
  105. package/dist/components/kb-conflict.mjs +4 -0
  106. package/dist/components/kb-connector.mjs +4 -0
  107. package/dist/components/kb-data.css +649 -0
  108. package/dist/components/kb-eval.mjs +4 -0
  109. package/dist/components/kb-explain.mjs +4 -0
  110. package/dist/components/kb-filter.mjs +4 -0
  111. package/dist/components/kb-gap.mjs +4 -0
  112. package/dist/components/kb-ground.css +566 -0
  113. package/dist/components/kb-hittest.mjs +4 -0
  114. package/dist/components/kb-ingest.css +898 -0
  115. package/dist/components/kb-metadata.mjs +4 -0
  116. package/dist/components/kb-ops.css +548 -0
  117. package/dist/components/kb-passage.mjs +4 -0
  118. package/dist/components/kb-perm.css +444 -0
  119. package/dist/components/kb-pipeline.mjs +4 -0
  120. package/dist/components/kb-qa.mjs +4 -0
  121. package/dist/components/kb-rerank.mjs +4 -0
  122. package/dist/components/kb-retrieval.mjs +4 -0
  123. package/dist/components/kb-review.mjs +4 -0
  124. package/dist/components/kb-sandbox.mjs +4 -0
  125. package/dist/components/kb-search.css +940 -0
  126. package/dist/components/kb-segment.mjs +4 -0
  127. package/dist/components/kb-sources.mjs +4 -0
  128. package/dist/components/kb-sql.mjs +4 -0
  129. package/dist/components/kb-trace.mjs +4 -0
  130. package/dist/components/kb-visibility.mjs +4 -0
  131. package/dist/components/kb-who-can.mjs +4 -0
  132. package/dist/components/kb.css +252 -0
  133. package/dist/components/kb.mjs +9 -0
  134. package/dist/index.d.mts +37 -2
  135. package/dist/index.mjs +8555 -205
  136. package/dist/registry.json +325 -3
  137. package/dist/ui.css +5451 -45
  138. package/docs/research/2026-10-03-ai-kb-components.md +392 -0
  139. package/docs/research/2026-10-03-kb-permissions.md +992 -0
  140. package/docs/spec/kb-family.md +443 -0
  141. package/package.json +2 -2
@@ -0,0 +1,22 @@
1
+ import { KbEvalRow } from './kb-core.js';
2
+ import './ai-core.js';
3
+
4
+ /** 可选 run(下拉项) */
5
+ interface KbEvalRunRef {
6
+ id: string;
7
+ label: string;
8
+ }
9
+ /** renderKbEvalCompare 入参模型 */
10
+ interface KbEvalCompareModel {
11
+ runs: KbEvalRunRef[];
12
+ rows: KbEvalRow[];
13
+ /** 指标列名(缺省从 rows 的 scoresA/scoresB 键序并集推导) */
14
+ metrics?: string[];
15
+ }
16
+ /**
17
+ * 渲染评估 run 对比(快照:整体替换 el 内容)。返回挂载容器 el。
18
+ * 头部 runA/runB 下拉切换 → icen:kb-eval-compare { runA, runB }(消费方重取数据重渲)。
19
+ */
20
+ declare function renderKbEvalCompare(el: HTMLElement, model: KbEvalCompareModel): HTMLElement;
21
+
22
+ export { type KbEvalCompareModel, type KbEvalRunRef, renderKbEvalCompare };
@@ -0,0 +1,408 @@
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
+
134
+ // src/behaviors/kb-core.ts
135
+ 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>` });
136
+ var BUILTIN_SOURCE_TYPES = {
137
+ 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"/>'),
138
+ 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"/>'),
139
+ 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"/>'),
140
+ sheet: ic("\u8868\u683C", '<rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M3 15h18M9 3v18M15 3v18"/>'),
141
+ 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"/>'),
142
+ 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"/>'),
143
+ code: ic("\u4EE3\u7801", '<path d="m16 18 6-6-6-6"/><path d="m8 6-6 6 6 6"/>'),
144
+ 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"/>'),
145
+ 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"/>'),
146
+ mail: ic("\u90AE\u4EF6", '<rect x="2" y="4" width="20" height="16" rx="2"/><path d="m22 7-10 7L2 7"/>')
147
+ };
148
+ var sourceTypeRegistry = new Map(Object.entries(BUILTIN_SOURCE_TYPES));
149
+ function formatScore(v, digits = 3) {
150
+ if (v == null || !Number.isFinite(v)) return "\u2014";
151
+ return v.toFixed(digits).replace(/\.?0+$/, (s) => s.includes(".") ? "" : s);
152
+ }
153
+ function evalGrade(scoreDelta, costDeltaPct, latencyDeltaPct) {
154
+ if (scoreDelta == null || Math.abs(scoreDelta) < 1e-9) {
155
+ if (scoreDelta == null) return "tie";
156
+ const worse = costDeltaPct != null && costDeltaPct > 5 || latencyDeltaPct != null && latencyDeltaPct > 15;
157
+ return worse ? "tradeoff" : "tie";
158
+ }
159
+ if (scoreDelta > 0) {
160
+ const worse = costDeltaPct != null && costDeltaPct > 5 || latencyDeltaPct != null && latencyDeltaPct > 15;
161
+ return worse ? "tradeoff" : "improvement";
162
+ }
163
+ return "regression";
164
+ }
165
+ function meanScore(scores) {
166
+ if (!scores) return null;
167
+ const vals = Object.values(scores).filter((v) => typeof v === "number");
168
+ return vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : null;
169
+ }
170
+ function deltaLabel(v, digits = 1) {
171
+ if (v == null || !Number.isFinite(v)) return "\u2014";
172
+ const s = v.toFixed(digits);
173
+ return v > 0 ? `+${s}` : s;
174
+ }
175
+
176
+ // src/behaviors/events.ts
177
+ function emitIcen(source, type, detail) {
178
+ return source.dispatchEvent(
179
+ new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
180
+ );
181
+ }
182
+
183
+ // src/behaviors/kb-eval.ts
184
+ var GRADE_LABELS = {
185
+ improvement: "\u63D0\u5347",
186
+ regression: "\u56DE\u5F52",
187
+ tradeoff: "\u6743\u8861",
188
+ tie: "\u6301\u5E73"
189
+ };
190
+ function deriveMetrics(rows) {
191
+ const seen = [];
192
+ for (const r of rows) {
193
+ for (const k of Object.keys(r.scoresA ?? {})) if (!seen.includes(k)) seen.push(k);
194
+ for (const k of Object.keys(r.scoresB ?? {})) if (!seen.includes(k)) seen.push(k);
195
+ }
196
+ return seen;
197
+ }
198
+ function overallMean(rows, key) {
199
+ const vals = rows.map((r) => meanScore(r[key])).filter((v) => v != null);
200
+ return vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : null;
201
+ }
202
+ function rowIsRegression(row) {
203
+ const ma = meanScore(row.scoresA);
204
+ const mb = meanScore(row.scoresB);
205
+ if (ma != null && mb != null) return mb < ma;
206
+ const keys = /* @__PURE__ */ new Set([...Object.keys(row.scoresA ?? {}), ...Object.keys(row.scoresB ?? {})]);
207
+ for (const k of keys) {
208
+ const a = row.scoresA?.[k];
209
+ const b = row.scoresB?.[k];
210
+ if (typeof a === "number" && typeof b === "number" && b < a) return true;
211
+ }
212
+ return false;
213
+ }
214
+ function diffInline(a, b) {
215
+ const min = Math.min(a.length, b.length);
216
+ let start = 0;
217
+ while (start < min && a.charCodeAt(start) === b.charCodeAt(start)) start++;
218
+ let endA = a.length;
219
+ let endB = b.length;
220
+ while (endA > start && endB > start && a.charCodeAt(endA - 1) === b.charCodeAt(endB - 1)) {
221
+ endA--;
222
+ endB--;
223
+ }
224
+ return { prefix: a.slice(0, start), aMid: a.slice(start, endA), bMid: b.slice(start, endB), suffix: a.slice(endA) };
225
+ }
226
+ function appendDiffText(pre, prefix, mid, midClass, suffix) {
227
+ if (prefix) pre.append(h("span", void 0, prefix));
228
+ if (mid) pre.append(h("span", midClass, mid));
229
+ if (suffix) pre.append(h("span", void 0, suffix));
230
+ }
231
+ function evalGridTemplate(metrics) {
232
+ const cols = ["minmax(140px, 1.2fr)", "minmax(140px, 1fr)", "minmax(140px, 1fr)", "minmax(80px, 0.7fr)"];
233
+ for (let i = 0; i < metrics.length; i++) cols.push("44px", "44px", "64px");
234
+ return cols.join(" ");
235
+ }
236
+ function labelOf(runs, id) {
237
+ return runs.find((r) => r.id === id)?.label ?? id;
238
+ }
239
+ function buildRunSelect(runs, selected, tag) {
240
+ const sel = h("select", "kb-eval-run");
241
+ sel.dataset.run = tag;
242
+ if (!runs.length) {
243
+ const o = h("option", void 0, "\u2014");
244
+ o.value = "";
245
+ sel.append(o);
246
+ }
247
+ for (const r of runs) {
248
+ const o = h("option", void 0, r.label || r.id);
249
+ o.value = r.id;
250
+ sel.append(o);
251
+ }
252
+ sel.value = selected;
253
+ return sel;
254
+ }
255
+ function truncCell(text) {
256
+ const c = h("span", "kb-eval-cell", text);
257
+ c.title = text;
258
+ return c;
259
+ }
260
+ function deltaCell(d, digits) {
261
+ const cls = d == null ? "" : d > 0 ? " is-up" : d < 0 ? " is-down" : "";
262
+ return h("span", `kb-num kb-eval-num kb-eval-delta${cls}`, d != null ? deltaLabel(d, digits) : "\u2014");
263
+ }
264
+ function renderKbEvalCompare(el, model) {
265
+ if (typeof document === "undefined") return el;
266
+ const metrics = model.metrics && model.metrics.length ? model.metrics.slice() : deriveMetrics(model.rows);
267
+ const runs = model.runs;
268
+ const runA = runs[0]?.id ?? "";
269
+ const runB = runs[1]?.id ?? runs[0]?.id ?? "";
270
+ el.className = "kb-eval";
271
+ el.replaceChildren();
272
+ const bar = h("div", "kb-eval-bar");
273
+ bar.append(h("span", void 0, "\u5BF9\u6BD4"));
274
+ const selA = buildRunSelect(runs, runA, "a");
275
+ const selB = buildRunSelect(runs, runB, "b");
276
+ const emitCompare = () => {
277
+ emitIcen(el, "icen:kb-eval-compare", { runA: selA.value, runB: selB.value });
278
+ };
279
+ selA.addEventListener("change", emitCompare);
280
+ selB.addEventListener("change", emitCompare);
281
+ bar.append(selA, h("span", void 0, "vs"), selB);
282
+ el.append(bar);
283
+ if (!model.rows.length) {
284
+ el.append(h("div", "kb-empty", "\u6CA1\u6709\u5BF9\u6BD4\u6570\u636E"));
285
+ return el;
286
+ }
287
+ const meanA = overallMean(model.rows, "scoresA");
288
+ const meanB = overallMean(model.rows, "scoresB");
289
+ const delta = meanA != null && meanB != null ? meanB - meanA : null;
290
+ const grade = evalGrade(delta);
291
+ const summary = h("div", "kb-eval-summary");
292
+ summary.append(h("span", `kb-eval-grade is-${grade}`, GRADE_LABELS[grade]));
293
+ summary.append(h("span", "kb-num", `A \u5747\u5206 ${formatScore(meanA)}`));
294
+ summary.append(h("span", "kb-meta", "\u2192"));
295
+ summary.append(h("span", "kb-num", `B \u5747\u5206 ${formatScore(meanB)}`));
296
+ const ppCell = deltaCell(delta != null ? delta * 100 : null, 1);
297
+ if (delta != null) ppCell.append("pp");
298
+ summary.append(ppCell);
299
+ summary.append(h("span", "kb-meta", `${model.rows.length} \u884C`));
300
+ el.append(summary);
301
+ const wrap = h("div", "kb-eval-tablewrap");
302
+ const table = h("div", "kb-eval-table");
303
+ const template = evalGridTemplate(metrics);
304
+ const head1 = h("div", "kb-eval-grid kb-eval-grid--head");
305
+ head1.style.gridTemplateColumns = template;
306
+ head1.append(
307
+ h("span", "kb-eval-h", "\u8F93\u5165"),
308
+ h("span", "kb-eval-h", `\u8F93\u51FA ${labelOf(runs, runA)}`),
309
+ h("span", "kb-eval-h", `\u8F93\u51FA ${labelOf(runs, runB)}`),
310
+ h("span", "kb-eval-h", "\u671F\u671B")
311
+ );
312
+ for (const m of metrics) {
313
+ const g = h("span", "kb-eval-h kb-eval-h--group", m);
314
+ g.style.gridColumn = "span 3";
315
+ head1.append(g);
316
+ }
317
+ const head2 = h("div", "kb-eval-grid kb-eval-grid--head");
318
+ head2.style.gridTemplateColumns = template;
319
+ for (let i = 0; i < 4; i++) head2.append(h("span", "kb-eval-h"));
320
+ for (let i = 0; i < metrics.length; i++) {
321
+ head2.append(
322
+ h("span", "kb-eval-h kb-eval-h--sub", "A"),
323
+ h("span", "kb-eval-h kb-eval-h--sub", "B"),
324
+ h("span", "kb-eval-h kb-eval-h--sub", "\u0394")
325
+ );
326
+ }
327
+ table.append(head1, head2);
328
+ const items = [];
329
+ let filterOn = false;
330
+ let hasRegression = false;
331
+ for (const row of model.rows) {
332
+ const regression = rowIsRegression(row);
333
+ hasRegression = hasRegression || regression;
334
+ const item = h("div", "kb-eval-item");
335
+ const line = h("div", "kb-eval-grid kb-eval-rowline");
336
+ line.style.gridTemplateColumns = template;
337
+ line.tabIndex = 0;
338
+ line.setAttribute("role", "button");
339
+ line.setAttribute("aria-expanded", "false");
340
+ const inputCell = h("span", "kb-eval-cell kb-eval-cell--input");
341
+ inputCell.append(h("span", "kb-eval-key", row.key));
342
+ const inputText = h("span", void 0, row.input);
343
+ inputText.title = row.input;
344
+ inputCell.append(inputText);
345
+ line.append(inputCell, truncCell(row.outputA ?? "\u2014"), truncCell(row.outputB ?? "\u2014"), truncCell(row.expected ?? "\u2014"));
346
+ for (const m of metrics) {
347
+ const a = row.scoresA?.[m] ?? null;
348
+ const b = row.scoresB?.[m] ?? null;
349
+ line.append(
350
+ h("span", "kb-num kb-eval-num", formatScore(a)),
351
+ h("span", "kb-num kb-eval-num", formatScore(b)),
352
+ deltaCell(typeof a === "number" && typeof b === "number" ? b - a : null, 2)
353
+ );
354
+ }
355
+ const detail = h("div", "kb-eval-detail");
356
+ detail.hidden = true;
357
+ const diff = diffInline(row.outputA ?? "", row.outputB ?? "");
358
+ const colA = h("div", "kb-eval-detail-col");
359
+ colA.append(h("div", "kb-eval-detail-label", "\u8F93\u51FA A"));
360
+ const preA = h("pre", "kb-eval-detail-pre");
361
+ appendDiffText(preA, diff.prefix, diff.aMid, "kb-eval-diff-del", diff.suffix);
362
+ colA.append(preA);
363
+ const colB = h("div", "kb-eval-detail-col");
364
+ colB.append(h("div", "kb-eval-detail-label", "\u8F93\u51FA B"));
365
+ const preB = h("pre", "kb-eval-detail-pre");
366
+ appendDiffText(preB, diff.prefix, diff.bMid, "kb-eval-diff-ins", diff.suffix);
367
+ colB.append(preB);
368
+ detail.append(colA, colB);
369
+ if (row.expected) {
370
+ const exp = h("div", "kb-eval-detail-expected");
371
+ exp.append(h("span", "kb-eval-detail-label", "\u671F\u671B"));
372
+ exp.append(h("div", "kb-eval-detail-pre", row.expected));
373
+ detail.append(exp);
374
+ }
375
+ const toggle = () => {
376
+ const open = line.getAttribute("aria-expanded") !== "true";
377
+ line.setAttribute("aria-expanded", String(open));
378
+ detail.hidden = !open;
379
+ };
380
+ line.addEventListener("click", toggle);
381
+ line.addEventListener("keydown", (e) => {
382
+ if (e.key !== "Enter" && e.key !== " ") return;
383
+ e.preventDefault();
384
+ toggle();
385
+ });
386
+ item.append(line, detail);
387
+ items.push({ item, regression });
388
+ table.append(item);
389
+ }
390
+ wrap.append(table);
391
+ el.append(wrap);
392
+ if (hasRegression) {
393
+ const btn = h("button", "kb-eval-filter", "\u4EC5\u770B\u56DE\u5F52");
394
+ btn.type = "button";
395
+ btn.setAttribute("aria-pressed", "false");
396
+ btn.addEventListener("click", () => {
397
+ filterOn = !filterOn;
398
+ btn.setAttribute("aria-pressed", String(filterOn));
399
+ btn.classList.toggle("is-on", filterOn);
400
+ for (const it of items) it.item.hidden = filterOn && !it.regression;
401
+ });
402
+ bar.append(btn);
403
+ }
404
+ return el;
405
+ }
406
+ export {
407
+ renderKbEvalCompare
408
+ };
@@ -0,0 +1,11 @@
1
+ import { KbExplainFact } from './kb-core.js';
2
+ import './ai-core.js';
3
+
4
+ /**
5
+ * 渲染口径解释折叠面板(快照式:重复调用整段重建,默认折叠),返回挂载元素 el
6
+ * (render* 约定;SSR 原样返回)。展开体:表/列/筛选/聚合四组 .kb-chip(有才渲)
7
+ * + 指标口径行(name + ref 深链)。切换派 icen:kb-explain-toggle {el, open}。
8
+ */
9
+ declare function renderKbExplain(el: HTMLElement, fact: KbExplainFact | Partial<KbExplainFact>): HTMLElement;
10
+
11
+ export { renderKbExplain };