@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,677 @@
|
|
|
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
|
+
function getKbSourceType(name) {
|
|
182
|
+
if (name) return sourceTypeRegistry.get(name) ?? BUILTIN_SOURCE_TYPES.doc;
|
|
183
|
+
return BUILTIN_SOURCE_TYPES.doc;
|
|
184
|
+
}
|
|
185
|
+
function normalizeCitation(raw) {
|
|
186
|
+
const r = raw;
|
|
187
|
+
const score = typeof r.score === "number" && Number.isFinite(r.score) ? r.score : r.score == null ? null : null;
|
|
188
|
+
return {
|
|
189
|
+
id: String(r.id ?? ""),
|
|
190
|
+
index: typeof r.index === "number" ? r.index : void 0,
|
|
191
|
+
title: String(r.title ?? "\u672A\u547D\u540D\u6765\u6E90"),
|
|
192
|
+
url: typeof r.url === "string" && r.url ? r.url : void 0,
|
|
193
|
+
kind: typeof r.kind === "string" ? r.kind : void 0,
|
|
194
|
+
citedText: typeof r.citedText === "string" ? r.citedText : void 0,
|
|
195
|
+
snippet: typeof r.snippet === "string" ? r.snippet : void 0,
|
|
196
|
+
date: typeof r.date === "string" ? r.date : void 0,
|
|
197
|
+
score: score === void 0 ? null : score,
|
|
198
|
+
scoreKind: r.scoreKind,
|
|
199
|
+
official: r.official === true,
|
|
200
|
+
permission: r.permission === "restricted" || r.permission === "requestable" ? r.permission : "readable",
|
|
201
|
+
owner: typeof r.owner === "string" ? r.owner : void 0,
|
|
202
|
+
loc: r.loc,
|
|
203
|
+
documentId: typeof r.documentId === "string" ? r.documentId : void 0,
|
|
204
|
+
useCount: typeof r.useCount === "number" ? r.useCount : void 0
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
var CITE_RE = /\[(\d{1,3})\]/g;
|
|
208
|
+
function parseInlineCitations(text) {
|
|
209
|
+
const out = [];
|
|
210
|
+
if (!text) return out;
|
|
211
|
+
let last = 0;
|
|
212
|
+
CITE_RE.lastIndex = 0;
|
|
213
|
+
let m;
|
|
214
|
+
while ((m = CITE_RE.exec(text)) !== null) {
|
|
215
|
+
if (m.index > last) out.push({ text: text.slice(last, m.index) });
|
|
216
|
+
out.push({ text: m[0], n: Number(m[1]) });
|
|
217
|
+
last = m.index + m[0].length;
|
|
218
|
+
}
|
|
219
|
+
if (last < text.length) out.push({ text: text.slice(last) });
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
222
|
+
function relativeTime(input, now = /* @__PURE__ */ new Date()) {
|
|
223
|
+
const t = input instanceof Date ? input.getTime() : typeof input === "number" ? input : Date.parse(input);
|
|
224
|
+
if (!Number.isFinite(t)) return typeof input === "string" ? input : "\u2014";
|
|
225
|
+
const diff = now.getTime() - t;
|
|
226
|
+
const MIN = 6e4, HOUR = 36e5, DAY = 864e5;
|
|
227
|
+
if (diff < MIN) return "\u521A\u521A";
|
|
228
|
+
if (diff < HOUR) return `${Math.floor(diff / MIN)} \u5206\u949F\u524D`;
|
|
229
|
+
if (diff < DAY) return `${Math.floor(diff / HOUR)} \u5C0F\u65F6\u524D`;
|
|
230
|
+
if (diff < 30 * DAY) return `${Math.floor(diff / DAY)} \u5929\u524D`;
|
|
231
|
+
const d = new Date(t);
|
|
232
|
+
const ymd = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
233
|
+
return ymd;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// src/behaviors/events.ts
|
|
237
|
+
function emitIcen(source, type, detail) {
|
|
238
|
+
return source.dispatchEvent(
|
|
239
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// src/behaviors/popover.ts
|
|
244
|
+
var PANEL_SIZING_ATTRS = {
|
|
245
|
+
width: "data-panel-width",
|
|
246
|
+
minWidth: "data-panel-min",
|
|
247
|
+
maxWidth: "data-panel-max",
|
|
248
|
+
minHeight: "data-panel-min-height",
|
|
249
|
+
maxHeight: "data-panel-max-height"
|
|
250
|
+
};
|
|
251
|
+
function readPanelSizing(el) {
|
|
252
|
+
const out = {};
|
|
253
|
+
if (!el) return out;
|
|
254
|
+
for (const key of Object.keys(PANEL_SIZING_ATTRS)) {
|
|
255
|
+
const raw = el.getAttribute(PANEL_SIZING_ATTRS[key]);
|
|
256
|
+
if (raw == null) continue;
|
|
257
|
+
const n = Number(raw);
|
|
258
|
+
if (Number.isFinite(n) && n > 0) out[key] = n;
|
|
259
|
+
}
|
|
260
|
+
return out;
|
|
261
|
+
}
|
|
262
|
+
function resolvePanelSizing(el, overrides) {
|
|
263
|
+
const base = readPanelSizing(el);
|
|
264
|
+
if (!overrides) return base;
|
|
265
|
+
for (const key of Object.keys(overrides)) {
|
|
266
|
+
const v = overrides[key];
|
|
267
|
+
if (typeof v === "number" && Number.isFinite(v) && v > 0) base[key] = v;
|
|
268
|
+
}
|
|
269
|
+
return base;
|
|
270
|
+
}
|
|
271
|
+
function sizingToLayout(sizing, opts) {
|
|
272
|
+
const out = { ...opts };
|
|
273
|
+
if (sizing.minWidth != null) out.minWidth = sizing.minWidth;
|
|
274
|
+
if (sizing.maxWidth != null) out.maxWidth = sizing.maxWidth;
|
|
275
|
+
if (sizing.width != null) {
|
|
276
|
+
out.minWidth = sizing.width;
|
|
277
|
+
out.maxWidth = sizing.width;
|
|
278
|
+
}
|
|
279
|
+
if (sizing.maxHeight != null) out.maxHeight = sizing.maxHeight;
|
|
280
|
+
return out;
|
|
281
|
+
}
|
|
282
|
+
var DEFAULT_MIN_WIDTH = 220;
|
|
283
|
+
var DEFAULT_MAX_WIDTH = 420;
|
|
284
|
+
var DEFAULT_MIN_HEIGHT = 120;
|
|
285
|
+
var DEFAULT_MAX_HEIGHT = 420;
|
|
286
|
+
function clamp(value, min, max) {
|
|
287
|
+
if (max < min) return min;
|
|
288
|
+
return Math.min(max, Math.max(min, value));
|
|
289
|
+
}
|
|
290
|
+
function chooseSide(preferredSide, availableTop, availableBottom, desiredHeight) {
|
|
291
|
+
const preferredSpace = preferredSide === "top" ? availableTop : availableBottom;
|
|
292
|
+
const oppositeSpace = preferredSide === "top" ? availableBottom : availableTop;
|
|
293
|
+
if (preferredSpace >= desiredHeight) return preferredSide;
|
|
294
|
+
if (oppositeSpace >= desiredHeight) return preferredSide === "top" ? "bottom" : "top";
|
|
295
|
+
if (preferredSpace >= oppositeSpace) return preferredSide;
|
|
296
|
+
return preferredSide === "top" ? "bottom" : "top";
|
|
297
|
+
}
|
|
298
|
+
function horizontalAnchor(anchor, width, align) {
|
|
299
|
+
if (align === "center") return anchor.left + anchor.width / 2 - width / 2;
|
|
300
|
+
if (align === "end") return anchor.right - width;
|
|
301
|
+
return anchor.left;
|
|
302
|
+
}
|
|
303
|
+
function computePopoverLayout(anchor, viewport, opts = {}) {
|
|
304
|
+
const margin = opts.margin ?? 12;
|
|
305
|
+
const offset = opts.offset ?? 8;
|
|
306
|
+
const preferredSide = opts.side ?? "top";
|
|
307
|
+
const align = opts.align ?? "start";
|
|
308
|
+
if (preferredSide === "left" || preferredSide === "right") {
|
|
309
|
+
const availableLeft = Math.max(0, anchor.left - margin - offset);
|
|
310
|
+
const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
|
|
311
|
+
const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= DEFAULT_MIN_WIDTH ? preferredSide : preferredSide === "left" ? "right" : "left";
|
|
312
|
+
const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
313
|
+
const maxWidth2 = Math.min(
|
|
314
|
+
Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
|
|
315
|
+
side2 === "left" ? availableLeft : availableRight
|
|
316
|
+
);
|
|
317
|
+
const width2 = minWidth2;
|
|
318
|
+
const left2 = clamp(
|
|
319
|
+
side2 === "left" ? anchor.left - offset - width2 : anchor.right + offset,
|
|
320
|
+
margin,
|
|
321
|
+
viewport.width - margin - width2
|
|
322
|
+
);
|
|
323
|
+
const availableHeight2 = Math.max(80, viewport.height - margin * 2);
|
|
324
|
+
const maxHeight2 = Math.min(opts.maxHeight ?? DEFAULT_MAX_HEIGHT, availableHeight2);
|
|
325
|
+
const desired = clamp(opts.contentHeightHint ?? Math.min(anchor.height, maxHeight2), 40, maxHeight2);
|
|
326
|
+
const anchorTop = align === "center" ? anchor.top + anchor.height / 2 - desired / 2 : align === "end" ? anchor.bottom - desired : anchor.top;
|
|
327
|
+
const top2 = clamp(anchorTop, margin, viewport.height - margin - desired);
|
|
328
|
+
return { side: side2, left: left2, top: top2, width: width2, maxHeight: maxHeight2 };
|
|
329
|
+
}
|
|
330
|
+
const availableWidth = Math.max(80, viewport.width - margin * 2);
|
|
331
|
+
const requestedMinWidth = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
332
|
+
const requestedMaxWidth = Math.max(requestedMinWidth, opts.maxWidth ?? DEFAULT_MAX_WIDTH);
|
|
333
|
+
const maxWidth = Math.min(requestedMaxWidth, availableWidth);
|
|
334
|
+
const minWidth = Math.min(requestedMinWidth, maxWidth);
|
|
335
|
+
const availableTop = Math.max(0, anchor.top - margin - offset);
|
|
336
|
+
const availableBottom = Math.max(0, viewport.height - anchor.bottom - margin - offset);
|
|
337
|
+
const availableHeight = Math.max(80, viewport.height - margin * 2);
|
|
338
|
+
const minHeight = Math.min(Math.max(80, opts.minHeight ?? DEFAULT_MIN_HEIGHT), availableHeight);
|
|
339
|
+
const requestedMaxHeight = Math.max(minHeight, opts.maxHeight ?? DEFAULT_MAX_HEIGHT);
|
|
340
|
+
const fallbackHeight = Math.min(260, requestedMaxHeight);
|
|
341
|
+
const contentHeight = Math.max(opts.contentHeightHint ?? 0, fallbackHeight);
|
|
342
|
+
const desiredHeight = clamp(contentHeight, minHeight, Math.min(requestedMaxHeight, availableHeight));
|
|
343
|
+
const side = chooseSide(preferredSide, availableTop, availableBottom, desiredHeight);
|
|
344
|
+
const sideSpace = side === "top" ? availableTop : availableBottom;
|
|
345
|
+
const maxHeight = Math.min(
|
|
346
|
+
requestedMaxHeight,
|
|
347
|
+
Math.max(80, sideSpace || Math.max(availableTop, availableBottom)),
|
|
348
|
+
availableHeight
|
|
349
|
+
);
|
|
350
|
+
const positionedHeight = clamp(desiredHeight, 40, maxHeight);
|
|
351
|
+
const width = align === "stretch" ? clamp(anchor.width, minWidth, maxWidth) : maxWidth;
|
|
352
|
+
const left = clamp(horizontalAnchor(anchor, width, align), margin, viewport.width - margin - width);
|
|
353
|
+
const desiredTop = side === "top" ? anchor.top - offset - positionedHeight : anchor.bottom + offset;
|
|
354
|
+
const top = clamp(desiredTop, margin, viewport.height - margin - positionedHeight);
|
|
355
|
+
return { side, left, top, width, maxHeight };
|
|
356
|
+
}
|
|
357
|
+
var openPanels = /* @__PURE__ */ new Map();
|
|
358
|
+
function rectFromElement(el) {
|
|
359
|
+
const r = el.getBoundingClientRect();
|
|
360
|
+
return { left: r.left, top: r.top, right: r.right, bottom: r.bottom, width: r.width, height: r.height };
|
|
361
|
+
}
|
|
362
|
+
function resolveAnchor(state) {
|
|
363
|
+
if (state.anchor instanceof Element) {
|
|
364
|
+
if (!state.anchor.isConnected) return null;
|
|
365
|
+
return rectFromElement(state.anchor);
|
|
366
|
+
}
|
|
367
|
+
return state.anchor;
|
|
368
|
+
}
|
|
369
|
+
function relayout(state) {
|
|
370
|
+
const anchor = resolveAnchor(state);
|
|
371
|
+
if (!anchor) {
|
|
372
|
+
closePopover(state.panel);
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const layout = computePopoverLayout(
|
|
376
|
+
anchor,
|
|
377
|
+
{ width: window.innerWidth, height: window.innerHeight },
|
|
378
|
+
state.opts
|
|
379
|
+
);
|
|
380
|
+
const { panel } = state;
|
|
381
|
+
panel.style.left = `${layout.left}px`;
|
|
382
|
+
panel.style.top = `${layout.top}px`;
|
|
383
|
+
panel.style.width = `${layout.width}px`;
|
|
384
|
+
panel.style.maxHeight = `${layout.maxHeight}px`;
|
|
385
|
+
panel.dataset.side = layout.side;
|
|
386
|
+
panel.style.visibility = state.prevVisibility;
|
|
387
|
+
}
|
|
388
|
+
function onWindowChange() {
|
|
389
|
+
openPanels.forEach(relayout);
|
|
390
|
+
}
|
|
391
|
+
function onOutsidePointerDown(e) {
|
|
392
|
+
const target = e.target;
|
|
393
|
+
if (!(target instanceof Node)) return;
|
|
394
|
+
openPanels.forEach((state) => {
|
|
395
|
+
if (state.panel.contains(target)) return;
|
|
396
|
+
if (state.anchor instanceof Element && state.anchor.contains(target)) return;
|
|
397
|
+
closePopover(state.panel);
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
function onKeyDown(e) {
|
|
401
|
+
if (e.key !== "Escape") return;
|
|
402
|
+
const states = Array.from(openPanels.values());
|
|
403
|
+
const top = states[states.length - 1];
|
|
404
|
+
if (top) {
|
|
405
|
+
e.preventDefault();
|
|
406
|
+
closePopover(top.panel);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function openPopover(panel, options) {
|
|
410
|
+
if (typeof document === "undefined") return;
|
|
411
|
+
if (openPanels.has(panel)) closePopover(panel);
|
|
412
|
+
const { anchor, onClose, sizing, ...rest } = options;
|
|
413
|
+
const layoutOpts = sizingToLayout(resolvePanelSizing(panel, sizing), rest);
|
|
414
|
+
const placeholder = document.createComment("popover-anchor");
|
|
415
|
+
panel.parentNode?.insertBefore(placeholder, panel);
|
|
416
|
+
const wasHidden = panel.hidden;
|
|
417
|
+
const prevVisibility = panel.style.visibility;
|
|
418
|
+
const state = {
|
|
419
|
+
panel,
|
|
420
|
+
anchor,
|
|
421
|
+
opts: layoutOpts,
|
|
422
|
+
onClose,
|
|
423
|
+
placeholder,
|
|
424
|
+
wasHidden,
|
|
425
|
+
prevVisibility
|
|
426
|
+
};
|
|
427
|
+
openPanels.set(panel, state);
|
|
428
|
+
document.body.appendChild(panel);
|
|
429
|
+
panel.hidden = false;
|
|
430
|
+
panel.style.visibility = "hidden";
|
|
431
|
+
relayout(state);
|
|
432
|
+
if (openPanels.size === 1) {
|
|
433
|
+
window.addEventListener("resize", onWindowChange);
|
|
434
|
+
window.addEventListener("scroll", onWindowChange, true);
|
|
435
|
+
document.addEventListener("pointerdown", onOutsidePointerDown, true);
|
|
436
|
+
window.addEventListener("keydown", onKeyDown);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
function closePopover(panel) {
|
|
440
|
+
if (typeof document === "undefined") return;
|
|
441
|
+
const state = openPanels.get(panel);
|
|
442
|
+
if (!state) return;
|
|
443
|
+
openPanels.delete(panel);
|
|
444
|
+
panel.style.visibility = state.prevVisibility;
|
|
445
|
+
if (typeof state.wasHidden === "string") panel.setAttribute("hidden", state.wasHidden);
|
|
446
|
+
else panel.hidden = state.wasHidden;
|
|
447
|
+
panel.style.left = "";
|
|
448
|
+
panel.style.top = "";
|
|
449
|
+
panel.style.width = "";
|
|
450
|
+
panel.style.maxHeight = "";
|
|
451
|
+
delete panel.dataset.side;
|
|
452
|
+
if (state.placeholder.parentNode) {
|
|
453
|
+
state.placeholder.parentNode.insertBefore(panel, state.placeholder);
|
|
454
|
+
state.placeholder.remove();
|
|
455
|
+
}
|
|
456
|
+
if (openPanels.size === 0) {
|
|
457
|
+
window.removeEventListener("resize", onWindowChange);
|
|
458
|
+
window.removeEventListener("scroll", onWindowChange, true);
|
|
459
|
+
document.removeEventListener("pointerdown", onOutsidePointerDown, true);
|
|
460
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
461
|
+
}
|
|
462
|
+
state.onClose?.();
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// src/behaviors/kb-citation.ts
|
|
466
|
+
var KB_STALE_DAYS = 90;
|
|
467
|
+
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>';
|
|
468
|
+
var DAY_MS = 864e5;
|
|
469
|
+
var HOVER_DELAY_MS = 300;
|
|
470
|
+
var CITE_TEXT_MAX = 150;
|
|
471
|
+
function isKbCitationStale(citation, now = /* @__PURE__ */ new Date()) {
|
|
472
|
+
if (!citation.date) return false;
|
|
473
|
+
const t = Date.parse(citation.date);
|
|
474
|
+
if (!Number.isFinite(t)) return false;
|
|
475
|
+
return now.getTime() - t > KB_STALE_DAYS * DAY_MS;
|
|
476
|
+
}
|
|
477
|
+
function kbDomainOf(url) {
|
|
478
|
+
if (!url) return "";
|
|
479
|
+
try {
|
|
480
|
+
return new URL(url).hostname;
|
|
481
|
+
} catch {
|
|
482
|
+
return "";
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
var registrySources = [];
|
|
486
|
+
function setKbCitationSources(sources) {
|
|
487
|
+
registrySources = Array.isArray(sources) ? sources.map(normalizeCitation) : [];
|
|
488
|
+
}
|
|
489
|
+
function getKbCitationSources() {
|
|
490
|
+
return registrySources.slice();
|
|
491
|
+
}
|
|
492
|
+
function lookupCitation(pool, n) {
|
|
493
|
+
if (!pool.length) return void 0;
|
|
494
|
+
const byIndex = pool.find((c) => c.index === n);
|
|
495
|
+
if (byIndex) return byIndex;
|
|
496
|
+
return n >= 1 && n <= pool.length ? pool[n - 1] : void 0;
|
|
497
|
+
}
|
|
498
|
+
function syntheticCitation(n) {
|
|
499
|
+
return normalizeCitation({ id: `cite-${n}`, index: n, title: `\u6765\u6E90 ${n}` });
|
|
500
|
+
}
|
|
501
|
+
function clip(text, max) {
|
|
502
|
+
return text.length > max ? `${text.slice(0, max)}\u2026` : text;
|
|
503
|
+
}
|
|
504
|
+
function renderCitationMark(n) {
|
|
505
|
+
if (typeof document === "undefined") return null;
|
|
506
|
+
const btn = h("button", "kb-citation", String(n));
|
|
507
|
+
btn.type = "button";
|
|
508
|
+
btn.dataset.cite = String(n);
|
|
509
|
+
btn.setAttribute("aria-label", `\u6765\u6E90 ${n}`);
|
|
510
|
+
return btn;
|
|
511
|
+
}
|
|
512
|
+
function citationMark(n, pool) {
|
|
513
|
+
if (pool && !lookupCitation(pool, n)) {
|
|
514
|
+
const sup = h("sup", "kb-citation is-dead", String(n));
|
|
515
|
+
sup.dataset.cite = String(n);
|
|
516
|
+
sup.setAttribute("aria-hidden", "true");
|
|
517
|
+
return sup;
|
|
518
|
+
}
|
|
519
|
+
return renderCitationMark(n) ?? h("sup", "kb-citation is-dead", String(n));
|
|
520
|
+
}
|
|
521
|
+
function renderCitationText(el, text, sources) {
|
|
522
|
+
if (typeof document === "undefined") return el;
|
|
523
|
+
const pool = sources && sources.length ? sources.map(normalizeCitation) : void 0;
|
|
524
|
+
if (pool) setKbCitationSources(pool);
|
|
525
|
+
el.textContent = "";
|
|
526
|
+
for (const seg of parseInlineCitations(text ?? "")) {
|
|
527
|
+
if (typeof seg.n === "number") el.appendChild(citationMark(seg.n, pool));
|
|
528
|
+
else if (seg.text) el.appendChild(document.createTextNode(seg.text));
|
|
529
|
+
}
|
|
530
|
+
return el;
|
|
531
|
+
}
|
|
532
|
+
function buildBadgeRow(citation, now) {
|
|
533
|
+
const row = h("div", "kb-citation-card-badges");
|
|
534
|
+
if (citation.official) row.appendChild(h("span", "kb-badge kb-badge--official", "\u5B98\u65B9"));
|
|
535
|
+
if (citation.date) {
|
|
536
|
+
const b = h("span", "kb-badge", relativeTime(citation.date, now));
|
|
537
|
+
if (isKbCitationStale(citation, now)) {
|
|
538
|
+
b.classList.add("kb-badge--stale");
|
|
539
|
+
b.prepend(h("i", "kb-dot is-warn"));
|
|
540
|
+
}
|
|
541
|
+
row.appendChild(b);
|
|
542
|
+
}
|
|
543
|
+
if (citation.permission !== "readable") {
|
|
544
|
+
const lock = h("span", "kb-badge kb-badge--restricted", "\u53D7\u9650");
|
|
545
|
+
const svg2 = svgIcon(KB_ICON_LOCK);
|
|
546
|
+
if (svg2) lock.prepend(svg2);
|
|
547
|
+
row.appendChild(lock);
|
|
548
|
+
}
|
|
549
|
+
return row;
|
|
550
|
+
}
|
|
551
|
+
function buildCitationCard(citation, now) {
|
|
552
|
+
const card = h("div", "kb-citation-card");
|
|
553
|
+
card.setAttribute("role", "tooltip");
|
|
554
|
+
const revoked = citation.revoked === true;
|
|
555
|
+
const head = h("div", "kb-citation-card-head");
|
|
556
|
+
const icon = h("span", "kb-citation-card-icon");
|
|
557
|
+
const svg2 = svgIcon(getKbSourceType(citation.kind).icon);
|
|
558
|
+
if (svg2) icon.appendChild(svg2);
|
|
559
|
+
head.appendChild(icon);
|
|
560
|
+
head.appendChild(h("span", "kb-citation-card-title", citation.title));
|
|
561
|
+
if (revoked) head.appendChild(h("span", "kb-chip is-revoked", "\u6765\u6E90\u5DF2\u4E0D\u53EF\u7528"));
|
|
562
|
+
card.appendChild(head);
|
|
563
|
+
const domain = kbDomainOf(citation.url) || getKbSourceType(citation.kind).label;
|
|
564
|
+
card.appendChild(h("div", "kb-citation-card-domain", domain));
|
|
565
|
+
if (!revoked) {
|
|
566
|
+
const quoteText = clip(citation.citedText ?? citation.snippet ?? "", CITE_TEXT_MAX);
|
|
567
|
+
if (quoteText) {
|
|
568
|
+
const quote = h("div", "kb-quote kb-citation-card-quote", quoteText);
|
|
569
|
+
if (citation.permission !== "readable") quote.classList.add("kb-citation-card-masked");
|
|
570
|
+
card.appendChild(quote);
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
const badges = buildBadgeRow(citation, now);
|
|
574
|
+
if (badges.childElementCount > 0) card.appendChild(badges);
|
|
575
|
+
return card;
|
|
576
|
+
}
|
|
577
|
+
var hover = { timer: null, panel: null, mark: null };
|
|
578
|
+
function cancelHoverTimer() {
|
|
579
|
+
if (hover.timer) {
|
|
580
|
+
clearTimeout(hover.timer);
|
|
581
|
+
hover.timer = null;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
function closeHoverCard() {
|
|
585
|
+
cancelHoverTimer();
|
|
586
|
+
hover.mark = null;
|
|
587
|
+
if (hover.panel) {
|
|
588
|
+
const panel = hover.panel;
|
|
589
|
+
hover.panel = null;
|
|
590
|
+
closePopover(panel);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
var initedScopes = /* @__PURE__ */ new WeakSet();
|
|
594
|
+
function initKbCitation(root, opts) {
|
|
595
|
+
if (typeof document === "undefined") return () => void 0;
|
|
596
|
+
const scope = root ?? document;
|
|
597
|
+
const key = scope;
|
|
598
|
+
if (initedScopes.has(key)) return () => void 0;
|
|
599
|
+
initedScopes.add(key);
|
|
600
|
+
const target = scope;
|
|
601
|
+
const poolOf = () => opts?.sources && opts.sources.length ? opts.sources : registrySources;
|
|
602
|
+
const cardDataFor = (n) => {
|
|
603
|
+
const found = lookupCitation(poolOf(), n) ?? null;
|
|
604
|
+
if (opts?.getCard) return opts.getCard(found ?? syntheticCitation(n)) ?? found;
|
|
605
|
+
return found;
|
|
606
|
+
};
|
|
607
|
+
const citeNum = (mark) => {
|
|
608
|
+
const n = Number(mark.getAttribute("data-cite"));
|
|
609
|
+
return Number.isFinite(n) && n >= 1 ? n : 0;
|
|
610
|
+
};
|
|
611
|
+
const showCard = (mark) => {
|
|
612
|
+
const n = citeNum(mark);
|
|
613
|
+
if (n < 1) return;
|
|
614
|
+
const data = cardDataFor(n);
|
|
615
|
+
if (!data) return;
|
|
616
|
+
const panel = buildCitationCard(data, /* @__PURE__ */ new Date());
|
|
617
|
+
hover.panel = panel;
|
|
618
|
+
openPopover(panel, {
|
|
619
|
+
anchor: mark,
|
|
620
|
+
side: "bottom",
|
|
621
|
+
align: "start",
|
|
622
|
+
sizing: { minWidth: 280, maxWidth: 340, maxHeight: 360 },
|
|
623
|
+
onClose: () => {
|
|
624
|
+
panel.remove();
|
|
625
|
+
if (hover.panel === panel) hover.panel = null;
|
|
626
|
+
}
|
|
627
|
+
});
|
|
628
|
+
};
|
|
629
|
+
const onClick = (e) => {
|
|
630
|
+
const t = e.target instanceof Element ? e.target : null;
|
|
631
|
+
const mark = t?.closest(".kb-citation");
|
|
632
|
+
if (!mark || mark.classList.contains("is-dead")) return;
|
|
633
|
+
const n = citeNum(mark);
|
|
634
|
+
if (n < 1) return;
|
|
635
|
+
closeHoverCard();
|
|
636
|
+
const citation = cardDataFor(n) ?? lookupCitation(poolOf(), n) ?? syntheticCitation(n);
|
|
637
|
+
emitIcen(mark, "icen:kb-citation-open", { citation });
|
|
638
|
+
};
|
|
639
|
+
const onPointerOver = (e) => {
|
|
640
|
+
const t = e.target instanceof Element ? e.target : null;
|
|
641
|
+
const mark = t?.closest(".kb-citation:not(.is-dead)");
|
|
642
|
+
if (!mark || hover.mark === mark) return;
|
|
643
|
+
closeHoverCard();
|
|
644
|
+
hover.mark = mark;
|
|
645
|
+
hover.timer = setTimeout(() => {
|
|
646
|
+
hover.timer = null;
|
|
647
|
+
if (hover.mark === mark && mark.isConnected) showCard(mark);
|
|
648
|
+
}, opts?.hoverDelay ?? HOVER_DELAY_MS);
|
|
649
|
+
};
|
|
650
|
+
const onPointerOut = (e) => {
|
|
651
|
+
if (!hover.mark) return;
|
|
652
|
+
const rt = e.relatedTarget;
|
|
653
|
+
if (rt instanceof Node && hover.mark.contains(rt)) return;
|
|
654
|
+
closeHoverCard();
|
|
655
|
+
};
|
|
656
|
+
target.addEventListener("click", onClick);
|
|
657
|
+
target.addEventListener("pointerover", onPointerOver);
|
|
658
|
+
target.addEventListener("pointerout", onPointerOut);
|
|
659
|
+
return () => {
|
|
660
|
+
target.removeEventListener("click", onClick);
|
|
661
|
+
target.removeEventListener("pointerover", onPointerOver);
|
|
662
|
+
target.removeEventListener("pointerout", onPointerOut);
|
|
663
|
+
initedScopes.delete(key);
|
|
664
|
+
if (hover.mark && scope instanceof Node && scope.contains(hover.mark)) closeHoverCard();
|
|
665
|
+
};
|
|
666
|
+
}
|
|
667
|
+
export {
|
|
668
|
+
KB_ICON_LOCK,
|
|
669
|
+
KB_STALE_DAYS,
|
|
670
|
+
getKbCitationSources,
|
|
671
|
+
initKbCitation,
|
|
672
|
+
isKbCitationStale,
|
|
673
|
+
kbDomainOf,
|
|
674
|
+
renderCitationMark,
|
|
675
|
+
renderCitationText,
|
|
676
|
+
setKbCitationSources
|
|
677
|
+
};
|