@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,13 @@
|
|
|
1
1
|
// src/behaviors/ai-core.ts
|
|
2
|
+
var USAGE_KEYS = ["input", "output", "cacheRead", "cacheWrite", "reasoning", "total"];
|
|
3
|
+
function addUsage(sum2, u) {
|
|
4
|
+
const out = { ...sum2 };
|
|
5
|
+
for (const k of USAGE_KEYS) {
|
|
6
|
+
const v = u[k];
|
|
7
|
+
if (typeof v === "number" && Number.isFinite(v)) out[k] = (out[k] ?? 0) + v;
|
|
8
|
+
}
|
|
9
|
+
return out;
|
|
10
|
+
}
|
|
2
11
|
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
12
|
var ICON_TERMINAL = svg('<path d="m4 17 6-6-6-6"/><path d="M12 19h8"/>');
|
|
4
13
|
var ICON_FILE = svg(
|
|
@@ -258,6 +267,13 @@ function contentToText(content) {
|
|
|
258
267
|
return content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
259
268
|
}
|
|
260
269
|
|
|
270
|
+
// src/behaviors/events.ts
|
|
271
|
+
function emitIcen(source, type, detail) {
|
|
272
|
+
return source.dispatchEvent(
|
|
273
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
274
|
+
);
|
|
275
|
+
}
|
|
276
|
+
|
|
261
277
|
// src/behaviors/ai-provider.ts
|
|
262
278
|
function isObj2(v) {
|
|
263
279
|
return typeof v === "object" && v !== null;
|
|
@@ -439,15 +455,6 @@ async function readHttpError(res, provider) {
|
|
|
439
455
|
const { type, message } = extractHttpError(raw, status);
|
|
440
456
|
return new AiProviderError({ provider, status, type, message, raw });
|
|
441
457
|
}
|
|
442
|
-
var USAGE_KEYS = ["input", "output", "cacheRead", "cacheWrite", "reasoning", "total"];
|
|
443
|
-
function addUsage(sum2, u) {
|
|
444
|
-
if (!u) return sum2;
|
|
445
|
-
for (const k of USAGE_KEYS) {
|
|
446
|
-
const v = u[k];
|
|
447
|
-
if (typeof v === "number" && Number.isFinite(v)) sum2[k] = (sum2[k] ?? 0) + v;
|
|
448
|
-
}
|
|
449
|
-
return sum2;
|
|
450
|
-
}
|
|
451
458
|
function isAuditEntry(v) {
|
|
452
459
|
return isObj2(v) && typeof v.id === "string" && typeof v.ts === "number" && typeof v.provider === "string" && typeof v.model === "string" && typeof v.baseURL === "string" && typeof v.stream === "boolean" && (v.status === "ok" || v.status === "error") && typeof v.durationMs === "number";
|
|
453
460
|
}
|
|
@@ -492,7 +499,7 @@ function createAiAuditor(opts) {
|
|
|
492
499
|
},
|
|
493
500
|
summary() {
|
|
494
501
|
const sum2 = {};
|
|
495
|
-
for (const e of entries) addUsage(sum2, e.usage);
|
|
502
|
+
for (const e of entries) addUsage(sum2, e.usage ?? {});
|
|
496
503
|
return sum2;
|
|
497
504
|
},
|
|
498
505
|
totals() {
|
|
@@ -502,7 +509,7 @@ function createAiAuditor(opts) {
|
|
|
502
509
|
let ttftSum = 0;
|
|
503
510
|
let ttftN = 0;
|
|
504
511
|
for (const e of entries) {
|
|
505
|
-
addUsage(usage, e.usage);
|
|
512
|
+
addUsage(usage, e.usage ?? {});
|
|
506
513
|
if (typeof e.cost === "number") cost += e.cost;
|
|
507
514
|
if (e.status === "error") errors++;
|
|
508
515
|
if (typeof e.ttftMs === "number") {
|
|
@@ -523,7 +530,7 @@ function createAiAuditor(opts) {
|
|
|
523
530
|
const out = {};
|
|
524
531
|
for (const e of entries) {
|
|
525
532
|
const key = `${e.provider}/${e.model}`;
|
|
526
|
-
addUsage(out[key] ??= {}, e.usage);
|
|
533
|
+
addUsage(out[key] ??= {}, e.usage ?? {});
|
|
527
534
|
}
|
|
528
535
|
return out;
|
|
529
536
|
}
|
|
@@ -778,23 +785,18 @@ function joinSignals(a, b) {
|
|
|
778
785
|
return c.signal;
|
|
779
786
|
}
|
|
780
787
|
const fwd = () => {
|
|
788
|
+
a.removeEventListener("abort", fwd);
|
|
789
|
+
b.removeEventListener("abort", fwd);
|
|
781
790
|
if (!c.signal.aborted) c.abort();
|
|
782
791
|
};
|
|
783
792
|
a.addEventListener("abort", fwd, { once: true });
|
|
784
793
|
b.addEventListener("abort", fwd, { once: true });
|
|
785
794
|
return c.signal;
|
|
786
795
|
}
|
|
787
|
-
function
|
|
796
|
+
function dispatchAiEvent(name, detail) {
|
|
788
797
|
if (typeof document === "undefined") return;
|
|
789
798
|
try {
|
|
790
|
-
document
|
|
791
|
-
} catch {
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
function dispatchDoneEvent(detail) {
|
|
795
|
-
if (typeof document === "undefined") return;
|
|
796
|
-
try {
|
|
797
|
-
document.dispatchEvent(new CustomEvent("icen:ai-done", { detail, bubbles: true }));
|
|
799
|
+
emitIcen(document, name, detail);
|
|
798
800
|
} catch {
|
|
799
801
|
}
|
|
800
802
|
}
|
|
@@ -866,7 +868,31 @@ function createAiClient(options) {
|
|
|
866
868
|
return { entry, cost };
|
|
867
869
|
}
|
|
868
870
|
const publishUsage = (usage) => {
|
|
869
|
-
if (Object.keys(usage).length > 0)
|
|
871
|
+
if (Object.keys(usage).length > 0) dispatchAiEvent("icen:ai-usage", usage);
|
|
872
|
+
};
|
|
873
|
+
const finalize = (req, streaming, status, usage, error, t0, ttftMs) => {
|
|
874
|
+
const durationMs = Date.now() - t0;
|
|
875
|
+
const { cost } = audit(
|
|
876
|
+
req,
|
|
877
|
+
streaming,
|
|
878
|
+
status === "ok" ? "ok" : "error",
|
|
879
|
+
usage,
|
|
880
|
+
durationMs,
|
|
881
|
+
status === "cancelled" ? "\u8BF7\u6C42\u5DF2\u53D6\u6D88" : error,
|
|
882
|
+
ttftMs
|
|
883
|
+
);
|
|
884
|
+
dispatchAiEvent("icen:ai-done", {
|
|
885
|
+
status,
|
|
886
|
+
provider: def.id,
|
|
887
|
+
model: req.model ?? defaultModel,
|
|
888
|
+
stream: streaming,
|
|
889
|
+
usage,
|
|
890
|
+
cost,
|
|
891
|
+
error: status === "cancelled" ? void 0 : error,
|
|
892
|
+
durationMs,
|
|
893
|
+
ttftMs
|
|
894
|
+
});
|
|
895
|
+
return cost;
|
|
870
896
|
};
|
|
871
897
|
async function chat(req) {
|
|
872
898
|
const t0 = Date.now();
|
|
@@ -908,23 +934,14 @@ function createAiClient(options) {
|
|
|
908
934
|
}
|
|
909
935
|
const usage = normalizeUsage(usageRaw);
|
|
910
936
|
publishUsage(usage);
|
|
911
|
-
const
|
|
912
|
-
dispatchDoneEvent({
|
|
913
|
-
status: "ok",
|
|
914
|
-
provider: def.id,
|
|
915
|
-
model: req.model ?? defaultModel,
|
|
916
|
-
stream: false,
|
|
917
|
-
usage,
|
|
918
|
-
cost,
|
|
919
|
-
durationMs: Date.now() - t0
|
|
920
|
-
});
|
|
937
|
+
const cost = finalize(req, false, "ok", usage, void 0, t0);
|
|
921
938
|
const result = { text: outText, usage, cost, finishReason, raw: j };
|
|
922
939
|
if (outParts && outParts.length > 0) result.parts = outParts;
|
|
923
940
|
return result;
|
|
924
941
|
} catch (err) {
|
|
925
942
|
const e = normalizeThrown(err);
|
|
926
943
|
audit(req, false, "error", {}, Date.now() - t0, e.message);
|
|
927
|
-
|
|
944
|
+
dispatchAiEvent("icen:ai-done", {
|
|
928
945
|
status: e.type === "cancelled" ? "cancelled" : "error",
|
|
929
946
|
provider: def.id,
|
|
930
947
|
model: req.model ?? defaultModel,
|
|
@@ -990,26 +1007,15 @@ function createAiClient(options) {
|
|
|
990
1007
|
} finally {
|
|
991
1008
|
const usage = normalizeUsage(usageRaw);
|
|
992
1009
|
publishUsage(usage);
|
|
993
|
-
const
|
|
1010
|
+
const cost = finalize(
|
|
994
1011
|
req,
|
|
995
1012
|
true,
|
|
996
|
-
errorType === "cancelled" ? "
|
|
1013
|
+
errorType === "cancelled" ? "cancelled" : status,
|
|
997
1014
|
usage,
|
|
998
|
-
|
|
999
|
-
|
|
1015
|
+
error,
|
|
1016
|
+
t0,
|
|
1000
1017
|
ttftMs
|
|
1001
1018
|
);
|
|
1002
|
-
dispatchDoneEvent({
|
|
1003
|
-
status: errorType === "cancelled" ? "cancelled" : status,
|
|
1004
|
-
provider: def.id,
|
|
1005
|
-
model: req.model ?? defaultModel,
|
|
1006
|
-
stream: true,
|
|
1007
|
-
usage,
|
|
1008
|
-
cost,
|
|
1009
|
-
error: errorType === "cancelled" ? void 0 : error,
|
|
1010
|
-
durationMs: Date.now() - t0,
|
|
1011
|
-
ttftMs
|
|
1012
|
-
});
|
|
1013
1019
|
finished = true;
|
|
1014
1020
|
notify();
|
|
1015
1021
|
ac.abort();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
interface AiThreadItem {
|
|
2
|
+
/** 稳定标识(事件 detail 的 key) */
|
|
3
|
+
key: string;
|
|
4
|
+
/** 会话标题 */
|
|
5
|
+
label: string;
|
|
6
|
+
/** 分组名(如「今天」/「昨天」/「更早」);groups 模式下生效 */
|
|
7
|
+
group?: string;
|
|
8
|
+
/** 内联 SVG 字符串(lucide 风格 24×24 stroke);渲染时经 svgIcon() 消毒 */
|
|
9
|
+
icon?: string;
|
|
10
|
+
/** 已归档:.is-archived 降透明并沉底(分组模式收进「已归档」组) */
|
|
11
|
+
archived?: boolean;
|
|
12
|
+
}
|
|
13
|
+
interface AiThreadsModel {
|
|
14
|
+
items: AiThreadItem[];
|
|
15
|
+
/** 当前激活会话 key(路由同步) */
|
|
16
|
+
activeKey?: string;
|
|
17
|
+
/** 分组模式:true 时按 group 字段分组(组头可折叠);缺省/false 平铺 */
|
|
18
|
+
groups?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* 快照渲染会话列表:平铺或分组(model.groups)、activeKey 高亮、archived 沉底
|
|
22
|
+
* (分组模式收进「已归档」组,组头可折叠)。返回挂载容器 el(SSR 原样返回)。
|
|
23
|
+
*/
|
|
24
|
+
declare function renderAiThreads(el: HTMLElement, model: AiThreadsModel): HTMLElement;
|
|
25
|
+
/**
|
|
26
|
+
* 外部路由同步激活态:el 范围内 data-key === key 的 .ai-thread 挂 .is-active
|
|
27
|
+
* (+aria-current),其余摘除;key 为空串/null/undefined 时清空全部激活。
|
|
28
|
+
* 对静态手写 DOM 与 renderAiThreads 产物一视同仁。返回 el。
|
|
29
|
+
*/
|
|
30
|
+
declare function setAiThreadsActive(el: HTMLElement, key: string | null | undefined): HTMLElement;
|
|
31
|
+
/**
|
|
32
|
+
* 会话列表委托初始化:选择 / 动作挂载点 / 组头折叠。
|
|
33
|
+
* 幂等(scope 挂 __icenAiThreadsInit);返回销毁函数(解绑监听 + 复位标记,可重新 init)。
|
|
34
|
+
* SSR 下为 no-op。
|
|
35
|
+
*/
|
|
36
|
+
declare function initAiThreads(root?: ParentNode): () => void;
|
|
37
|
+
|
|
38
|
+
export { type AiThreadItem, type AiThreadsModel, initAiThreads, renderAiThreads, setAiThreadsActive };
|
|
@@ -0,0 +1,319 @@
|
|
|
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/events.ts
|
|
167
|
+
function emitIcen(source, type, detail) {
|
|
168
|
+
return source.dispatchEvent(
|
|
169
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// src/behaviors/ai-threads.ts
|
|
174
|
+
var ARCHIVED_GROUP = "\u5DF2\u5F52\u6863";
|
|
175
|
+
function dispatch(node, name, detail) {
|
|
176
|
+
emitIcen(node, name, detail);
|
|
177
|
+
}
|
|
178
|
+
function buildThread(item, activeKey) {
|
|
179
|
+
const row = h("div", "ai-thread");
|
|
180
|
+
if (item.key) row.dataset.key = item.key;
|
|
181
|
+
if (item.archived) row.classList.add("is-archived");
|
|
182
|
+
if (activeKey != null && item.key === activeKey) {
|
|
183
|
+
row.classList.add("is-active");
|
|
184
|
+
row.setAttribute("aria-current", "true");
|
|
185
|
+
}
|
|
186
|
+
row.setAttribute("role", "button");
|
|
187
|
+
row.tabIndex = 0;
|
|
188
|
+
if (item.icon) {
|
|
189
|
+
const iconWrap = h("span", "ai-thread-icon");
|
|
190
|
+
const svg2 = svgIcon(item.icon);
|
|
191
|
+
if (svg2) iconWrap.appendChild(svg2);
|
|
192
|
+
row.appendChild(iconWrap);
|
|
193
|
+
}
|
|
194
|
+
row.appendChild(h("span", "ai-thread-label", item.label));
|
|
195
|
+
const action = h("button", "ai-thread-action", "\xB7\xB7\xB7");
|
|
196
|
+
action.type = "button";
|
|
197
|
+
action.setAttribute("aria-label", "\u4F1A\u8BDD\u64CD\u4F5C");
|
|
198
|
+
action.setAttribute("data-thread-action", "rename");
|
|
199
|
+
row.appendChild(action);
|
|
200
|
+
return row;
|
|
201
|
+
}
|
|
202
|
+
function renderAiThreads(el, model) {
|
|
203
|
+
if (typeof document === "undefined") return el;
|
|
204
|
+
el.textContent = "";
|
|
205
|
+
const root = h("div", "ai-threads");
|
|
206
|
+
const items = Array.isArray(model.items) ? model.items : [];
|
|
207
|
+
const active = items.filter((it) => !it.archived);
|
|
208
|
+
const archived = items.filter((it) => it.archived);
|
|
209
|
+
if (model.groups) {
|
|
210
|
+
const ungrouped = active.filter((it) => !(it.group ?? "").trim());
|
|
211
|
+
for (const it of ungrouped) root.appendChild(buildThread(it, model.activeKey));
|
|
212
|
+
const groups = /* @__PURE__ */ new Map();
|
|
213
|
+
for (const it of active) {
|
|
214
|
+
const label = (it.group ?? "").trim();
|
|
215
|
+
if (!label) continue;
|
|
216
|
+
const list = groups.get(label) ?? [];
|
|
217
|
+
list.push(it);
|
|
218
|
+
groups.set(label, list);
|
|
219
|
+
}
|
|
220
|
+
for (const [label, list] of groups) {
|
|
221
|
+
const group = h("div", "ai-threads-group");
|
|
222
|
+
group.dataset.group = label;
|
|
223
|
+
const head = h("button", "ai-threads-group-head", label);
|
|
224
|
+
head.type = "button";
|
|
225
|
+
head.setAttribute("aria-expanded", "true");
|
|
226
|
+
group.appendChild(head);
|
|
227
|
+
const listEl = h("div", "ai-threads-group-list");
|
|
228
|
+
for (const it of list) listEl.appendChild(buildThread(it, model.activeKey));
|
|
229
|
+
group.appendChild(listEl);
|
|
230
|
+
root.appendChild(group);
|
|
231
|
+
}
|
|
232
|
+
if (archived.length > 0) {
|
|
233
|
+
const group = h("div", "ai-threads-group");
|
|
234
|
+
group.dataset.group = ARCHIVED_GROUP;
|
|
235
|
+
const head = h("button", "ai-threads-group-head", ARCHIVED_GROUP);
|
|
236
|
+
head.type = "button";
|
|
237
|
+
head.setAttribute("aria-expanded", "true");
|
|
238
|
+
group.appendChild(head);
|
|
239
|
+
const listEl = h("div", "ai-threads-group-list");
|
|
240
|
+
for (const it of archived) listEl.appendChild(buildThread(it, model.activeKey));
|
|
241
|
+
group.appendChild(listEl);
|
|
242
|
+
root.appendChild(group);
|
|
243
|
+
}
|
|
244
|
+
} else {
|
|
245
|
+
for (const it of active) root.appendChild(buildThread(it, model.activeKey));
|
|
246
|
+
for (const it of archived) root.appendChild(buildThread(it, model.activeKey));
|
|
247
|
+
}
|
|
248
|
+
el.appendChild(root);
|
|
249
|
+
return el;
|
|
250
|
+
}
|
|
251
|
+
function setAiThreadsActive(el, key) {
|
|
252
|
+
const threads = Array.from(el.querySelectorAll(".ai-thread"));
|
|
253
|
+
if (el instanceof HTMLElement && el.classList.contains("ai-thread")) threads.unshift(el);
|
|
254
|
+
for (const t of threads) {
|
|
255
|
+
const hit = key != null && key !== "" && t.dataset.key === key;
|
|
256
|
+
t.classList.toggle("is-active", hit);
|
|
257
|
+
if (hit) t.setAttribute("aria-current", "true");
|
|
258
|
+
else t.removeAttribute("aria-current");
|
|
259
|
+
}
|
|
260
|
+
return el;
|
|
261
|
+
}
|
|
262
|
+
function initAiThreads(root) {
|
|
263
|
+
if (typeof document === "undefined") return () => void 0;
|
|
264
|
+
const scope = root ?? document;
|
|
265
|
+
const marked = scope;
|
|
266
|
+
if (marked.__icenAiThreadsInit) return () => void 0;
|
|
267
|
+
marked.__icenAiThreadsInit = true;
|
|
268
|
+
const target = scope;
|
|
269
|
+
const selectThread = (thread) => {
|
|
270
|
+
const key = thread.dataset.key ?? "";
|
|
271
|
+
dispatch(thread, "icen:ai-thread-select", { key });
|
|
272
|
+
};
|
|
273
|
+
const toggleGroup = (head) => {
|
|
274
|
+
const group = head.closest(".ai-threads-group");
|
|
275
|
+
if (!group) return;
|
|
276
|
+
const open = head.getAttribute("aria-expanded") !== "true";
|
|
277
|
+
head.setAttribute("aria-expanded", String(open));
|
|
278
|
+
group.classList.toggle("is-collapsed", !open);
|
|
279
|
+
};
|
|
280
|
+
const onClick = (e) => {
|
|
281
|
+
const t = e.target instanceof Element ? e.target : null;
|
|
282
|
+
if (!t) return;
|
|
283
|
+
const action = t.closest(".ai-thread-action");
|
|
284
|
+
if (action) {
|
|
285
|
+
const thread2 = action.closest(".ai-thread");
|
|
286
|
+
if (!thread2) return;
|
|
287
|
+
dispatch(thread2, "icen:ai-thread-action", { action: "rename", key: thread2.dataset.key ?? "" });
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
const head = t.closest(".ai-threads-group-head");
|
|
291
|
+
if (head) {
|
|
292
|
+
toggleGroup(head);
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
const thread = t.closest(".ai-thread");
|
|
296
|
+
if (thread) selectThread(thread);
|
|
297
|
+
};
|
|
298
|
+
const onKeydown = (e) => {
|
|
299
|
+
const ke = e;
|
|
300
|
+
if (ke.key !== "Enter" && ke.key !== " ") return;
|
|
301
|
+
const t = ke.target instanceof Element ? ke.target : null;
|
|
302
|
+
const thread = t?.closest(".ai-thread");
|
|
303
|
+
if (!thread || thread.getAttribute("role") !== "button") return;
|
|
304
|
+
ke.preventDefault();
|
|
305
|
+
selectThread(thread);
|
|
306
|
+
};
|
|
307
|
+
target.addEventListener("click", onClick);
|
|
308
|
+
target.addEventListener("keydown", onKeydown);
|
|
309
|
+
return () => {
|
|
310
|
+
target.removeEventListener("click", onClick);
|
|
311
|
+
target.removeEventListener("keydown", onKeydown);
|
|
312
|
+
marked.__icenAiThreadsInit = false;
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
export {
|
|
316
|
+
initAiThreads,
|
|
317
|
+
renderAiThreads,
|
|
318
|
+
setAiThreadsActive
|
|
319
|
+
};
|
|
@@ -8,11 +8,19 @@ interface AiSubagentHandle {
|
|
|
8
8
|
el: HTMLElement;
|
|
9
9
|
update(patch: Partial<AiToolCallModel>): void;
|
|
10
10
|
}
|
|
11
|
+
/** 渲染工具调用卡(整卡挂进传入 el),返回 { el, update }——有生命周期的 render* 返句柄:update(patch) 增量重渲。 */
|
|
11
12
|
declare function renderAiToolCall(el: HTMLElement, model: AiToolCallModel): AiToolCallHandle;
|
|
13
|
+
/** 渲染子智能体卡(activities 递归复用 renderAiToolCall / 自身),返回 { el, update }——有生命周期的 render* 返句柄。 */
|
|
12
14
|
declare function renderAiSubagent(el: HTMLElement, model: AiToolCallModel): AiSubagentHandle;
|
|
13
|
-
/**
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
/**
|
|
16
|
+
* 初始化 root 下所有工具调用卡(root 自身是 .ai-tool 也算)。重复调用幂等;
|
|
17
|
+
* 返回销毁函数(复刻 initAiChat 约定:解绑全部卡片监听并复位幂等标记)。
|
|
18
|
+
*/
|
|
19
|
+
declare function initAiTool(root?: ParentNode): () => void;
|
|
20
|
+
/**
|
|
21
|
+
* 初始化 root 下所有子智能体卡(含嵌套;root 自身是 .ai-subagent 也算)。重复调用幂等;
|
|
22
|
+
* 返回销毁函数(复刻 initAiChat 约定:解绑全部卡片监听并复位幂等标记)。
|
|
23
|
+
*/
|
|
24
|
+
declare function initAiSubagent(root?: ParentNode): () => void;
|
|
17
25
|
|
|
18
26
|
export { type AiSubagentHandle, type AiToolCallHandle, initAiSubagent, initAiTool, renderAiSubagent, renderAiToolCall };
|