@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,405 @@
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 KB_ROLES = ["viewer", "commenter", "editor", "owner"];
182
+ function kbRoleLabel(r) {
183
+ return r === "viewer" ? "\u53EF\u67E5\u770B" : r === "commenter" ? "\u53EF\u8BC4\u8BBA" : r === "editor" ? "\u53EF\u7F16\u8F91" : r === "owner" ? "\u8D1F\u8D23\u4EBA" : "\u65E0\u6743\u9650";
184
+ }
185
+ function normalizeAclEntry(raw) {
186
+ const r = raw;
187
+ const s = r.subject ?? {};
188
+ const kind = s.kind === "user" || s.kind === "group" || s.kind === "org" || s.kind === "anyone" || s.kind === "link" ? s.kind : "user";
189
+ return {
190
+ subject: { kind, id: String(s.id ?? ""), label: String(s.label ?? "\u672A\u547D\u540D\u4E3B\u4F53"), note: typeof s.note === "string" ? s.note : void 0 },
191
+ role: KB_ROLES.includes(r.role) ? r.role : "viewer",
192
+ inheritedFrom: typeof r.inheritedFrom === "string" && r.inheritedFrom ? r.inheritedFrom : void 0,
193
+ expiresAt: typeof r.expiresAt === "string" && r.expiresAt ? r.expiresAt : void 0,
194
+ deny: r.deny === true,
195
+ system: r.system === true
196
+ };
197
+ }
198
+ function formatCount(n) {
199
+ if (n == null || !Number.isFinite(n)) return "\u2014";
200
+ if (Math.abs(n) >= 1e4) return `${(n / 1e3).toFixed(1)}k`;
201
+ return n.toLocaleString("en-US");
202
+ }
203
+ function relativeTime(input, now = /* @__PURE__ */ new Date()) {
204
+ const t = input instanceof Date ? input.getTime() : typeof input === "number" ? input : Date.parse(input);
205
+ if (!Number.isFinite(t)) return typeof input === "string" ? input : "\u2014";
206
+ const diff = now.getTime() - t;
207
+ const MIN = 6e4, HOUR = 36e5, DAY = 864e5;
208
+ if (diff < MIN) return "\u521A\u521A";
209
+ if (diff < HOUR) return `${Math.floor(diff / MIN)} \u5206\u949F\u524D`;
210
+ if (diff < DAY) return `${Math.floor(diff / HOUR)} \u5C0F\u65F6\u524D`;
211
+ if (diff < 30 * DAY) return `${Math.floor(diff / DAY)} \u5929\u524D`;
212
+ const d = new Date(t);
213
+ const ymd = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
214
+ return ymd;
215
+ }
216
+
217
+ // src/behaviors/events.ts
218
+ function emitIcen(source, type, detail) {
219
+ return source.dispatchEvent(
220
+ new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
221
+ );
222
+ }
223
+
224
+ // src/behaviors/kb-citation.ts
225
+ var KB_ICON_LOCK = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>';
226
+
227
+ // src/behaviors/kb-acl.ts
228
+ function normalizeAclModel(raw) {
229
+ const r = raw;
230
+ const ex = r.exceptions;
231
+ return {
232
+ resource: String(r.resource ?? "\u672A\u547D\u540D\u8D44\u6E90"),
233
+ parentChain: Array.isArray(r.parentChain) ? r.parentChain.map(String).filter(Boolean) : [],
234
+ broken: r.broken === true,
235
+ exceptions: ex && typeof ex === "object" ? { label: String(ex.label ?? "\u4F8B\u5916"), count: Number.isFinite(ex.count) ? ex.count : 0 } : null,
236
+ entries: Array.isArray(r.entries) ? r.entries.map((e) => normalizeAclEntry(e)) : [],
237
+ links: Array.isArray(r.links) ? r.links.map((e) => normalizeAclEntry(e)) : [],
238
+ exposureCount: typeof r.exposureCount === "number" && Number.isFinite(r.exposureCount) ? r.exposureCount : null
239
+ };
240
+ }
241
+ var aclPanelMap = /* @__PURE__ */ new WeakMap();
242
+ var aclRowMap = /* @__PURE__ */ new WeakMap();
243
+ var DAY_MS = 864e5;
244
+ var EXPIRING_SOON_MS = 14 * DAY_MS;
245
+ function ymdOf(t) {
246
+ const d = new Date(t);
247
+ return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
248
+ }
249
+ function expiryMeta(expiresAt, now) {
250
+ if (!expiresAt) return { text: "", soon: false };
251
+ const t = Date.parse(expiresAt);
252
+ if (!Number.isFinite(t)) return { text: "", soon: false };
253
+ if (t <= now.getTime()) return { text: `\u5DF2\u8FC7\u671F\uFF08${relativeTime(t, now)}\uFF09`, soon: false };
254
+ return { text: `${ymdOf(t)} \u5230\u671F`, soon: t - now.getTime() <= EXPIRING_SOON_MS };
255
+ }
256
+ function denyFirst(entries) {
257
+ return [...entries].sort((a, b) => Number(b.deny === true) - Number(a.deny === true));
258
+ }
259
+ function buildAclRow(entry, now) {
260
+ const row = h("div", "kb-acl-grant");
261
+ if (entry.deny) row.classList.add("is-deny");
262
+ if (entry.system) row.classList.add("is-system");
263
+ aclRowMap.set(row, entry);
264
+ const subject = h("span", "kb-acl-subject");
265
+ subject.dataset.kind = entry.subject.kind;
266
+ subject.appendChild(h("span", "kb-acl-subject-label", entry.subject.label));
267
+ const note = entry.system ? "\u7CFB\u7EDF\u81EA\u52A8\uFF0C\u4E0D\u53EF\u624B\u5DE5\u79FB\u9664" : entry.subject.note;
268
+ if (note) subject.appendChild(h("span", "kb-acl-note kb-meta", note));
269
+ const role = h("span", "kb-acl-role kb-chip", entry.deny ? "\u62D2\u7EDD" : kbRoleLabel(entry.role));
270
+ const metaBits = [];
271
+ if (entry.inheritedFrom) metaBits.push(`\u7EE7\u627F\u81EA ${entry.inheritedFrom}`);
272
+ const expiry = expiryMeta(entry.expiresAt, now);
273
+ if (expiry.text) {
274
+ metaBits.push(expiry.text);
275
+ if (expiry.soon) row.classList.add("is-expiring");
276
+ }
277
+ if (entry.system) metaBits.push("\u7CFB\u7EDF\u7BA1\u7406");
278
+ const remove = h("button", "kb-acl-remove", "\xD7");
279
+ remove.type = "button";
280
+ remove.dataset.aclRemove = "";
281
+ remove.setAttribute("aria-label", `\u79FB\u9664 ${entry.subject.label}`);
282
+ remove.title = `\u79FB\u9664 ${entry.subject.label}`;
283
+ row.append(subject, role);
284
+ if (metaBits.length) row.appendChild(h("span", "kb-acl-meta", metaBits.join(" \xB7 ")));
285
+ row.appendChild(remove);
286
+ return row;
287
+ }
288
+ function renderKbAcl(el, model, opts) {
289
+ if (typeof document === "undefined") return el;
290
+ const m = normalizeAclModel(model);
291
+ const now = /* @__PURE__ */ new Date();
292
+ el.textContent = "";
293
+ el.classList.add("kb-acl");
294
+ aclPanelMap.set(el, opts);
295
+ const title = h("div", "kb-acl-title");
296
+ const lock = svgIcon(KB_ICON_LOCK);
297
+ if (lock) title.appendChild(lock);
298
+ title.appendChild(h("span", "kb-acl-title-text", m.resource));
299
+ const inheritBtn = h(
300
+ "button",
301
+ m.broken ? "kb-acl-act is-danger" : "kb-acl-act",
302
+ m.broken ? "\u6062\u590D\u7EE7\u627F" : "\u505C\u6B62\u7EE7\u627F"
303
+ );
304
+ inheritBtn.type = "button";
305
+ if (m.broken) {
306
+ inheritBtn.dataset.aclRestore = "";
307
+ inheritBtn.title = "\u6062\u590D\u7EE7\u627F\u5C06\u4E22\u5F03\u672C\u9879\u81EA\u5B9A\u4E49\u7684\u6743\u9650";
308
+ } else {
309
+ inheritBtn.dataset.aclBreak = "";
310
+ inheritBtn.title = "\u505C\u6B62\u4ECE\u7236\u7EA7\u7EE7\u627F\uFF0C\u6539\u4E3A\u72EC\u7ACB\u7BA1\u7406\u672C\u9879\u6743\u9650";
311
+ }
312
+ const head = h("header", "kb-acl-head");
313
+ head.append(title, inheritBtn);
314
+ el.appendChild(head);
315
+ const banner = h("div", "kb-acl-inherit");
316
+ if (m.broken) {
317
+ banner.classList.add("is-broken");
318
+ banner.appendChild(h("span", "kb-acl-inherit-text", "\u5DF2\u505C\u6B62\u7EE7\u627F\u2014\u2014\u672C\u9879\u6743\u9650\u72EC\u7ACB\u4E8E\u7236\u7EA7"));
319
+ } else {
320
+ if (m.parentChain.length) {
321
+ banner.appendChild(h("span", "kb-acl-inherit-label", "\u7EE7\u627F\u81EA"));
322
+ m.parentChain.forEach((name, i) => {
323
+ if (i > 0) banner.appendChild(h("span", "kb-acl-chain-link", "/"));
324
+ banner.appendChild(h("span", "kb-acl-chain-node", name));
325
+ });
326
+ } else {
327
+ banner.appendChild(h("span", "kb-acl-inherit-label", "\u6839\u7EA7\u8D44\u6E90\u2014\u2014\u65E0\u7236\u7EA7\u7EE7\u627F"));
328
+ }
329
+ if (m.exceptions) {
330
+ banner.classList.add("is-partial");
331
+ banner.appendChild(h("span", "kb-acl-chain-link", "\xB7"));
332
+ banner.appendChild(h("span", "kb-acl-exceptions", `${m.exceptions.label}\uFF1A${m.exceptions.count} \u4E2A\u5B50\u9879\u5DF2\u8131\u79BB`));
333
+ }
334
+ }
335
+ el.appendChild(banner);
336
+ const grants = h("div", "kb-acl-grants");
337
+ grants.appendChild(h("div", "kb-acl-sec", "\u76F4\u63A5\u8BBF\u95EE"));
338
+ const orderedEntries = denyFirst(m.entries);
339
+ if (!orderedEntries.length) grants.appendChild(h("div", "kb-acl-none kb-meta", "\u6682\u65E0\u76F4\u63A5\u6388\u6743"));
340
+ for (const entry of orderedEntries) grants.appendChild(buildAclRow(entry, now));
341
+ el.appendChild(grants);
342
+ const links = h("div", "kb-acl-links");
343
+ links.appendChild(h("div", "kb-acl-sec", "\u94FE\u63A5\u8BBF\u95EE"));
344
+ const orderedLinks = denyFirst(m.links);
345
+ if (!orderedLinks.length) links.appendChild(h("div", "kb-acl-none kb-meta", "\u65E0\u94FE\u63A5"));
346
+ for (const entry of orderedLinks) links.appendChild(buildAclRow(entry, now));
347
+ el.appendChild(links);
348
+ const foot = h("footer", "kb-acl-foot");
349
+ const grantBtn = h("button", "kb-acl-grant-btn", "\u6388\u4E88\u8BBF\u95EE");
350
+ grantBtn.type = "button";
351
+ grantBtn.dataset.aclGrant = "";
352
+ foot.appendChild(grantBtn);
353
+ if (m.exposureCount != null) {
354
+ foot.appendChild(h("span", "kb-meta", `\u5BF9 ${formatCount(m.exposureCount)} \u4EBA\u53EF\u89C1`));
355
+ }
356
+ el.appendChild(foot);
357
+ return el;
358
+ }
359
+ function initKbAcl(root) {
360
+ if (typeof document === "undefined") return () => void 0;
361
+ const scope = root ?? document;
362
+ const marked = scope;
363
+ if (marked.__icenKbAclInit) return () => void 0;
364
+ marked.__icenKbAclInit = true;
365
+ const target = scope;
366
+ const onClick = (e) => {
367
+ const t = e.target instanceof Element ? e.target : null;
368
+ if (!t) return;
369
+ const panel = t.closest(".kb-acl");
370
+ if (!panel) return;
371
+ const opts = aclPanelMap.get(panel);
372
+ if (opts === void 0 && !aclPanelMap.has(panel)) return;
373
+ const removeBtn = t.closest("[data-acl-remove]");
374
+ if (removeBtn) {
375
+ const row = removeBtn.closest(".kb-acl-grant");
376
+ const entry = row ? aclRowMap.get(row) : void 0;
377
+ if (!entry || row?.classList.contains("is-system")) return;
378
+ emitIcen(row ?? panel, "icen:kb-acl-remove", { subject: entry.subject });
379
+ opts?.onRemove?.(entry.subject);
380
+ return;
381
+ }
382
+ if (t.closest("[data-acl-grant]")) {
383
+ emitIcen(panel, "icen:kb-acl-grant", {});
384
+ opts?.onGrant?.();
385
+ return;
386
+ }
387
+ const inheritBtn = t.closest("[data-acl-break], [data-acl-restore]");
388
+ if (inheritBtn) {
389
+ const action = inheritBtn.hasAttribute("data-acl-restore") ? "restore" : "break";
390
+ emitIcen(panel, "icen:kb-acl-inherit", { action });
391
+ if (action === "break") opts?.onBreak?.();
392
+ else opts?.onRestore?.();
393
+ }
394
+ };
395
+ target.addEventListener("click", onClick);
396
+ return () => {
397
+ target.removeEventListener("click", onClick);
398
+ marked.__icenKbAclInit = false;
399
+ };
400
+ }
401
+ export {
402
+ initKbAcl,
403
+ normalizeAclModel,
404
+ renderKbAcl
405
+ };
@@ -0,0 +1,38 @@
1
+ import { KbAnswerKind, KbVerifiedVia, KbCitation, KbClarification, KbExplainFact, KbPermissionNotice, KbQueryResult } from './kb-core.js';
2
+ import './ai-core.js';
3
+
4
+ /** 问数答案的组合模型(各槽位有才渲;共享类型一律 import 自 kb-core) */
5
+ interface KbAnswerModel {
6
+ /** generated 生成式 / verified 命中可信资产 */
7
+ kind: KbAnswerKind;
8
+ verifiedVia?: KbVerifiedVia;
9
+ /** 答案正文(可含 [n] 引用编号) */
10
+ body?: string;
11
+ /** 与正文 [n] 对齐的来源(编号有效性据此判定,无效编号降级纯文本) */
12
+ citations?: KbCitation[];
13
+ clarify?: KbClarification;
14
+ explain?: KbExplainFact;
15
+ permissionNotice?: KbPermissionNotice;
16
+ result?: KbQueryResult | Partial<KbQueryResult>;
17
+ /** ChartSpec(纯 JSON 或其字符串;走 normalizeChartSpec 归一) */
18
+ chartSpec?: unknown;
19
+ followUps?: string[];
20
+ }
21
+ /** renderKbAnswer 选项:handle 回调通道(事件通道恒开) */
22
+ interface KbAnswerOptions {
23
+ /** 结果导出(format 取自 result.exportFormats;未提供则导出钮禁用) */
24
+ onExport?: (format: string) => void;
25
+ /** 追问建议点击 */
26
+ onFollowUp?: (text: string) => void;
27
+ /** 认证徽章点击(与 icen:kb-verified-open 同语义) */
28
+ onVerifiedOpen?: (assetId: string) => void;
29
+ }
30
+ /**
31
+ * 渲染问数答案组合容器(快照式:重复调用整段重建),返回挂载元素 el
32
+ * (render* 约定;SSR 原样返回)。槽位顺序:徽章区(认证 + 权限提示)→ 正文
33
+ * ([n] 角标流)→ 澄清反问 → 口径解释折叠 → 结果表(截断声明 + 导出)→ 图表区
34
+ * (ChartSpec + 类型切换)→ 追问建议。各槽位有才渲;权限提示不可关闭。
35
+ */
36
+ declare function renderKbAnswer(el: HTMLElement, model: KbAnswerModel | Partial<KbAnswerModel>, opts?: KbAnswerOptions): HTMLElement;
37
+
38
+ export { type KbAnswerModel, type KbAnswerOptions, renderKbAnswer };