@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,589 @@
|
|
|
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 relativeTime(input, now = /* @__PURE__ */ new Date()) {
|
|
150
|
+
const t = input instanceof Date ? input.getTime() : typeof input === "number" ? input : Date.parse(input);
|
|
151
|
+
if (!Number.isFinite(t)) return typeof input === "string" ? input : "\u2014";
|
|
152
|
+
const diff = now.getTime() - t;
|
|
153
|
+
const MIN = 6e4, HOUR = 36e5, DAY = 864e5;
|
|
154
|
+
if (diff < MIN) return "\u521A\u521A";
|
|
155
|
+
if (diff < HOUR) return `${Math.floor(diff / MIN)} \u5206\u949F\u524D`;
|
|
156
|
+
if (diff < DAY) return `${Math.floor(diff / HOUR)} \u5C0F\u65F6\u524D`;
|
|
157
|
+
if (diff < 30 * DAY) return `${Math.floor(diff / DAY)} \u5929\u524D`;
|
|
158
|
+
const d = new Date(t);
|
|
159
|
+
const ymd = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
160
|
+
return ymd;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// src/behaviors/events.ts
|
|
164
|
+
function onIcen(type, a, b, c) {
|
|
165
|
+
if (typeof document === "undefined") return () => {
|
|
166
|
+
};
|
|
167
|
+
const selector = typeof a === "string" ? a : null;
|
|
168
|
+
const handler = selector ? b : a;
|
|
169
|
+
const opts = selector ? c : b;
|
|
170
|
+
const within = opts?.within ?? document;
|
|
171
|
+
const once = opts?.once === true;
|
|
172
|
+
let off = () => {
|
|
173
|
+
};
|
|
174
|
+
const fn = (e) => {
|
|
175
|
+
const broadcast = !(e.target instanceof Element);
|
|
176
|
+
if (selector) {
|
|
177
|
+
if (broadcast) return;
|
|
178
|
+
const detail = e.detail;
|
|
179
|
+
const src = detail?.source instanceof Element ? detail.source : e.target;
|
|
180
|
+
if (!(src instanceof Element) || !src.closest(selector)) return;
|
|
181
|
+
}
|
|
182
|
+
handler(e);
|
|
183
|
+
if (once) off();
|
|
184
|
+
};
|
|
185
|
+
within.addEventListener(type, fn);
|
|
186
|
+
off = () => {
|
|
187
|
+
within.removeEventListener(type, fn);
|
|
188
|
+
opts?.signal?.removeEventListener("abort", off);
|
|
189
|
+
};
|
|
190
|
+
if (opts?.signal) {
|
|
191
|
+
if (opts.signal.aborted) {
|
|
192
|
+
off();
|
|
193
|
+
return () => {
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
opts.signal.addEventListener("abort", off, { once: true });
|
|
197
|
+
}
|
|
198
|
+
return off;
|
|
199
|
+
}
|
|
200
|
+
function emitIcen(source, type, detail) {
|
|
201
|
+
return source.dispatchEvent(
|
|
202
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/behaviors/ai-diff.ts
|
|
207
|
+
function renderLineRow(line) {
|
|
208
|
+
const tr = document.createElement("tr");
|
|
209
|
+
tr.className = `ai-diff-line--${line.type}`;
|
|
210
|
+
if (line.type === "hunk") {
|
|
211
|
+
const td = h("td", "", line.text);
|
|
212
|
+
td.colSpan = 3;
|
|
213
|
+
tr.appendChild(td);
|
|
214
|
+
return tr;
|
|
215
|
+
}
|
|
216
|
+
const oldTd = h("td", "ai-diff-ln", line.oldNo === null ? "" : String(line.oldNo));
|
|
217
|
+
oldTd.setAttribute("aria-hidden", "true");
|
|
218
|
+
const newTd = h("td", "ai-diff-ln", line.newNo === null ? "" : String(line.newNo));
|
|
219
|
+
newTd.setAttribute("aria-hidden", "true");
|
|
220
|
+
tr.appendChild(oldTd);
|
|
221
|
+
tr.appendChild(newTd);
|
|
222
|
+
tr.appendChild(h("td", "ai-diff-code", line.text));
|
|
223
|
+
return tr;
|
|
224
|
+
}
|
|
225
|
+
function renderFile(file) {
|
|
226
|
+
const root = h("div", `ai-diff-file is-${file.status}`);
|
|
227
|
+
root.dataset.path = file.path;
|
|
228
|
+
const head = h("div", "ai-diff-head");
|
|
229
|
+
head.setAttribute("role", "button");
|
|
230
|
+
head.tabIndex = 0;
|
|
231
|
+
head.setAttribute("aria-expanded", "false");
|
|
232
|
+
const pathText = file.status === "renamed" && file.oldPath ? `${file.oldPath} \u2192 ${file.path}` : file.path;
|
|
233
|
+
head.appendChild(h("span", "ai-diff-path", pathText));
|
|
234
|
+
const stat = h("span", "ai-diff-stat");
|
|
235
|
+
if (file.addCount > 0) stat.appendChild(h("b", "ai-diff-add", `+${file.addCount}`));
|
|
236
|
+
if (file.delCount > 0) stat.appendChild(h("b", "ai-diff-del", `\u2212${file.delCount}`));
|
|
237
|
+
if (stat.children.length > 0) head.appendChild(stat);
|
|
238
|
+
const actions = h("span", "ai-diff-actions");
|
|
239
|
+
actions.appendChild(h("button", "", "\u63A5\u53D7")).setAttribute("data-ai-diff-accept", "");
|
|
240
|
+
const rejectBtn = h("button", "", "\u62D2\u7EDD");
|
|
241
|
+
rejectBtn.setAttribute("data-ai-diff-reject", "");
|
|
242
|
+
actions.appendChild(rejectBtn);
|
|
243
|
+
head.appendChild(actions);
|
|
244
|
+
const verdict = h("span", "ai-diff-verdict");
|
|
245
|
+
verdict.hidden = true;
|
|
246
|
+
head.appendChild(verdict);
|
|
247
|
+
root.appendChild(head);
|
|
248
|
+
const body = h("div", "ai-diff-body");
|
|
249
|
+
body.hidden = true;
|
|
250
|
+
const table = h("table", "ai-diff-table");
|
|
251
|
+
if (file.hunks.length === 0) {
|
|
252
|
+
const tr = document.createElement("tr");
|
|
253
|
+
tr.className = "ai-diff-line--ctx";
|
|
254
|
+
const td = h("td", "", file.binary ? "\u4E8C\u8FDB\u5236\u6587\u4EF6\uFF0C\u65E0\u6CD5\u663E\u793A\u6587\u672C\u5DEE\u5F02" : "\u65E0\u5DEE\u5F02\u5185\u5BB9");
|
|
255
|
+
td.colSpan = 3;
|
|
256
|
+
tr.appendChild(td);
|
|
257
|
+
table.appendChild(tr);
|
|
258
|
+
} else {
|
|
259
|
+
for (const hunk of file.hunks) {
|
|
260
|
+
table.appendChild(renderLineRow({ type: "hunk", oldNo: null, newNo: null, text: hunk.header }));
|
|
261
|
+
for (const line of hunk.lines) table.appendChild(renderLineRow(line));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
body.appendChild(table);
|
|
265
|
+
root.appendChild(body);
|
|
266
|
+
return root;
|
|
267
|
+
}
|
|
268
|
+
function renderAiDiff(el, model) {
|
|
269
|
+
if (typeof document === "undefined") return el;
|
|
270
|
+
el.textContent = "";
|
|
271
|
+
const root = h("div", "ai-diff");
|
|
272
|
+
const files = Array.isArray(model?.files) ? model.files : [];
|
|
273
|
+
for (const file of files) root.appendChild(renderFile(file));
|
|
274
|
+
el.appendChild(root);
|
|
275
|
+
return el;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// src/behaviors/kb-canvas.ts
|
|
279
|
+
var DEFAULT_RATIO = 0.42;
|
|
280
|
+
var SELBAR_HIDE_MS = 1500;
|
|
281
|
+
function clampNum(v, fallback, min, max) {
|
|
282
|
+
const n = typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
283
|
+
return Math.min(max, Math.max(min, n));
|
|
284
|
+
}
|
|
285
|
+
function mountContent(target, content) {
|
|
286
|
+
target.textContent = "";
|
|
287
|
+
if (content == null) return;
|
|
288
|
+
if (typeof content === "string") target.textContent = content;
|
|
289
|
+
else target.appendChild(content);
|
|
290
|
+
}
|
|
291
|
+
function createKbCanvas(el, opts = {}) {
|
|
292
|
+
const stub = {
|
|
293
|
+
setChat: () => void 0,
|
|
294
|
+
setDoc: () => void 0,
|
|
295
|
+
addDiff: () => void 0,
|
|
296
|
+
addVersion: () => void 0,
|
|
297
|
+
setSelectionTools: () => void 0,
|
|
298
|
+
destroy: () => void 0
|
|
299
|
+
};
|
|
300
|
+
if (typeof document === "undefined" || !el) return stub;
|
|
301
|
+
const canvasHost = el;
|
|
302
|
+
canvasHost.__icenKbCanvas?.destroy();
|
|
303
|
+
el.textContent = "";
|
|
304
|
+
const minR = clampNum(opts.minRatio, 0.2, 0.05, 0.5);
|
|
305
|
+
const maxR = 1 - minR;
|
|
306
|
+
let ratio = clampNum(DEFAULT_RATIO, DEFAULT_RATIO, minR, maxR);
|
|
307
|
+
el.classList.add("kb-canvas");
|
|
308
|
+
const main = h("div", "kb-canvas-main");
|
|
309
|
+
const chat = h("div", "kb-canvas-chat");
|
|
310
|
+
const divider = h("div", "kb-canvas-divider");
|
|
311
|
+
const doc = h("div", "kb-canvas-doc");
|
|
312
|
+
main.append(chat, divider, doc);
|
|
313
|
+
const versions = h("div", "kb-canvas-versions");
|
|
314
|
+
versions.hidden = true;
|
|
315
|
+
const selbar = h("div", "kb-canvas-selbar");
|
|
316
|
+
selbar.hidden = true;
|
|
317
|
+
el.append(main, versions, selbar);
|
|
318
|
+
const ac = new AbortController();
|
|
319
|
+
const { signal } = ac;
|
|
320
|
+
let destroyed = false;
|
|
321
|
+
let tools = [];
|
|
322
|
+
let selText = "";
|
|
323
|
+
divider.setAttribute("role", "separator");
|
|
324
|
+
divider.setAttribute("aria-orientation", "vertical");
|
|
325
|
+
divider.setAttribute("aria-label", "\u8C03\u6574\u753B\u5E03\u53CC\u680F\u6BD4\u4F8B");
|
|
326
|
+
divider.tabIndex = 0;
|
|
327
|
+
divider.title = "\u62D6\u62FD\u8C03\u6574\u6BD4\u4F8B\uFF08\u2190/\u2192 \u5FAE\u8C03\uFF0C\u53CC\u51FB\u590D\u4F4D\uFF09";
|
|
328
|
+
const applyRatio = (r) => {
|
|
329
|
+
ratio = Math.min(maxR, Math.max(minR, r));
|
|
330
|
+
el.style.setProperty("--kb-canvas-ratio", String(Math.round(ratio * 1e4) / 1e4));
|
|
331
|
+
divider.setAttribute("aria-valuemin", String(Math.round(minR * 1e3) / 10));
|
|
332
|
+
divider.setAttribute("aria-valuemax", String(Math.round(maxR * 1e3) / 10));
|
|
333
|
+
divider.setAttribute("aria-valuenow", String(Math.round(ratio * 1e3) / 10));
|
|
334
|
+
};
|
|
335
|
+
applyRatio(ratio);
|
|
336
|
+
let dragging = false;
|
|
337
|
+
let dragStartX = 0;
|
|
338
|
+
let dragStartRatio = ratio;
|
|
339
|
+
let dragWidth = 1;
|
|
340
|
+
let rafId = 0;
|
|
341
|
+
let pendingRatio = null;
|
|
342
|
+
const flushRatio = () => {
|
|
343
|
+
rafId = 0;
|
|
344
|
+
if (pendingRatio != null) {
|
|
345
|
+
applyRatio(pendingRatio);
|
|
346
|
+
pendingRatio = null;
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
const endDrag = (e) => {
|
|
350
|
+
if (!dragging) return;
|
|
351
|
+
dragging = false;
|
|
352
|
+
if (rafId) {
|
|
353
|
+
cancelAnimationFrame(rafId);
|
|
354
|
+
rafId = 0;
|
|
355
|
+
}
|
|
356
|
+
if (pendingRatio != null) {
|
|
357
|
+
applyRatio(pendingRatio);
|
|
358
|
+
pendingRatio = null;
|
|
359
|
+
}
|
|
360
|
+
divider.classList.remove("is-active");
|
|
361
|
+
el.classList.remove("is-dragging");
|
|
362
|
+
try {
|
|
363
|
+
divider.releasePointerCapture(e.pointerId);
|
|
364
|
+
} catch {
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
divider.addEventListener("pointerdown", (e) => {
|
|
368
|
+
if (e.button !== 0) return;
|
|
369
|
+
dragging = true;
|
|
370
|
+
dragStartX = e.clientX;
|
|
371
|
+
dragStartRatio = ratio;
|
|
372
|
+
dragWidth = main.clientWidth || 1;
|
|
373
|
+
divider.setPointerCapture(e.pointerId);
|
|
374
|
+
divider.classList.add("is-active");
|
|
375
|
+
el.classList.add("is-dragging");
|
|
376
|
+
e.preventDefault();
|
|
377
|
+
});
|
|
378
|
+
divider.addEventListener(
|
|
379
|
+
"pointermove",
|
|
380
|
+
(e) => {
|
|
381
|
+
if (!dragging) return;
|
|
382
|
+
pendingRatio = dragStartRatio + (e.clientX - dragStartX) / dragWidth;
|
|
383
|
+
if (!rafId) rafId = requestAnimationFrame(flushRatio);
|
|
384
|
+
},
|
|
385
|
+
{ passive: true, signal }
|
|
386
|
+
);
|
|
387
|
+
divider.addEventListener("pointerup", endDrag, { passive: true, signal });
|
|
388
|
+
divider.addEventListener("pointercancel", endDrag, { passive: true, signal });
|
|
389
|
+
divider.addEventListener(
|
|
390
|
+
"keydown",
|
|
391
|
+
(e) => {
|
|
392
|
+
let next = null;
|
|
393
|
+
if (e.key === "ArrowLeft") next = ratio - 0.02;
|
|
394
|
+
else if (e.key === "ArrowRight") next = ratio + 0.02;
|
|
395
|
+
else if (e.key === "Home") next = minR;
|
|
396
|
+
else if (e.key === "End") next = maxR;
|
|
397
|
+
if (next == null) return;
|
|
398
|
+
e.preventDefault();
|
|
399
|
+
applyRatio(next);
|
|
400
|
+
},
|
|
401
|
+
{ signal }
|
|
402
|
+
);
|
|
403
|
+
divider.addEventListener("dblclick", () => applyRatio(DEFAULT_RATIO), { signal });
|
|
404
|
+
versions.addEventListener(
|
|
405
|
+
"click",
|
|
406
|
+
(e) => {
|
|
407
|
+
const t = e.target instanceof Element ? e.target : null;
|
|
408
|
+
const ver = t?.closest(".kb-canvas-ver");
|
|
409
|
+
if (!ver) return;
|
|
410
|
+
const versionId = ver.dataset.versionId ?? "";
|
|
411
|
+
if (!versionId) return;
|
|
412
|
+
emitIcen(ver, "icen:kb-canvas-version", { versionId });
|
|
413
|
+
opts.onVersion?.({ versionId });
|
|
414
|
+
},
|
|
415
|
+
{ signal }
|
|
416
|
+
);
|
|
417
|
+
let hideTimer = null;
|
|
418
|
+
let selbarHovered = false;
|
|
419
|
+
const hideSelbar = () => {
|
|
420
|
+
if (hideTimer) {
|
|
421
|
+
clearTimeout(hideTimer);
|
|
422
|
+
hideTimer = null;
|
|
423
|
+
}
|
|
424
|
+
selbarHovered = false;
|
|
425
|
+
selbar.hidden = true;
|
|
426
|
+
};
|
|
427
|
+
const armHide = () => {
|
|
428
|
+
if (hideTimer) clearTimeout(hideTimer);
|
|
429
|
+
hideTimer = selbarHovered ? null : setTimeout(() => {
|
|
430
|
+
hideTimer = null;
|
|
431
|
+
hideSelbar();
|
|
432
|
+
}, SELBAR_HIDE_MS);
|
|
433
|
+
};
|
|
434
|
+
const selectionWithinHost = () => {
|
|
435
|
+
const sel = window.getSelection();
|
|
436
|
+
if (!sel || sel.isCollapsed || sel.rangeCount === 0) return false;
|
|
437
|
+
const node = sel.anchorNode;
|
|
438
|
+
return node != null && el.contains(node);
|
|
439
|
+
};
|
|
440
|
+
const showSelbar = (text) => {
|
|
441
|
+
if (destroyed || !tools.length) return;
|
|
442
|
+
const sel = window.getSelection();
|
|
443
|
+
if (!sel || sel.rangeCount === 0) {
|
|
444
|
+
hideSelbar();
|
|
445
|
+
return;
|
|
446
|
+
}
|
|
447
|
+
const rect = sel.getRangeAt(0).getBoundingClientRect();
|
|
448
|
+
if (!rect || rect.width === 0 && rect.height === 0) {
|
|
449
|
+
hideSelbar();
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
selText = text;
|
|
453
|
+
selbar.hidden = false;
|
|
454
|
+
const bw = selbar.offsetWidth || 0;
|
|
455
|
+
const bh = selbar.offsetHeight || 0;
|
|
456
|
+
const host = el.getBoundingClientRect();
|
|
457
|
+
let left = rect.left + rect.width / 2 - bw / 2 - host.left;
|
|
458
|
+
let top = rect.top - bh - 8 - host.top;
|
|
459
|
+
left = Math.max(4, Math.min(left, host.width - bw - 4));
|
|
460
|
+
if (top < 4) top = rect.bottom - host.top + 8;
|
|
461
|
+
selbar.style.left = `${Math.round(left)}px`;
|
|
462
|
+
selbar.style.top = `${Math.round(top)}px`;
|
|
463
|
+
armHide();
|
|
464
|
+
};
|
|
465
|
+
onIcen(
|
|
466
|
+
"icen:text-select",
|
|
467
|
+
(e) => {
|
|
468
|
+
if (destroyed) return;
|
|
469
|
+
const text = e.detail.text ?? "";
|
|
470
|
+
if (text.trim()) showSelbar(text);
|
|
471
|
+
},
|
|
472
|
+
{ within: el, signal }
|
|
473
|
+
);
|
|
474
|
+
document.addEventListener(
|
|
475
|
+
"mouseup",
|
|
476
|
+
() => {
|
|
477
|
+
if (destroyed) return;
|
|
478
|
+
const sel = window.getSelection();
|
|
479
|
+
const text = sel?.toString() ?? "";
|
|
480
|
+
if (!sel || !text.trim() || !sel.anchorNode || !el.contains(sel.anchorNode)) return;
|
|
481
|
+
showSelbar(text);
|
|
482
|
+
},
|
|
483
|
+
{ passive: true, signal }
|
|
484
|
+
);
|
|
485
|
+
document.addEventListener(
|
|
486
|
+
"selectionchange",
|
|
487
|
+
() => {
|
|
488
|
+
if (selbar.hidden) return;
|
|
489
|
+
if (!selectionWithinHost()) hideSelbar();
|
|
490
|
+
},
|
|
491
|
+
{ passive: true, signal }
|
|
492
|
+
);
|
|
493
|
+
selbar.addEventListener("pointerenter", () => {
|
|
494
|
+
selbarHovered = true;
|
|
495
|
+
if (hideTimer) {
|
|
496
|
+
clearTimeout(hideTimer);
|
|
497
|
+
hideTimer = null;
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
selbar.addEventListener("pointerleave", () => {
|
|
501
|
+
selbarHovered = false;
|
|
502
|
+
armHide();
|
|
503
|
+
});
|
|
504
|
+
selbar.addEventListener("mousedown", (e) => e.preventDefault());
|
|
505
|
+
selbar.addEventListener(
|
|
506
|
+
"click",
|
|
507
|
+
(e) => {
|
|
508
|
+
const t = e.target instanceof Element ? e.target : null;
|
|
509
|
+
const btn = t?.closest(".kb-canvas-selbar-btn");
|
|
510
|
+
if (!btn) return;
|
|
511
|
+
const instruction = btn.dataset.instruction ?? "";
|
|
512
|
+
if (!instruction) return;
|
|
513
|
+
const detail = { instruction, selection: { text: selText } };
|
|
514
|
+
emitIcen(el, "icen:kb-canvas-ai", detail);
|
|
515
|
+
opts.onAI?.(detail);
|
|
516
|
+
hideSelbar();
|
|
517
|
+
},
|
|
518
|
+
{ signal }
|
|
519
|
+
);
|
|
520
|
+
const rebuildSelbar = () => {
|
|
521
|
+
selbar.textContent = "";
|
|
522
|
+
for (const tool of tools) {
|
|
523
|
+
const btn = h("button", "kb-canvas-selbar-btn");
|
|
524
|
+
btn.type = "button";
|
|
525
|
+
btn.textContent = tool.label;
|
|
526
|
+
btn.dataset.instruction = tool.instruction;
|
|
527
|
+
selbar.appendChild(btn);
|
|
528
|
+
}
|
|
529
|
+
if (selbar.hidden) return;
|
|
530
|
+
if (!tools.length) hideSelbar();
|
|
531
|
+
else armHide();
|
|
532
|
+
};
|
|
533
|
+
const destroy = () => {
|
|
534
|
+
if (destroyed) return;
|
|
535
|
+
destroyed = true;
|
|
536
|
+
ac.abort();
|
|
537
|
+
if (hideTimer) clearTimeout(hideTimer);
|
|
538
|
+
if (rafId) cancelAnimationFrame(rafId);
|
|
539
|
+
hideTimer = null;
|
|
540
|
+
rafId = 0;
|
|
541
|
+
el.classList.remove("kb-canvas", "is-dragging");
|
|
542
|
+
el.style.removeProperty("--kb-canvas-ratio");
|
|
543
|
+
el.textContent = "";
|
|
544
|
+
};
|
|
545
|
+
const handle = {
|
|
546
|
+
setChat: (content) => {
|
|
547
|
+
if (!destroyed) mountContent(chat, content);
|
|
548
|
+
},
|
|
549
|
+
setDoc: (content) => {
|
|
550
|
+
if (!destroyed) mountContent(doc, content);
|
|
551
|
+
},
|
|
552
|
+
addDiff: (files) => {
|
|
553
|
+
if (destroyed) return;
|
|
554
|
+
const wrap = h("div", "kb-canvas-diff");
|
|
555
|
+
renderAiDiff(wrap, { files });
|
|
556
|
+
doc.appendChild(wrap);
|
|
557
|
+
},
|
|
558
|
+
addVersion: (v) => {
|
|
559
|
+
if (destroyed || !v || !v.id) return;
|
|
560
|
+
const ver = h("button", "kb-canvas-ver");
|
|
561
|
+
ver.type = "button";
|
|
562
|
+
ver.dataset.versionId = String(v.id);
|
|
563
|
+
if (v.current === true) {
|
|
564
|
+
for (const prev of Array.from(versions.querySelectorAll(".kb-canvas-ver.is-current"))) {
|
|
565
|
+
prev.classList.remove("is-current");
|
|
566
|
+
}
|
|
567
|
+
ver.classList.add("is-current");
|
|
568
|
+
}
|
|
569
|
+
const dot = h("i", "kb-canvas-ver-dot");
|
|
570
|
+
dot.setAttribute("aria-hidden", "true");
|
|
571
|
+
ver.appendChild(dot);
|
|
572
|
+
ver.appendChild(h("span", "kb-canvas-ver-label", v.label ?? String(v.id)));
|
|
573
|
+
ver.appendChild(h("span", "kb-canvas-ver-time kb-num", relativeTime(v.at)));
|
|
574
|
+
versions.appendChild(ver);
|
|
575
|
+
versions.hidden = false;
|
|
576
|
+
},
|
|
577
|
+
setSelectionTools: (list) => {
|
|
578
|
+
if (destroyed) return;
|
|
579
|
+
tools = Array.isArray(list) ? list.filter((t) => t && typeof t.label === "string" && typeof t.instruction === "string") : [];
|
|
580
|
+
rebuildSelbar();
|
|
581
|
+
},
|
|
582
|
+
destroy
|
|
583
|
+
};
|
|
584
|
+
canvasHost.__icenKbCanvas = handle;
|
|
585
|
+
return handle;
|
|
586
|
+
}
|
|
587
|
+
export {
|
|
588
|
+
createKbCanvas
|
|
589
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { KbChainStep } from './kb-core.js';
|
|
2
|
+
import './ai-core.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 渲染工具链条横向摘要条(与 ai-tool-call 单卡互补的「一屏看完整个链」视角)。
|
|
6
|
+
* 幂等:重复调用只更新状态类与 detail,不重建 DOM(流式安全);返回挂载元素 el。
|
|
7
|
+
*/
|
|
8
|
+
declare function renderKbChain(el: HTMLElement, steps: KbChainStep[]): HTMLElement;
|
|
9
|
+
/**
|
|
10
|
+
* 委托初始化:点击可点步骤 → icen:kb-chain-step {index, step}(自 el 冒泡,onIcen 可收)。
|
|
11
|
+
* 幂等(重复调用安全);返回销毁函数(移除监听并复位标记,可重新 init)。SSR 下 no-op。
|
|
12
|
+
*/
|
|
13
|
+
declare function initKbChain(root?: ParentNode): () => void;
|
|
14
|
+
|
|
15
|
+
export { initKbChain, renderKbChain };
|