@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.
- package/AGENTS.md +49 -6
- package/README.md +12 -9
- package/dist/behaviors/ai-branch.d.mts +19 -0
- package/dist/behaviors/ai-branch.mjs +217 -0
- package/dist/behaviors/ai-chat.d.mts +8 -6
- package/dist/behaviors/ai-chat.mjs +68 -8
- package/dist/behaviors/ai-composer.mjs +108 -20
- package/dist/behaviors/ai-feedback.d.mts +26 -0
- package/dist/behaviors/ai-feedback.mjs +303 -0
- package/dist/behaviors/ai-panel.d.mts +15 -1
- package/dist/behaviors/ai-panel.mjs +26 -0
- package/dist/behaviors/ai-threads.d.mts +38 -0
- package/dist/behaviors/ai-threads.mjs +319 -0
- package/dist/behaviors/ai-tool.mjs +5 -0
- package/dist/behaviors/ai-tools.mjs +5 -0
- package/dist/behaviors/charts.d.mts +103 -1
- package/dist/behaviors/charts.mjs +795 -0
- package/dist/behaviors/events.d.mts +193 -0
- package/dist/behaviors/kb-access.d.mts +34 -0
- package/dist/behaviors/kb-access.mjs +569 -0
- package/dist/behaviors/kb-acl.d.mts +50 -0
- package/dist/behaviors/kb-acl.mjs +405 -0
- package/dist/behaviors/kb-answer.d.mts +38 -0
- package/dist/behaviors/kb-answer.mjs +2055 -0
- package/dist/behaviors/kb-audit.d.mts +23 -0
- package/dist/behaviors/kb-audit.mjs +375 -0
- package/dist/behaviors/kb-canvas.d.mts +44 -0
- package/dist/behaviors/kb-canvas.mjs +589 -0
- package/dist/behaviors/kb-chain.d.mts +15 -0
- package/dist/behaviors/kb-chain.mjs +270 -0
- package/dist/behaviors/kb-checkpoint.d.mts +16 -0
- package/dist/behaviors/kb-checkpoint.mjs +342 -0
- package/dist/behaviors/kb-chunks.d.mts +31 -0
- package/dist/behaviors/kb-chunks.mjs +475 -0
- package/dist/behaviors/kb-citation.d.mts +38 -0
- package/dist/behaviors/kb-citation.mjs +677 -0
- package/dist/behaviors/kb-clarify.d.mts +20 -0
- package/dist/behaviors/kb-clarify.mjs +248 -0
- package/dist/behaviors/kb-conflict.d.mts +22 -0
- package/dist/behaviors/kb-conflict.mjs +284 -0
- package/dist/behaviors/kb-connector.d.mts +17 -0
- package/dist/behaviors/kb-connector.mjs +375 -0
- package/dist/behaviors/kb-core.d.mts +529 -0
- package/dist/behaviors/kb-core.mjs +890 -0
- package/dist/behaviors/kb-eval.d.mts +22 -0
- package/dist/behaviors/kb-eval.mjs +408 -0
- package/dist/behaviors/kb-explain.d.mts +11 -0
- package/dist/behaviors/kb-explain.mjs +243 -0
- package/dist/behaviors/kb-filter.d.mts +31 -0
- package/dist/behaviors/kb-filter.mjs +587 -0
- package/dist/behaviors/kb-gap.d.mts +27 -0
- package/dist/behaviors/kb-gap.mjs +273 -0
- package/dist/behaviors/kb-hittest.d.mts +34 -0
- package/dist/behaviors/kb-hittest.mjs +409 -0
- package/dist/behaviors/kb-metadata.d.mts +29 -0
- package/dist/behaviors/kb-metadata.mjs +326 -0
- package/dist/behaviors/kb-passage.d.mts +41 -0
- package/dist/behaviors/kb-passage.mjs +529 -0
- package/dist/behaviors/kb-pipeline.d.mts +18 -0
- package/dist/behaviors/kb-pipeline.mjs +381 -0
- package/dist/behaviors/kb-qa.d.mts +26 -0
- package/dist/behaviors/kb-qa.mjs +332 -0
- package/dist/behaviors/kb-rerank.d.mts +20 -0
- package/dist/behaviors/kb-rerank.mjs +315 -0
- package/dist/behaviors/kb-retrieval.d.mts +49 -0
- package/dist/behaviors/kb-retrieval.mjs +746 -0
- package/dist/behaviors/kb-review.d.mts +44 -0
- package/dist/behaviors/kb-review.mjs +451 -0
- package/dist/behaviors/kb-sandbox.d.mts +33 -0
- package/dist/behaviors/kb-sandbox.mjs +325 -0
- package/dist/behaviors/kb-segment.d.mts +32 -0
- package/dist/behaviors/kb-segment.mjs +499 -0
- package/dist/behaviors/kb-sources.d.mts +27 -0
- package/dist/behaviors/kb-sources.mjs +373 -0
- package/dist/behaviors/kb-sql.d.mts +43 -0
- package/dist/behaviors/kb-sql.mjs +948 -0
- package/dist/behaviors/kb-trace.d.mts +16 -0
- package/dist/behaviors/kb-trace.mjs +414 -0
- package/dist/behaviors/kb-visibility.d.mts +44 -0
- package/dist/behaviors/kb-visibility.mjs +403 -0
- package/dist/behaviors/kb-who-can.d.mts +32 -0
- package/dist/behaviors/kb-who-can.mjs +379 -0
- package/dist/components/ai-branch.mjs +3 -0
- package/dist/components/ai-chat.css +295 -0
- package/dist/components/ai-feedback.mjs +3 -0
- package/dist/components/ai-panel.css +217 -0
- package/dist/components/ai-threads.mjs +3 -0
- package/dist/components/chart-graph.css +57 -0
- package/dist/components/chart-graph.mjs +4 -0
- package/dist/components/chart-map.mjs +3 -0
- package/dist/components/chart-scatter.mjs +1 -1
- package/dist/components/charts.css +32 -0
- package/dist/components/charts.mjs +1 -0
- package/dist/components/kb-access.mjs +4 -0
- package/dist/components/kb-acl.mjs +4 -0
- package/dist/components/kb-agent.css +481 -0
- package/dist/components/kb-answer.mjs +4 -0
- package/dist/components/kb-audit.mjs +4 -0
- package/dist/components/kb-canvas.mjs +4 -0
- package/dist/components/kb-chain.mjs +4 -0
- package/dist/components/kb-checkpoint.mjs +4 -0
- package/dist/components/kb-chunks.mjs +4 -0
- package/dist/components/kb-citation.mjs +4 -0
- package/dist/components/kb-clarify.mjs +4 -0
- package/dist/components/kb-conflict.mjs +4 -0
- package/dist/components/kb-connector.mjs +4 -0
- package/dist/components/kb-data.css +649 -0
- package/dist/components/kb-eval.mjs +4 -0
- package/dist/components/kb-explain.mjs +4 -0
- package/dist/components/kb-filter.mjs +4 -0
- package/dist/components/kb-gap.mjs +4 -0
- package/dist/components/kb-ground.css +566 -0
- package/dist/components/kb-hittest.mjs +4 -0
- package/dist/components/kb-ingest.css +898 -0
- package/dist/components/kb-metadata.mjs +4 -0
- package/dist/components/kb-ops.css +548 -0
- package/dist/components/kb-passage.mjs +4 -0
- package/dist/components/kb-perm.css +444 -0
- package/dist/components/kb-pipeline.mjs +4 -0
- package/dist/components/kb-qa.mjs +4 -0
- package/dist/components/kb-rerank.mjs +4 -0
- package/dist/components/kb-retrieval.mjs +4 -0
- package/dist/components/kb-review.mjs +4 -0
- package/dist/components/kb-sandbox.mjs +4 -0
- package/dist/components/kb-search.css +940 -0
- package/dist/components/kb-segment.mjs +4 -0
- package/dist/components/kb-sources.mjs +4 -0
- package/dist/components/kb-sql.mjs +4 -0
- package/dist/components/kb-trace.mjs +4 -0
- package/dist/components/kb-visibility.mjs +4 -0
- package/dist/components/kb-who-can.mjs +4 -0
- package/dist/components/kb.css +252 -0
- package/dist/components/kb.mjs +9 -0
- package/dist/index.d.mts +37 -2
- package/dist/index.mjs +8555 -205
- package/dist/registry.json +325 -3
- package/dist/ui.css +5451 -45
- package/docs/research/2026-10-03-ai-kb-components.md +392 -0
- package/docs/research/2026-10-03-kb-permissions.md +992 -0
- package/docs/spec/kb-family.md +443 -0
- package/package.json +2 -2
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { KbAuditEntry, KbHygieneIssue } from './kb-core.js';
|
|
2
|
+
import './ai-core.js';
|
|
3
|
+
|
|
4
|
+
/** renderKbAudit 入参(宽进:契约对象或素对象皆可,内部 normalize) */
|
|
5
|
+
interface KbAuditOptions {
|
|
6
|
+
/** 审计事件(原样渲染;排序与读取去重归数据侧,见文件头纪律说明) */
|
|
7
|
+
entries: Array<KbAuditEntry | Record<string, unknown>>;
|
|
8
|
+
/** 权限异味节(传入才渲染该节) */
|
|
9
|
+
hygiene?: Array<KbHygieneIssue | Record<string, unknown>>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 渲染权限审计时间线(快照式:重复调用整段重建 el 内容),返回挂载元素 el。
|
|
13
|
+
* 结构:通道过滤 bar + 导出钮 → 权限异味节(opts.hygiene 传入才渲染)→ 事件时间线。
|
|
14
|
+
*/
|
|
15
|
+
declare function renderKbAudit(el: HTMLElement, opts: KbAuditOptions): HTMLElement;
|
|
16
|
+
/**
|
|
17
|
+
* 初始化审计交互(root 级委托,缺省 document;幂等标记 + 返回销毁函数,销毁后可重新 init):
|
|
18
|
+
* 通道过滤(JS 直控行显隐 + aria-pressed + 容器 data-active-channel)/ 导出 / 异味处置。
|
|
19
|
+
* SSR 返回 no-op。
|
|
20
|
+
*/
|
|
21
|
+
declare function initKbAudit(root?: ParentNode): () => void;
|
|
22
|
+
|
|
23
|
+
export { type KbAuditOptions, initKbAudit, renderKbAudit };
|
|
@@ -0,0 +1,375 @@
|
|
|
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
|
+
var KB_AUDIT_ACTIONS = [
|
|
150
|
+
"read",
|
|
151
|
+
"preview",
|
|
152
|
+
"permission_change",
|
|
153
|
+
"grant",
|
|
154
|
+
"revoke",
|
|
155
|
+
"request",
|
|
156
|
+
"approve",
|
|
157
|
+
"deny",
|
|
158
|
+
"delegation",
|
|
159
|
+
"break_glass",
|
|
160
|
+
"sync"
|
|
161
|
+
];
|
|
162
|
+
function kbAuditActionLabel(a) {
|
|
163
|
+
return {
|
|
164
|
+
read: "\u8BFB\u53D6",
|
|
165
|
+
preview: "\u9884\u89C8",
|
|
166
|
+
permission_change: "\u6743\u9650\u53D8\u66F4",
|
|
167
|
+
grant: "\u6388\u4E88",
|
|
168
|
+
revoke: "\u6536\u56DE",
|
|
169
|
+
request: "\u7533\u8BF7",
|
|
170
|
+
approve: "\u6279\u51C6",
|
|
171
|
+
deny: "\u62D2\u7EDD",
|
|
172
|
+
delegation: "\u59D4\u6D3E",
|
|
173
|
+
break_glass: "\u5E94\u6025\u8BBF\u95EE",
|
|
174
|
+
sync: "\u6743\u9650\u540C\u6B65"
|
|
175
|
+
}[a] ?? a;
|
|
176
|
+
}
|
|
177
|
+
function auditChannelOf(a) {
|
|
178
|
+
if (a === "read" || a === "preview") return "read";
|
|
179
|
+
if (a === "permission_change" || a === "grant" || a === "revoke" || a === "delegation" || a === "sync") return "permission";
|
|
180
|
+
if (a === "request" || a === "approve" || a === "deny") return "request";
|
|
181
|
+
return "system";
|
|
182
|
+
}
|
|
183
|
+
function normalizeAuditEntry(raw) {
|
|
184
|
+
const r = raw;
|
|
185
|
+
const actor = r.actor ?? {};
|
|
186
|
+
const kind = actor.kind === "app" || actor.kind === "system" ? actor.kind : "human";
|
|
187
|
+
return {
|
|
188
|
+
id: String(r.id ?? ""),
|
|
189
|
+
at: String(r.at ?? ""),
|
|
190
|
+
actor: { kind, name: String(actor.name ?? "\u672A\u77E5") },
|
|
191
|
+
action: KB_AUDIT_ACTIONS.includes(r.action) ? r.action : "read",
|
|
192
|
+
resource: typeof r.resource === "string" && r.resource ? r.resource : void 0,
|
|
193
|
+
detail: String(r.detail ?? ""),
|
|
194
|
+
breakGlass: r.breakGlass === true
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
var KB_HYGIENE_KINDS = [
|
|
198
|
+
"org_wide_link",
|
|
199
|
+
"broad_group",
|
|
200
|
+
"sensitive_mismatch",
|
|
201
|
+
"broken_inheritance",
|
|
202
|
+
"orphaned_owner",
|
|
203
|
+
"overexposed"
|
|
204
|
+
];
|
|
205
|
+
function kbHygieneLabel(k) {
|
|
206
|
+
return {
|
|
207
|
+
org_wide_link: "\u7EC4\u7EC7\u7EA7\u94FE\u63A5",
|
|
208
|
+
broad_group: "\u8FC7\u5BBD\u7EC4\u6388\u6743",
|
|
209
|
+
sensitive_mismatch: "\u654F\u611F\u5EA6\u9519\u914D",
|
|
210
|
+
broken_inheritance: "\u65AD\u7EE7\u627F",
|
|
211
|
+
orphaned_owner: "\u8D1F\u8D23\u4EBA\u7F3A\u4F4D",
|
|
212
|
+
overexposed: "\u8FC7\u5EA6\u66B4\u9732"
|
|
213
|
+
}[k] ?? k;
|
|
214
|
+
}
|
|
215
|
+
function normalizeHygieneIssue(raw) {
|
|
216
|
+
const r = raw;
|
|
217
|
+
const severity = r.severity === "high" || r.severity === "medium" || r.severity === "low" ? r.severity : "medium";
|
|
218
|
+
return {
|
|
219
|
+
id: String(r.id ?? ""),
|
|
220
|
+
kind: KB_HYGIENE_KINDS.includes(r.kind) ? r.kind : "overexposed",
|
|
221
|
+
resource: String(r.resource ?? ""),
|
|
222
|
+
metric: String(r.metric ?? ""),
|
|
223
|
+
hint: String(r.hint ?? ""),
|
|
224
|
+
severity
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/behaviors/events.ts
|
|
229
|
+
function emitIcen(source, type, detail) {
|
|
230
|
+
return source.dispatchEvent(
|
|
231
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/behaviors/kb-audit.ts
|
|
236
|
+
var CHANNEL_CHIPS = [
|
|
237
|
+
["all", "\u5168\u90E8"],
|
|
238
|
+
["read", "\u8BFB\u53D6"],
|
|
239
|
+
["permission", "\u6743\u9650\u53D8\u66F4"],
|
|
240
|
+
["request", "\u7533\u8BF7"],
|
|
241
|
+
["system", "\u7CFB\u7EDF"]
|
|
242
|
+
];
|
|
243
|
+
function fmtStamp(at) {
|
|
244
|
+
const t = Date.parse(at);
|
|
245
|
+
if (!Number.isFinite(t)) return at || "\u2014";
|
|
246
|
+
const d = new Date(t);
|
|
247
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
248
|
+
return `${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}`;
|
|
249
|
+
}
|
|
250
|
+
var hygieneIssueMap = /* @__PURE__ */ new WeakMap();
|
|
251
|
+
function renderKbAudit(el, opts) {
|
|
252
|
+
if (typeof document === "undefined") return el;
|
|
253
|
+
const entries = (Array.isArray(opts.entries) ? opts.entries : []).map(normalizeAuditEntry);
|
|
254
|
+
const hygiene = Array.isArray(opts.hygiene) ? opts.hygiene.map(normalizeHygieneIssue) : void 0;
|
|
255
|
+
el.textContent = "";
|
|
256
|
+
const root = h("div", "kb-audit");
|
|
257
|
+
root.dataset.activeChannel = "all";
|
|
258
|
+
const bar = h("div", "kb-audit-bar");
|
|
259
|
+
const channels = h("div", "kb-audit-channels");
|
|
260
|
+
channels.setAttribute("role", "group");
|
|
261
|
+
channels.setAttribute("aria-label", "\u5BA1\u8BA1\u901A\u9053\u8FC7\u6EE4");
|
|
262
|
+
for (const [value, label] of CHANNEL_CHIPS) {
|
|
263
|
+
const chip = h("button", "kb-chip", label);
|
|
264
|
+
chip.type = "button";
|
|
265
|
+
chip.dataset.channel = value;
|
|
266
|
+
chip.setAttribute("aria-pressed", String(value === "all"));
|
|
267
|
+
channels.appendChild(chip);
|
|
268
|
+
}
|
|
269
|
+
bar.appendChild(channels);
|
|
270
|
+
const exportBtn = h("button", "kb-audit-export", "\u5BFC\u51FA CSV");
|
|
271
|
+
exportBtn.type = "button";
|
|
272
|
+
exportBtn.dataset.auditExport = "";
|
|
273
|
+
exportBtn.setAttribute("aria-label", "\u5BFC\u51FA\u5F53\u524D\u8FC7\u6EE4\u89C6\u56FE\u4E3A CSV");
|
|
274
|
+
bar.appendChild(exportBtn);
|
|
275
|
+
root.appendChild(bar);
|
|
276
|
+
if (hygiene) {
|
|
277
|
+
const section = h("div", "kb-audit-hygiene");
|
|
278
|
+
section.setAttribute("role", "list");
|
|
279
|
+
section.setAttribute("aria-label", "\u6743\u9650\u5F02\u5473");
|
|
280
|
+
if (!hygiene.length) {
|
|
281
|
+
section.appendChild(h("div", "kb-empty", "\u672A\u53D1\u73B0\u6743\u9650\u5F02\u5473"));
|
|
282
|
+
} else {
|
|
283
|
+
for (const issue of hygiene) {
|
|
284
|
+
const row = h("div", `kb-hygiene is-${issue.severity}`);
|
|
285
|
+
row.setAttribute("role", "listitem");
|
|
286
|
+
row.dataset.hygieneId = issue.id;
|
|
287
|
+
hygieneIssueMap.set(row, issue);
|
|
288
|
+
row.appendChild(h("span", "kb-badge kb-hygiene-kind", kbHygieneLabel(issue.kind)));
|
|
289
|
+
row.appendChild(h("span", "kb-hygiene-resource", issue.resource));
|
|
290
|
+
row.appendChild(h("span", "kb-num kb-hygiene-metric", issue.metric));
|
|
291
|
+
row.appendChild(h("span", "kb-meta kb-hygiene-hint", issue.hint));
|
|
292
|
+
const act = h("button", "kb-hygiene-act", "\u5904\u7F6E");
|
|
293
|
+
act.type = "button";
|
|
294
|
+
act.dataset.hygieneAct = "";
|
|
295
|
+
act.setAttribute("aria-label", `\u5904\u7F6E\uFF1A${kbHygieneLabel(issue.kind)} \xB7 ${issue.resource}`);
|
|
296
|
+
row.appendChild(act);
|
|
297
|
+
section.appendChild(row);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
root.appendChild(section);
|
|
301
|
+
}
|
|
302
|
+
const list = h("ol", "kb-audit-list");
|
|
303
|
+
if (!entries.length) {
|
|
304
|
+
list.appendChild(h("li", "kb-empty", "\u6682\u65E0\u5BA1\u8BA1\u4E8B\u4EF6"));
|
|
305
|
+
} else {
|
|
306
|
+
for (const e of entries) {
|
|
307
|
+
const breakglass = e.breakGlass || e.action === "break_glass";
|
|
308
|
+
const row = h("li", `kb-audit-entry${breakglass ? " is-breakglass" : ""}`);
|
|
309
|
+
row.dataset.channel = auditChannelOf(e.action);
|
|
310
|
+
row.appendChild(h("span", "kb-audit-at kb-num", fmtStamp(e.at)));
|
|
311
|
+
const actor = h("span", "kb-audit-actor", e.actor.name);
|
|
312
|
+
actor.dataset.kind = e.actor.kind;
|
|
313
|
+
row.appendChild(actor);
|
|
314
|
+
row.appendChild(h("span", "kb-audit-action kb-chip", kbAuditActionLabel(e.action)));
|
|
315
|
+
row.appendChild(h("span", "kb-audit-detail", e.resource ? `${e.detail} \xB7 ${e.resource}` : e.detail));
|
|
316
|
+
if (breakglass) row.appendChild(h("span", "kb-meta kb-audit-alert", "\u5DF2\u5B9E\u65F6\u544A\u8B66\u5B89\u5168\u56E2\u961F"));
|
|
317
|
+
list.appendChild(row);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
root.appendChild(list);
|
|
321
|
+
el.appendChild(root);
|
|
322
|
+
return el;
|
|
323
|
+
}
|
|
324
|
+
var noopDestroy = () => void 0;
|
|
325
|
+
function initKbAudit(root) {
|
|
326
|
+
if (typeof document === "undefined") return noopDestroy;
|
|
327
|
+
const scope = root ?? document;
|
|
328
|
+
const marked = scope;
|
|
329
|
+
if (marked.__icenKbAuditInit) return noopDestroy;
|
|
330
|
+
marked.__icenKbAuditInit = true;
|
|
331
|
+
const target = scope;
|
|
332
|
+
const applyFilter = (auditRoot, channel) => {
|
|
333
|
+
auditRoot.dataset.activeChannel = channel;
|
|
334
|
+
auditRoot.querySelectorAll(".kb-audit-channels .kb-chip").forEach((chip) => {
|
|
335
|
+
chip.setAttribute("aria-pressed", String(chip.dataset.channel === channel));
|
|
336
|
+
});
|
|
337
|
+
auditRoot.querySelectorAll(".kb-audit-entry").forEach((row) => {
|
|
338
|
+
row.hidden = channel !== "all" && row.dataset.channel !== channel;
|
|
339
|
+
});
|
|
340
|
+
};
|
|
341
|
+
const onClick = (e) => {
|
|
342
|
+
const t = e.target instanceof Element ? e.target : null;
|
|
343
|
+
if (!t) return;
|
|
344
|
+
const actBtn = t.closest("[data-hygiene-act]");
|
|
345
|
+
if (actBtn) {
|
|
346
|
+
const row = actBtn.closest(".kb-hygiene");
|
|
347
|
+
const issue = row ? hygieneIssueMap.get(row) : void 0;
|
|
348
|
+
if (issue) emitIcen(actBtn, "icen:kb-hygiene-action", { issue });
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const auditRoot = t.closest(".kb-audit");
|
|
352
|
+
if (!auditRoot) return;
|
|
353
|
+
const chip = t.closest(".kb-audit-channels .kb-chip[data-channel]");
|
|
354
|
+
if (chip) {
|
|
355
|
+
const channel = chip.dataset.channel ?? "all";
|
|
356
|
+
applyFilter(auditRoot, channel);
|
|
357
|
+
emitIcen(chip, "icen:kb-audit-filter", { channel });
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const exportBtn = t.closest("[data-audit-export]");
|
|
361
|
+
if (exportBtn) {
|
|
362
|
+
const channel = auditRoot.dataset.activeChannel ?? "all";
|
|
363
|
+
emitIcen(exportBtn, "icen:kb-audit-export", { channel });
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
target.addEventListener("click", onClick);
|
|
367
|
+
return () => {
|
|
368
|
+
target.removeEventListener("click", onClick);
|
|
369
|
+
marked.__icenKbAuditInit = false;
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
export {
|
|
373
|
+
initKbAudit,
|
|
374
|
+
renderKbAudit
|
|
375
|
+
};
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { KbCanvasVersion } from './kb-core.js';
|
|
2
|
+
import { renderAiDiff } from './ai-diff.js';
|
|
3
|
+
import './ai-core.js';
|
|
4
|
+
|
|
5
|
+
interface KbCanvasOptions {
|
|
6
|
+
/** 左栏最小占比(同时约束右栏 1-minRatio);默认 0.2,钳制到 [0.05, 0.5] */
|
|
7
|
+
minRatio?: number;
|
|
8
|
+
/** icen:kb-canvas-ai 的 handle 通道(与事件双轨) */
|
|
9
|
+
onAI?: (detail: {
|
|
10
|
+
instruction: string;
|
|
11
|
+
selection?: {
|
|
12
|
+
text: string;
|
|
13
|
+
};
|
|
14
|
+
}) => void;
|
|
15
|
+
/** icen:kb-canvas-version 的 handle 通道(与事件双轨) */
|
|
16
|
+
onVersion?: (detail: {
|
|
17
|
+
versionId: string;
|
|
18
|
+
}) => void;
|
|
19
|
+
}
|
|
20
|
+
interface KbCanvasSelectionTool {
|
|
21
|
+
label: string;
|
|
22
|
+
instruction: string;
|
|
23
|
+
}
|
|
24
|
+
interface KbCanvasHandle {
|
|
25
|
+
/** 挂左栏(对话流):Node 经 appendChild 接收(宿主保留所有权);字符串按纯文本写入 */
|
|
26
|
+
setChat: (content?: Node | string | null) => void;
|
|
27
|
+
/** 挂右栏(文档):同 setChat 语义 */
|
|
28
|
+
setDoc: (content?: Node | string | null) => void;
|
|
29
|
+
/** 右栏追加内联 diff(宿主传入已解析的 AiDiffFile[],渲染复用 ai-diff) */
|
|
30
|
+
addDiff: (files: Parameters<typeof renderAiDiff>[1]['files']) => void;
|
|
31
|
+
/** 版本时间轴追加节点;current=true 时抢占高亮(其余去 current) */
|
|
32
|
+
addVersion: (v: KbCanvasVersion) => void;
|
|
33
|
+
/** 注入划词工具条工具(label + instruction);空数组 = 关闭划词功能 */
|
|
34
|
+
setSelectionTools: (tools: KbCanvasSelectionTool[]) => void;
|
|
35
|
+
/** 摘监听/定时器并清空注入结构;可重复调用 */
|
|
36
|
+
destroy: () => void;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 创建双栏工作台画布句柄:左对话/右文档 + 比例拖拽 + 内联 diff 挂载 + 版本时间轴 +
|
|
40
|
+
* 划词 AI 工具条。事件:icen:kb-canvas-ai / icen:kb-canvas-version(均自画布根冒泡)。
|
|
41
|
+
*/
|
|
42
|
+
declare function createKbCanvas(el: HTMLElement, opts?: KbCanvasOptions): KbCanvasHandle;
|
|
43
|
+
|
|
44
|
+
export { type KbCanvasHandle, type KbCanvasOptions, type KbCanvasSelectionTool, createKbCanvas };
|