@icen.ai/ui 0.8.0 → 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 +63 -14
- package/README.md +17 -9
- package/dist/base.css +4 -3
- package/dist/behaviors/accordion.d.mts +5 -2
- package/dist/behaviors/accordion.mjs +36 -11
- package/dist/behaviors/ai-branch.d.mts +19 -0
- package/dist/behaviors/ai-branch.mjs +217 -0
- package/dist/behaviors/ai-chat.d.mts +11 -7
- package/dist/behaviors/ai-chat.mjs +134 -51
- package/dist/behaviors/ai-composer.d.mts +28 -3
- package/dist/behaviors/ai-composer.mjs +399 -229
- package/dist/behaviors/ai-core.d.mts +9 -4
- package/dist/behaviors/ai-core.mjs +21 -8
- package/dist/behaviors/ai-diff.d.mts +2 -2
- package/dist/behaviors/ai-diff.mjs +172 -41
- package/dist/behaviors/ai-feedback.d.mts +26 -0
- package/dist/behaviors/ai-feedback.mjs +303 -0
- package/dist/behaviors/ai-panel.d.mts +26 -9
- package/dist/behaviors/ai-panel.mjs +211 -110
- package/dist/behaviors/ai-provider.mjs +54 -48
- package/dist/behaviors/ai-threads.d.mts +38 -0
- package/dist/behaviors/ai-threads.mjs +319 -0
- package/dist/behaviors/ai-tool.d.mts +12 -4
- package/dist/behaviors/ai-tool.mjs +316 -263
- package/dist/behaviors/ai-tools.d.mts +9 -6
- package/dist/behaviors/ai-tools.mjs +264 -155
- package/dist/behaviors/back-top.mjs +16 -1
- package/dist/behaviors/carousel.d.mts +5 -2
- package/dist/behaviors/carousel.mjs +53 -26
- package/dist/behaviors/charts.d.mts +170 -22
- package/dist/behaviors/charts.mjs +1016 -150
- package/dist/behaviors/command-palette.d.mts +40 -6
- package/dist/behaviors/command-palette.mjs +143 -15
- package/dist/behaviors/context-menu.d.mts +21 -4
- package/dist/behaviors/context-menu.mjs +122 -38
- package/dist/behaviors/controls.d.mts +59 -0
- package/dist/behaviors/controls.mjs +601 -0
- package/dist/behaviors/copy.d.mts +5 -2
- package/dist/behaviors/copy.mjs +37 -12
- package/dist/behaviors/datatable.d.mts +12 -0
- package/dist/behaviors/datatable.mjs +226 -43
- package/dist/behaviors/date-picker.d.mts +2 -2
- package/dist/behaviors/date-picker.mjs +171 -32
- package/dist/behaviors/dropdown.d.mts +6 -2
- package/dist/behaviors/dropdown.mjs +164 -25
- package/dist/behaviors/events.d.mts +523 -0
- package/dist/behaviors/events.mjs +208 -0
- package/dist/behaviors/input.mjs +25 -12
- 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/behaviors/modal.d.mts +24 -5
- package/dist/behaviors/modal.mjs +76 -10
- package/dist/behaviors/nav.d.mts +1 -1
- package/dist/behaviors/nav.mjs +33 -7
- package/dist/behaviors/notification.d.mts +20 -4
- package/dist/behaviors/notification.mjs +53 -73
- package/dist/behaviors/popover.d.mts +39 -3
- package/dist/behaviors/popover.mjs +90 -4
- package/dist/behaviors/select.d.mts +4 -2
- package/dist/behaviors/select.mjs +161 -24
- package/dist/behaviors/sidebar.d.mts +5 -2
- package/dist/behaviors/sidebar.mjs +43 -13
- package/dist/behaviors/slider.d.mts +5 -2
- package/dist/behaviors/slider.mjs +54 -43
- package/dist/behaviors/split-pane.d.mts +6 -2
- package/dist/behaviors/split-pane.mjs +62 -21
- package/dist/behaviors/tabs.d.mts +3 -2
- package/dist/behaviors/tabs.mjs +65 -14
- package/dist/behaviors/tag-input.d.mts +5 -2
- package/dist/behaviors/tag-input.mjs +61 -21
- package/dist/behaviors/theme.d.mts +13 -2
- package/dist/behaviors/theme.mjs +48 -4
- package/dist/behaviors/toast.d.mts +30 -8
- package/dist/behaviors/toast.mjs +74 -7
- package/dist/behaviors/tree.d.mts +5 -2
- package/dist/behaviors/tree.mjs +38 -15
- package/dist/behaviors/upload.mjs +21 -23
- package/dist/cli.mjs +1 -1
- 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 +218 -1
- package/dist/components/ai-threads.mjs +3 -0
- package/dist/components/ai-tool-call.mjs +1 -1
- package/dist/components/badge.css +1 -1
- package/dist/components/breadcrumb.css +0 -32
- package/dist/components/chart-calendar.mjs +1 -2
- 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-pie.css +0 -6
- package/dist/components/chart-scatter.mjs +1 -1
- package/dist/components/charts.css +33 -1
- package/dist/components/charts.mjs +1 -0
- package/dist/components/content.css +3 -16
- package/dist/components/datatable.css +1 -1
- package/dist/components/date-picker.css +1 -1
- 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/components/notification.css +2 -3
- package/dist/components/pagination.css +3 -1
- package/dist/components/pagination.mjs +1 -0
- package/dist/components/rating.mjs +1 -0
- package/dist/components/segmented.css +9 -1
- package/dist/components/segmented.mjs +1 -1
- package/dist/components/select.css +67 -4
- package/dist/components/steps.css +13 -3
- package/dist/components/steps.mjs +1 -0
- package/dist/components/switch.css +3 -1
- package/dist/components/switch.mjs +1 -0
- package/dist/components/table.mjs +1 -0
- package/dist/components/tabs.css +23 -0
- package/dist/components/tag-input.css +1 -6
- package/dist/components/upload.css +0 -15
- package/dist/index.d.mts +48 -11
- package/dist/index.mjs +13205 -3615
- package/dist/registry.json +366 -15
- package/dist/retro-effects.css +3 -16
- package/dist/tokens/retro-effects.css +3 -16
- package/dist/tokens/style-profiles.css +3 -2
- package/dist/tokens/typography.css +2 -0
- package/dist/tokens.css +5 -2
- package/dist/ui.css +5713 -267
- 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/ai-native.md +7 -4
- package/docs/spec/kb-family.md +443 -0
- package/package.json +2 -2
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
type AiStatus = 'pending' | 'running' | 'streaming' | 'approval' | 'done' | 'error' | 'cancelled';
|
|
2
|
+
/** 状态机七态(规格 §1 的唯一语言)——集群共享(切 is-* 类 / 校验 wire status 用) */
|
|
3
|
+
declare const AI_STATUSES: readonly AiStatus[];
|
|
2
4
|
/** 中文状态标签:等待/执行中/流式中/待确认/完成/失败/已取消 */
|
|
3
5
|
declare function aiStatusLabel(s: AiStatus): string;
|
|
4
6
|
interface AiUsage {
|
|
@@ -9,6 +11,8 @@ interface AiUsage {
|
|
|
9
11
|
reasoning?: number;
|
|
10
12
|
total?: number;
|
|
11
13
|
}
|
|
14
|
+
/** 六键用量求和(非破坏性:不动入参,返回新对象);语义对齐 ai-provider 的同名私有实现,集群共用 */
|
|
15
|
+
declare function addUsage(sum: AiUsage, u: AiUsage): AiUsage;
|
|
12
16
|
type AiKindTint = 'accent' | 'success' | 'warning' | 'error' | 'info' | 'muted';
|
|
13
17
|
interface AiKindDef {
|
|
14
18
|
/** 一行中文名(tool-call 卡片的标题区展示) */
|
|
@@ -39,7 +43,7 @@ interface AiToolCallModel {
|
|
|
39
43
|
}
|
|
40
44
|
/** 注册(或覆盖)一个 kind;name 大小写不敏感。第三方扩展入口。 */
|
|
41
45
|
declare function registerAiKind(name: string, def: AiKindDef): void;
|
|
42
|
-
/** 取 kind
|
|
46
|
+
/** 取 kind 定义;未注册回退内置 'note'。 */
|
|
43
47
|
declare function getAiKind(name: string): AiKindDef;
|
|
44
48
|
/** 从 tool 名推断 kind:Bash/Shell→shell、Read→read、mcp__*→mcp 等。 */
|
|
45
49
|
declare function inferKind(toolName: string): string;
|
|
@@ -114,9 +118,8 @@ interface AiResourceLinkPart {
|
|
|
114
118
|
type AiContentPart = AiTextPart | AiMediaPart | AiResourceLinkPart;
|
|
115
119
|
/** 消息与工具回执的内容:字符串(纯文本捷径)或部件数组(多模态) */
|
|
116
120
|
type AiContent = string | AiContentPart[];
|
|
121
|
+
/** 单部件守卫:type 合法且槽位类型正确(text 看 text、resource-link 看 uri、媒体看 url 或 data+mimeType) */
|
|
117
122
|
declare function isAiContentPart(v: unknown): v is AiContentPart;
|
|
118
|
-
/** 数组且至少一项像内容部件 → 视为部件数组(tool output 多模态探测用) */
|
|
119
|
-
declare function isAiContentPartArray(v: unknown): v is AiContentPart[];
|
|
120
123
|
/** 渲染地址归一:url 直取;data + mimeType 拼 data URI(base64) */
|
|
121
124
|
declare function aiContentUrl(part: AiContentPart): string | undefined;
|
|
122
125
|
/**
|
|
@@ -140,6 +143,8 @@ declare function estimateTokens(content: AiContent | AiUsage): number;
|
|
|
140
143
|
* 本次输出会成为下轮输入的一部分。与 auditor.summary()(累计计费)语义不同。
|
|
141
144
|
*/
|
|
142
145
|
declare function contextEstimate(usage: AiUsage): number;
|
|
146
|
+
/** 极简 DOM 构件:createElement + 可选 className + 可选 textContent —— 集群共用,免四处 createElement 三连 */
|
|
147
|
+
declare function h<K extends keyof HTMLElementTagNameMap>(tag: K, className?: string, text?: string): HTMLElementTagNameMap[K];
|
|
143
148
|
/**
|
|
144
149
|
* 把内联 SVG 字符串解析为消毒后的 SVGElement:
|
|
145
150
|
* - 剥全部 on* 事件属性;剔除 script / foreignObject 子树
|
|
@@ -148,4 +153,4 @@ declare function contextEstimate(usage: AiUsage): number;
|
|
|
148
153
|
*/
|
|
149
154
|
declare function svgIcon(svg: string): SVGElement | null;
|
|
150
155
|
|
|
151
|
-
export { type AiAudioPart, type AiContent, type AiContentPart, type AiFilePart, type AiImagePart, type AiKindDef, type AiKindTint, type AiMediaPart, type AiResourceLinkPart, type AiStatus, type AiTextPart, type AiToolCallModel, type AiUsage, type AiVideoPart, aiContentUrl, aiStatusLabel, contentToText, contextEstimate, estimateTokens, formatDuration, formatTokens, getAiKind, inferKind, isAiContentPart,
|
|
156
|
+
export { AI_STATUSES, type AiAudioPart, type AiContent, type AiContentPart, type AiFilePart, type AiImagePart, type AiKindDef, type AiKindTint, type AiMediaPart, type AiResourceLinkPart, type AiStatus, type AiTextPart, type AiToolCallModel, type AiUsage, type AiVideoPart, addUsage, aiContentUrl, aiStatusLabel, contentToText, contextEstimate, estimateTokens, formatDuration, formatTokens, getAiKind, h, inferKind, isAiContentPart, normalizeContentParts, normalizeMcpContent, normalizeToolCall, normalizeUsage, registerAiKind, svgIcon };
|
|
@@ -23,6 +23,15 @@ function aiStatusLabel(s) {
|
|
|
23
23
|
};
|
|
24
24
|
return labels[s] ?? s;
|
|
25
25
|
}
|
|
26
|
+
var USAGE_KEYS = ["input", "output", "cacheRead", "cacheWrite", "reasoning", "total"];
|
|
27
|
+
function addUsage(sum2, u) {
|
|
28
|
+
const out = { ...sum2 };
|
|
29
|
+
for (const k of USAGE_KEYS) {
|
|
30
|
+
const v = u[k];
|
|
31
|
+
if (typeof v === "number" && Number.isFinite(v)) out[k] = (out[k] ?? 0) + v;
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
26
35
|
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>`;
|
|
27
36
|
var ICON_TERMINAL = svg('<path d="m4 17 6-6-6-6"/><path d="M12 19h8"/>');
|
|
28
37
|
var ICON_FILE = svg(
|
|
@@ -147,14 +156,13 @@ var BUILTIN_KINDS = {
|
|
|
147
156
|
note: { label: "\u7B14\u8BB0", icon: ICON_FILE_TEXT, tint: "muted" },
|
|
148
157
|
chart: { label: "\u56FE\u8868", icon: ICON_CHART, tint: "info", summarize: sum((i) => pickStr(i, ["title", "name"])) }
|
|
149
158
|
};
|
|
150
|
-
var FALLBACK_KIND = { label: "\u7B14\u8BB0", icon: ICON_FILE_TEXT, tint: "muted" };
|
|
151
159
|
var kindRegistry = new Map(Object.entries(BUILTIN_KINDS));
|
|
152
160
|
function registerAiKind(name, def) {
|
|
153
161
|
const key = name.trim().toLowerCase();
|
|
154
162
|
if (key) kindRegistry.set(key, def);
|
|
155
163
|
}
|
|
156
164
|
function getAiKind(name) {
|
|
157
|
-
return kindRegistry.get(name.trim().toLowerCase()) ??
|
|
165
|
+
return kindRegistry.get(name.trim().toLowerCase()) ?? BUILTIN_KINDS.note;
|
|
158
166
|
}
|
|
159
167
|
var KIND_RULES = [
|
|
160
168
|
[/^mcp__/, "mcp"],
|
|
@@ -280,9 +288,9 @@ function formatDuration(ms) {
|
|
|
280
288
|
const m = Math.floor(ms / 6e4);
|
|
281
289
|
const s = Math.round(ms % 6e4 / 1e3);
|
|
282
290
|
if (ms < 36e5) return s > 0 ? `${m}m${s}s` : `${m}m`;
|
|
283
|
-
const
|
|
291
|
+
const h2 = Math.floor(ms / 36e5);
|
|
284
292
|
const rm = Math.round(ms % 36e5 / 6e4);
|
|
285
|
-
return rm > 0 ? `${
|
|
293
|
+
return rm > 0 ? `${h2}h${rm}m` : `${h2}h`;
|
|
286
294
|
}
|
|
287
295
|
var PART_TYPES = /* @__PURE__ */ new Set(["text", "image", "audio", "video", "file", "resource-link"]);
|
|
288
296
|
function isAiContentPart(v) {
|
|
@@ -293,9 +301,6 @@ function isAiContentPart(v) {
|
|
|
293
301
|
if (t === "resource-link") return typeof v.uri === "string";
|
|
294
302
|
return typeof v.url === "string" || typeof v.data === "string" && typeof v.mimeType === "string";
|
|
295
303
|
}
|
|
296
|
-
function isAiContentPartArray(v) {
|
|
297
|
-
return Array.isArray(v) && v.length > 0 && v.every(isAiContentPart);
|
|
298
|
-
}
|
|
299
304
|
function aiContentUrl(part) {
|
|
300
305
|
if (part.type === "text" || part.type === "resource-link") return void 0;
|
|
301
306
|
if (typeof part.url === "string" && part.url) return part.url;
|
|
@@ -438,6 +443,12 @@ function contextEstimate(usage) {
|
|
|
438
443
|
const u = normalizeUsage(usage);
|
|
439
444
|
return (u.input ?? 0) + (u.cacheRead ?? 0) + (u.cacheWrite ?? 0) + (u.output ?? 0) + (u.reasoning ?? 0);
|
|
440
445
|
}
|
|
446
|
+
function h(tag, className, text) {
|
|
447
|
+
const node = document.createElement(tag);
|
|
448
|
+
if (className) node.className = className;
|
|
449
|
+
if (text != null) node.textContent = text;
|
|
450
|
+
return node;
|
|
451
|
+
}
|
|
441
452
|
function svgIcon(svg2) {
|
|
442
453
|
if (typeof document === "undefined" || typeof DOMParser === "undefined") return null;
|
|
443
454
|
let parsed;
|
|
@@ -471,6 +482,8 @@ function svgIcon(svg2) {
|
|
|
471
482
|
return document.importNode(root, true);
|
|
472
483
|
}
|
|
473
484
|
export {
|
|
485
|
+
AI_STATUSES,
|
|
486
|
+
addUsage,
|
|
474
487
|
aiContentUrl,
|
|
475
488
|
aiStatusLabel,
|
|
476
489
|
contentToText,
|
|
@@ -479,9 +492,9 @@ export {
|
|
|
479
492
|
formatDuration,
|
|
480
493
|
formatTokens,
|
|
481
494
|
getAiKind,
|
|
495
|
+
h,
|
|
482
496
|
inferKind,
|
|
483
497
|
isAiContentPart,
|
|
484
|
-
isAiContentPartArray,
|
|
485
498
|
normalizeContentParts,
|
|
486
499
|
normalizeMcpContent,
|
|
487
500
|
normalizeToolCall,
|
|
@@ -40,10 +40,10 @@ interface AiDiffModel {
|
|
|
40
40
|
*/
|
|
41
41
|
declare function parseUnifiedDiff(text: string): AiDiffFile[];
|
|
42
42
|
/**
|
|
43
|
-
* 渲染 diff 审阅卡(DOM API,全 textContent
|
|
43
|
+
* 渲染 diff 审阅卡(DOM API,全 textContent),返回挂载元素 el(render* 约定)。
|
|
44
44
|
* 接受 parseUnifiedDiff 的产物;重复调用会替换 el 内容。
|
|
45
45
|
*/
|
|
46
|
-
declare function renderAiDiff(
|
|
46
|
+
declare function renderAiDiff(el: HTMLElement, model: AiDiffModel): HTMLElement;
|
|
47
47
|
/**
|
|
48
48
|
* 委托初始化:展开/折叠 + 接受/拒绝。重复调用安全;
|
|
49
49
|
* 返回销毁函数(移除监听并复位标记,可重新 init)。
|
|
@@ -1,3 +1,143 @@
|
|
|
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/events.ts
|
|
135
|
+
function emitIcen(source, type, detail) {
|
|
136
|
+
return source.dispatchEvent(
|
|
137
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
1
141
|
// src/behaviors/ai-diff.ts
|
|
2
142
|
var RE_HUNK = /^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/;
|
|
3
143
|
var RE_OLD_FILE = /^--- (.*)$/;
|
|
@@ -114,60 +254,54 @@ function parseUnifiedDiff(text) {
|
|
|
114
254
|
}
|
|
115
255
|
return files;
|
|
116
256
|
}
|
|
117
|
-
function el(tag, className, text) {
|
|
118
|
-
const node = document.createElement(tag);
|
|
119
|
-
if (className) node.className = className;
|
|
120
|
-
if (text !== void 0) node.textContent = text;
|
|
121
|
-
return node;
|
|
122
|
-
}
|
|
123
257
|
function renderLineRow(line) {
|
|
124
258
|
const tr = document.createElement("tr");
|
|
125
259
|
tr.className = `ai-diff-line--${line.type}`;
|
|
126
260
|
if (line.type === "hunk") {
|
|
127
|
-
const td =
|
|
261
|
+
const td = h("td", "", line.text);
|
|
128
262
|
td.colSpan = 3;
|
|
129
263
|
tr.appendChild(td);
|
|
130
264
|
return tr;
|
|
131
265
|
}
|
|
132
|
-
const oldTd =
|
|
266
|
+
const oldTd = h("td", "ai-diff-ln", line.oldNo === null ? "" : String(line.oldNo));
|
|
133
267
|
oldTd.setAttribute("aria-hidden", "true");
|
|
134
|
-
const newTd =
|
|
268
|
+
const newTd = h("td", "ai-diff-ln", line.newNo === null ? "" : String(line.newNo));
|
|
135
269
|
newTd.setAttribute("aria-hidden", "true");
|
|
136
270
|
tr.appendChild(oldTd);
|
|
137
271
|
tr.appendChild(newTd);
|
|
138
|
-
tr.appendChild(
|
|
272
|
+
tr.appendChild(h("td", "ai-diff-code", line.text));
|
|
139
273
|
return tr;
|
|
140
274
|
}
|
|
141
275
|
function renderFile(file) {
|
|
142
|
-
const root =
|
|
276
|
+
const root = h("div", `ai-diff-file is-${file.status}`);
|
|
143
277
|
root.dataset.path = file.path;
|
|
144
|
-
const head =
|
|
278
|
+
const head = h("div", "ai-diff-head");
|
|
145
279
|
head.setAttribute("role", "button");
|
|
146
280
|
head.tabIndex = 0;
|
|
147
281
|
head.setAttribute("aria-expanded", "false");
|
|
148
282
|
const pathText = file.status === "renamed" && file.oldPath ? `${file.oldPath} \u2192 ${file.path}` : file.path;
|
|
149
|
-
head.appendChild(
|
|
150
|
-
const stat =
|
|
151
|
-
if (file.addCount > 0) stat.appendChild(
|
|
152
|
-
if (file.delCount > 0) stat.appendChild(
|
|
283
|
+
head.appendChild(h("span", "ai-diff-path", pathText));
|
|
284
|
+
const stat = h("span", "ai-diff-stat");
|
|
285
|
+
if (file.addCount > 0) stat.appendChild(h("b", "ai-diff-add", `+${file.addCount}`));
|
|
286
|
+
if (file.delCount > 0) stat.appendChild(h("b", "ai-diff-del", `\u2212${file.delCount}`));
|
|
153
287
|
if (stat.children.length > 0) head.appendChild(stat);
|
|
154
|
-
const actions =
|
|
155
|
-
actions.appendChild(
|
|
156
|
-
const rejectBtn =
|
|
288
|
+
const actions = h("span", "ai-diff-actions");
|
|
289
|
+
actions.appendChild(h("button", "", "\u63A5\u53D7")).setAttribute("data-ai-diff-accept", "");
|
|
290
|
+
const rejectBtn = h("button", "", "\u62D2\u7EDD");
|
|
157
291
|
rejectBtn.setAttribute("data-ai-diff-reject", "");
|
|
158
292
|
actions.appendChild(rejectBtn);
|
|
159
293
|
head.appendChild(actions);
|
|
160
|
-
const verdict =
|
|
294
|
+
const verdict = h("span", "ai-diff-verdict");
|
|
161
295
|
verdict.hidden = true;
|
|
162
296
|
head.appendChild(verdict);
|
|
163
297
|
root.appendChild(head);
|
|
164
|
-
const body =
|
|
298
|
+
const body = h("div", "ai-diff-body");
|
|
165
299
|
body.hidden = true;
|
|
166
|
-
const table =
|
|
300
|
+
const table = h("table", "ai-diff-table");
|
|
167
301
|
if (file.hunks.length === 0) {
|
|
168
302
|
const tr = document.createElement("tr");
|
|
169
303
|
tr.className = "ai-diff-line--ctx";
|
|
170
|
-
const td =
|
|
304
|
+
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");
|
|
171
305
|
td.colSpan = 3;
|
|
172
306
|
tr.appendChild(td);
|
|
173
307
|
table.appendChild(tr);
|
|
@@ -181,13 +315,14 @@ function renderFile(file) {
|
|
|
181
315
|
root.appendChild(body);
|
|
182
316
|
return root;
|
|
183
317
|
}
|
|
184
|
-
function renderAiDiff(
|
|
185
|
-
if (typeof document === "undefined") return;
|
|
186
|
-
|
|
187
|
-
const root =
|
|
318
|
+
function renderAiDiff(el, model) {
|
|
319
|
+
if (typeof document === "undefined") return el;
|
|
320
|
+
el.textContent = "";
|
|
321
|
+
const root = h("div", "ai-diff");
|
|
188
322
|
const files = Array.isArray(model?.files) ? model.files : [];
|
|
189
323
|
for (const file of files) root.appendChild(renderFile(file));
|
|
190
|
-
|
|
324
|
+
el.appendChild(root);
|
|
325
|
+
return el;
|
|
191
326
|
}
|
|
192
327
|
var noopDestroy = () => void 0;
|
|
193
328
|
function closestFile(node) {
|
|
@@ -206,10 +341,7 @@ function toggleFile(head) {
|
|
|
206
341
|
const open = body.hidden;
|
|
207
342
|
body.hidden = !open;
|
|
208
343
|
head.setAttribute("aria-expanded", String(open));
|
|
209
|
-
file
|
|
210
|
-
bubbles: true,
|
|
211
|
-
detail: { el: file, open }
|
|
212
|
-
}));
|
|
344
|
+
emitIcen(file, "icen:ai-toggle", { el: file, open });
|
|
213
345
|
}
|
|
214
346
|
function decide(btn, accepted) {
|
|
215
347
|
const file = closestFile(btn);
|
|
@@ -218,7 +350,7 @@ function decide(btn, accepted) {
|
|
|
218
350
|
file.classList.add(accepted ? "is-accepted" : "is-rejected");
|
|
219
351
|
let verdict = file.querySelector(".ai-diff-verdict");
|
|
220
352
|
if (!verdict) {
|
|
221
|
-
verdict =
|
|
353
|
+
verdict = h("span", "ai-diff-verdict");
|
|
222
354
|
file.querySelector(".ai-diff-head")?.appendChild(verdict);
|
|
223
355
|
}
|
|
224
356
|
verdict.textContent = accepted ? "\u5DF2\u63A5\u53D7" : "\u5DF2\u62D2\u7EDD";
|
|
@@ -229,10 +361,7 @@ function decide(btn, accepted) {
|
|
|
229
361
|
b.hidden = true;
|
|
230
362
|
b.disabled = true;
|
|
231
363
|
});
|
|
232
|
-
file
|
|
233
|
-
bubbles: true,
|
|
234
|
-
detail: { path: filePath(file) }
|
|
235
|
-
}));
|
|
364
|
+
emitIcen(file, accepted ? "icen:ai-diff-accept" : "icen:ai-diff-reject", { path: filePath(file) });
|
|
236
365
|
}
|
|
237
366
|
function initAiDiff(root) {
|
|
238
367
|
if (typeof document === "undefined") return noopDestroy;
|
|
@@ -244,14 +373,16 @@ function initAiDiff(root) {
|
|
|
244
373
|
const onClick = (e) => {
|
|
245
374
|
const t = e.target instanceof Element ? e.target : null;
|
|
246
375
|
if (!t) return;
|
|
247
|
-
|
|
376
|
+
const acceptBtn = t.closest("[data-ai-diff-accept]");
|
|
377
|
+
if (acceptBtn) {
|
|
248
378
|
e.stopPropagation();
|
|
249
|
-
decide(
|
|
379
|
+
decide(acceptBtn, true);
|
|
250
380
|
return;
|
|
251
381
|
}
|
|
252
|
-
|
|
382
|
+
const rejectBtn = t.closest("[data-ai-diff-reject]");
|
|
383
|
+
if (rejectBtn) {
|
|
253
384
|
e.stopPropagation();
|
|
254
|
-
decide(
|
|
385
|
+
decide(rejectBtn, false);
|
|
255
386
|
return;
|
|
256
387
|
}
|
|
257
388
|
const head = t.closest(".ai-diff-head");
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** 反馈事件 detail(与 IcenEventMap['icen:ai-feedback'] 同构;el 为 .ai-feedback 根) */
|
|
2
|
+
interface AiFeedbackDetail {
|
|
3
|
+
value: 1 | -1 | 0;
|
|
4
|
+
reason?: string;
|
|
5
|
+
el?: HTMLElement;
|
|
6
|
+
}
|
|
7
|
+
interface AiFeedbackOpts {
|
|
8
|
+
/** 踩因枚举(默认五项) */
|
|
9
|
+
reasons?: string[];
|
|
10
|
+
/** 反馈回调(与 icen:ai-feedback 事件双通道) */
|
|
11
|
+
onFeedback?: (detail: AiFeedbackDetail) => void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 渲染 👍/👎 双钮 + 踩因 chips + 提交/已提交态(快照渲染,全 textContent)。
|
|
15
|
+
* 交互由 initAiFeedback 委托驱动;opts.onFeedback 与 icen:ai-feedback 事件双通道。
|
|
16
|
+
* 返回挂载容器 el(render* 返回挂载元素约定,SSR 下原样返回)。
|
|
17
|
+
*/
|
|
18
|
+
declare function renderAiFeedback(el: HTMLElement, opts?: AiFeedbackOpts): HTMLElement;
|
|
19
|
+
/**
|
|
20
|
+
* 反馈委托初始化:vote 切换/取消、chip 单选、提交进入 .is-done 终态。
|
|
21
|
+
* 幂等(scope 挂 __icenAiFeedbackInit);返回销毁函数(解绑监听 + 复位标记,可重新 init)。
|
|
22
|
+
* SSR 下为 no-op。按钮为原生 button,键盘 Enter/Space 走原生 click,无需键盘委托。
|
|
23
|
+
*/
|
|
24
|
+
declare function initAiFeedback(root?: ParentNode): () => void;
|
|
25
|
+
|
|
26
|
+
export { type AiFeedbackDetail, type AiFeedbackOpts, initAiFeedback, renderAiFeedback };
|