@icen.ai/ui 0.8.0 → 0.8.1
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 +15 -9
- package/README.md +8 -3
- 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-chat.d.mts +3 -1
- package/dist/behaviors/ai-chat.mjs +66 -43
- package/dist/behaviors/ai-composer.d.mts +28 -3
- package/dist/behaviors/ai-composer.mjs +291 -209
- 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-panel.d.mts +11 -8
- package/dist/behaviors/ai-panel.mjs +185 -110
- package/dist/behaviors/ai-provider.mjs +54 -48
- package/dist/behaviors/ai-tool.d.mts +12 -4
- package/dist/behaviors/ai-tool.mjs +311 -263
- package/dist/behaviors/ai-tools.d.mts +9 -6
- package/dist/behaviors/ai-tools.mjs +259 -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 +68 -22
- package/dist/behaviors/charts.mjs +221 -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 +330 -0
- package/dist/behaviors/events.mjs +208 -0
- package/dist/behaviors/input.mjs +25 -12
- 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-panel.css +1 -1
- 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-pie.css +0 -6
- package/dist/components/charts.css +1 -1
- 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/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 +12 -10
- package/dist/index.mjs +2700 -1460
- package/dist/registry.json +42 -13
- 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 +138 -98
- package/docs/spec/ai-native.md +7 -4
- package/package.json +1 -1
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
// src/behaviors/ai-core.ts
|
|
2
|
+
var AI_STATUSES = [
|
|
3
|
+
"pending",
|
|
4
|
+
"running",
|
|
5
|
+
"streaming",
|
|
6
|
+
"approval",
|
|
7
|
+
"done",
|
|
8
|
+
"error",
|
|
9
|
+
"cancelled"
|
|
10
|
+
];
|
|
2
11
|
function aiStatusLabel(s) {
|
|
3
12
|
const labels = {
|
|
4
13
|
pending: "\u7B49\u5F85",
|
|
@@ -171,9 +180,15 @@ function formatDuration(ms) {
|
|
|
171
180
|
const m = Math.floor(ms / 6e4);
|
|
172
181
|
const s = Math.round(ms % 6e4 / 1e3);
|
|
173
182
|
if (ms < 36e5) return s > 0 ? `${m}m${s}s` : `${m}m`;
|
|
174
|
-
const
|
|
183
|
+
const h2 = Math.floor(ms / 36e5);
|
|
175
184
|
const rm = Math.round(ms % 36e5 / 6e4);
|
|
176
|
-
return rm > 0 ? `${
|
|
185
|
+
return rm > 0 ? `${h2}h${rm}m` : `${h2}h`;
|
|
186
|
+
}
|
|
187
|
+
function h(tag, className, text) {
|
|
188
|
+
const node = document.createElement(tag);
|
|
189
|
+
if (className) node.className = className;
|
|
190
|
+
if (text != null) node.textContent = text;
|
|
191
|
+
return node;
|
|
177
192
|
}
|
|
178
193
|
function svgIcon(svg2) {
|
|
179
194
|
if (typeof document === "undefined" || typeof DOMParser === "undefined") return null;
|
|
@@ -187,15 +202,15 @@ function svgIcon(svg2) {
|
|
|
187
202
|
if (!root || root.tagName.toLowerCase() !== "svg" || parsed.querySelector("parsererror")) {
|
|
188
203
|
return null;
|
|
189
204
|
}
|
|
190
|
-
const sanitize = (
|
|
191
|
-
for (const attr of Array.from(
|
|
205
|
+
const sanitize = (el) => {
|
|
206
|
+
for (const attr of Array.from(el.attributes)) {
|
|
192
207
|
const name = attr.name.toLowerCase();
|
|
193
|
-
if (name.startsWith("on"))
|
|
208
|
+
if (name.startsWith("on")) el.removeAttribute(attr.name);
|
|
194
209
|
if ((name === "href" || name === "xlink:href") && attr.value.trim().toLowerCase().startsWith("javascript:")) {
|
|
195
|
-
|
|
210
|
+
el.removeAttribute(attr.name);
|
|
196
211
|
}
|
|
197
212
|
}
|
|
198
|
-
for (const child of Array.from(
|
|
213
|
+
for (const child of Array.from(el.children)) {
|
|
199
214
|
const tag = child.tagName.toLowerCase();
|
|
200
215
|
if (tag === "script" || tag === "foreignobject") {
|
|
201
216
|
child.remove();
|
|
@@ -209,6 +224,44 @@ function svgIcon(svg2) {
|
|
|
209
224
|
}
|
|
210
225
|
|
|
211
226
|
// src/behaviors/popover.ts
|
|
227
|
+
var PANEL_SIZING_ATTRS = {
|
|
228
|
+
width: "data-panel-width",
|
|
229
|
+
minWidth: "data-panel-min",
|
|
230
|
+
maxWidth: "data-panel-max",
|
|
231
|
+
minHeight: "data-panel-min-height",
|
|
232
|
+
maxHeight: "data-panel-max-height"
|
|
233
|
+
};
|
|
234
|
+
function readPanelSizing(el) {
|
|
235
|
+
const out = {};
|
|
236
|
+
if (!el) return out;
|
|
237
|
+
for (const key of Object.keys(PANEL_SIZING_ATTRS)) {
|
|
238
|
+
const raw = el.getAttribute(PANEL_SIZING_ATTRS[key]);
|
|
239
|
+
if (raw == null) continue;
|
|
240
|
+
const n = Number(raw);
|
|
241
|
+
if (Number.isFinite(n) && n > 0) out[key] = n;
|
|
242
|
+
}
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
function resolvePanelSizing(el, overrides) {
|
|
246
|
+
const base = readPanelSizing(el);
|
|
247
|
+
if (!overrides) return base;
|
|
248
|
+
for (const key of Object.keys(overrides)) {
|
|
249
|
+
const v = overrides[key];
|
|
250
|
+
if (typeof v === "number" && Number.isFinite(v) && v > 0) base[key] = v;
|
|
251
|
+
}
|
|
252
|
+
return base;
|
|
253
|
+
}
|
|
254
|
+
function sizingToLayout(sizing, opts) {
|
|
255
|
+
const out = { ...opts };
|
|
256
|
+
if (sizing.minWidth != null) out.minWidth = sizing.minWidth;
|
|
257
|
+
if (sizing.maxWidth != null) out.maxWidth = sizing.maxWidth;
|
|
258
|
+
if (sizing.width != null) {
|
|
259
|
+
out.minWidth = sizing.width;
|
|
260
|
+
out.maxWidth = sizing.width;
|
|
261
|
+
}
|
|
262
|
+
if (sizing.maxHeight != null) out.maxHeight = sizing.maxHeight;
|
|
263
|
+
return out;
|
|
264
|
+
}
|
|
212
265
|
var DEFAULT_MIN_WIDTH = 220;
|
|
213
266
|
var DEFAULT_MAX_WIDTH = 420;
|
|
214
267
|
var DEFAULT_MIN_HEIGHT = 120;
|
|
@@ -233,13 +286,35 @@ function horizontalAnchor(anchor, width, align) {
|
|
|
233
286
|
function computePopoverLayout(anchor, viewport, opts = {}) {
|
|
234
287
|
const margin = opts.margin ?? 12;
|
|
235
288
|
const offset = opts.offset ?? 8;
|
|
289
|
+
const preferredSide = opts.side ?? "top";
|
|
290
|
+
const align = opts.align ?? "start";
|
|
291
|
+
if (preferredSide === "left" || preferredSide === "right") {
|
|
292
|
+
const availableLeft = Math.max(0, anchor.left - margin - offset);
|
|
293
|
+
const availableRight = Math.max(0, viewport.width - anchor.right - margin - offset);
|
|
294
|
+
const side2 = (preferredSide === "left" ? availableLeft : availableRight) >= DEFAULT_MIN_WIDTH ? preferredSide : preferredSide === "left" ? "right" : "left";
|
|
295
|
+
const minWidth2 = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
296
|
+
const maxWidth2 = Math.min(
|
|
297
|
+
Math.max(opts.maxWidth ?? DEFAULT_MAX_WIDTH, minWidth2),
|
|
298
|
+
side2 === "left" ? availableLeft : availableRight
|
|
299
|
+
);
|
|
300
|
+
const width2 = minWidth2;
|
|
301
|
+
const left2 = clamp(
|
|
302
|
+
side2 === "left" ? anchor.left - offset - width2 : anchor.right + offset,
|
|
303
|
+
margin,
|
|
304
|
+
viewport.width - margin - width2
|
|
305
|
+
);
|
|
306
|
+
const availableHeight2 = Math.max(80, viewport.height - margin * 2);
|
|
307
|
+
const maxHeight2 = Math.min(opts.maxHeight ?? DEFAULT_MAX_HEIGHT, availableHeight2);
|
|
308
|
+
const desired = clamp(opts.contentHeightHint ?? Math.min(anchor.height, maxHeight2), 40, maxHeight2);
|
|
309
|
+
const anchorTop = align === "center" ? anchor.top + anchor.height / 2 - desired / 2 : align === "end" ? anchor.bottom - desired : anchor.top;
|
|
310
|
+
const top2 = clamp(anchorTop, margin, viewport.height - margin - desired);
|
|
311
|
+
return { side: side2, left: left2, top: top2, width: width2, maxHeight: maxHeight2 };
|
|
312
|
+
}
|
|
236
313
|
const availableWidth = Math.max(80, viewport.width - margin * 2);
|
|
237
314
|
const requestedMinWidth = Math.max(1, opts.minWidth ?? DEFAULT_MIN_WIDTH);
|
|
238
315
|
const requestedMaxWidth = Math.max(requestedMinWidth, opts.maxWidth ?? DEFAULT_MAX_WIDTH);
|
|
239
316
|
const maxWidth = Math.min(requestedMaxWidth, availableWidth);
|
|
240
317
|
const minWidth = Math.min(requestedMinWidth, maxWidth);
|
|
241
|
-
const preferredSide = opts.side ?? "top";
|
|
242
|
-
const align = opts.align ?? "start";
|
|
243
318
|
const availableTop = Math.max(0, anchor.top - margin - offset);
|
|
244
319
|
const availableBottom = Math.max(0, viewport.height - anchor.bottom - margin - offset);
|
|
245
320
|
const availableHeight = Math.max(80, viewport.height - margin * 2);
|
|
@@ -263,8 +338,8 @@ function computePopoverLayout(anchor, viewport, opts = {}) {
|
|
|
263
338
|
return { side, left, top, width, maxHeight };
|
|
264
339
|
}
|
|
265
340
|
var openPanels = /* @__PURE__ */ new Map();
|
|
266
|
-
function rectFromElement(
|
|
267
|
-
const r =
|
|
341
|
+
function rectFromElement(el) {
|
|
342
|
+
const r = el.getBoundingClientRect();
|
|
268
343
|
return { left: r.left, top: r.top, right: r.right, bottom: r.bottom, width: r.width, height: r.height };
|
|
269
344
|
}
|
|
270
345
|
function resolveAnchor(state) {
|
|
@@ -317,7 +392,8 @@ function onKeyDown(e) {
|
|
|
317
392
|
function openPopover(panel, options) {
|
|
318
393
|
if (typeof document === "undefined") return;
|
|
319
394
|
if (openPanels.has(panel)) closePopover(panel);
|
|
320
|
-
const { anchor, onClose, ...
|
|
395
|
+
const { anchor, onClose, sizing, ...rest } = options;
|
|
396
|
+
const layoutOpts = sizingToLayout(resolvePanelSizing(panel, sizing), rest);
|
|
321
397
|
const placeholder = document.createComment("popover-anchor");
|
|
322
398
|
panel.parentNode?.insertBefore(placeholder, panel);
|
|
323
399
|
const wasHidden = panel.hidden;
|
|
@@ -369,32 +445,24 @@ function closePopover(panel) {
|
|
|
369
445
|
state.onClose?.();
|
|
370
446
|
}
|
|
371
447
|
|
|
372
|
-
// src/behaviors/
|
|
373
|
-
function
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
return node;
|
|
448
|
+
// src/behaviors/events.ts
|
|
449
|
+
function emitIcen(source, type, detail) {
|
|
450
|
+
return source.dispatchEvent(
|
|
451
|
+
new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
|
|
452
|
+
);
|
|
378
453
|
}
|
|
454
|
+
|
|
455
|
+
// src/behaviors/ai-panel.ts
|
|
379
456
|
function dispatch(node, name, detail) {
|
|
380
|
-
node
|
|
457
|
+
emitIcen(node, name, detail);
|
|
381
458
|
}
|
|
382
|
-
var STATUS_CLASSES = [
|
|
383
|
-
"pending",
|
|
384
|
-
"running",
|
|
385
|
-
"streaming",
|
|
386
|
-
"approval",
|
|
387
|
-
"done",
|
|
388
|
-
"error",
|
|
389
|
-
"cancelled"
|
|
390
|
-
];
|
|
391
459
|
function stripStatusClasses(node) {
|
|
392
|
-
for (const s of
|
|
460
|
+
for (const s of AI_STATUSES) node.classList.remove(`is-${s}`);
|
|
393
461
|
}
|
|
394
462
|
function buildTodoHead(done, total) {
|
|
395
|
-
const head =
|
|
396
|
-
head.appendChild(
|
|
397
|
-
const bar =
|
|
463
|
+
const head = h("div", "ai-todo-head");
|
|
464
|
+
head.appendChild(h("span", "ai-todo-progress", `${done}/${total}`));
|
|
465
|
+
const bar = h("div", "ai-todo-bar");
|
|
398
466
|
bar.setAttribute("role", "progressbar");
|
|
399
467
|
bar.setAttribute("aria-valuemin", "0");
|
|
400
468
|
bar.setAttribute("aria-valuemax", String(total));
|
|
@@ -407,23 +475,24 @@ function buildTodoHead(done, total) {
|
|
|
407
475
|
return head;
|
|
408
476
|
}
|
|
409
477
|
function buildTodoItem(item, index) {
|
|
410
|
-
const row =
|
|
478
|
+
const row = h("div", `ai-todo-item is-${item.status}`);
|
|
411
479
|
row.dataset.index = String(index);
|
|
412
|
-
row.appendChild(
|
|
413
|
-
row.appendChild(
|
|
414
|
-
row.appendChild(
|
|
480
|
+
row.appendChild(h("span", "ai-item-status"));
|
|
481
|
+
row.appendChild(h("span", "ai-todo-text", item.content));
|
|
482
|
+
row.appendChild(h("span", "ai-todo-active", item.activeForm ?? item.content));
|
|
415
483
|
return row;
|
|
416
484
|
}
|
|
417
|
-
function renderAiTodo(
|
|
418
|
-
if (typeof document === "undefined") return;
|
|
419
|
-
|
|
420
|
-
const root =
|
|
485
|
+
function renderAiTodo(el, items) {
|
|
486
|
+
if (typeof document === "undefined") return el;
|
|
487
|
+
el.textContent = "";
|
|
488
|
+
const root = h("div", "ai-todo");
|
|
421
489
|
const list = Array.isArray(items) ? items : [];
|
|
422
490
|
const done = list.filter((i) => i.status === "done").length;
|
|
423
491
|
root.appendChild(buildTodoHead(done, list.length));
|
|
424
492
|
list.forEach((item, i) => root.appendChild(buildTodoItem(item, i)));
|
|
425
493
|
if (list.length > 0 && done === list.length) root.classList.add("is-complete");
|
|
426
|
-
|
|
494
|
+
el.appendChild(root);
|
|
495
|
+
return el;
|
|
427
496
|
}
|
|
428
497
|
var TODO_NEXT = {
|
|
429
498
|
pending: "running",
|
|
@@ -431,7 +500,7 @@ var TODO_NEXT = {
|
|
|
431
500
|
done: "pending"
|
|
432
501
|
};
|
|
433
502
|
function todoItemStatus(item) {
|
|
434
|
-
for (const s of
|
|
503
|
+
for (const s of AI_STATUSES) {
|
|
435
504
|
if (item.classList.contains(`is-${s}`)) return s;
|
|
436
505
|
}
|
|
437
506
|
return "pending";
|
|
@@ -512,8 +581,8 @@ function initAiTodo(root) {
|
|
|
512
581
|
marked.__icenAiTodoInit = false;
|
|
513
582
|
};
|
|
514
583
|
}
|
|
515
|
-
function renderAiUsage(
|
|
516
|
-
if (typeof document === "undefined") return;
|
|
584
|
+
function renderAiUsage(el, usage, opts = {}) {
|
|
585
|
+
if (typeof document === "undefined") return el;
|
|
517
586
|
const u = normalizeUsage(usage);
|
|
518
587
|
const segs = [
|
|
519
588
|
{ key: "input", className: "ai-usage-seg--input", label: "\u8F93\u5165", value: u.input ?? 0 },
|
|
@@ -525,9 +594,9 @@ function renderAiUsage(elm, usage, opts = {}) {
|
|
|
525
594
|
const activeSegs = segs.filter((s) => s.value > 0);
|
|
526
595
|
const sum2 = activeSegs.reduce((acc, s) => acc + s.value, 0);
|
|
527
596
|
const total = u.total ?? sum2;
|
|
528
|
-
|
|
529
|
-
const root =
|
|
530
|
-
const bar =
|
|
597
|
+
el.textContent = "";
|
|
598
|
+
const root = h("div", "ai-usage");
|
|
599
|
+
const bar = h("div", "ai-usage-bar");
|
|
531
600
|
const summary = activeSegs.length > 0 ? activeSegs.map((s) => `${s.label} ${formatTokens(s.value)}`).join("\uFF0C") : "\u6682\u65E0\u7528\u91CF";
|
|
532
601
|
bar.setAttribute("role", "img");
|
|
533
602
|
bar.setAttribute("aria-label", `\u7528\u91CF\uFF1A${summary}`);
|
|
@@ -539,15 +608,15 @@ function renderAiUsage(elm, usage, opts = {}) {
|
|
|
539
608
|
}
|
|
540
609
|
root.appendChild(bar);
|
|
541
610
|
if (activeSegs.length > 0) {
|
|
542
|
-
const legend =
|
|
611
|
+
const legend = h("div", "ai-usage-legend");
|
|
543
612
|
for (const s of activeSegs) {
|
|
544
|
-
const item =
|
|
613
|
+
const item = h("span", "ai-usage-legend-item");
|
|
545
614
|
const dot = document.createElement("i");
|
|
546
615
|
dot.className = `ai-usage-dot ${s.className}`;
|
|
547
616
|
dot.setAttribute("aria-hidden", "true");
|
|
548
617
|
item.appendChild(dot);
|
|
549
|
-
item.appendChild(
|
|
550
|
-
item.appendChild(
|
|
618
|
+
item.appendChild(h("span", "ai-usage-legend-label", s.label));
|
|
619
|
+
item.appendChild(h("span", "ai-usage-legend-value", formatTokens(s.value)));
|
|
551
620
|
legend.appendChild(item);
|
|
552
621
|
}
|
|
553
622
|
root.appendChild(legend);
|
|
@@ -560,17 +629,18 @@ function renderAiUsage(elm, usage, opts = {}) {
|
|
|
560
629
|
if (typeof opts.cost === "number" && Number.isFinite(opts.cost)) {
|
|
561
630
|
totalParts.push(` \xB7 $${opts.cost.toFixed(4)}`);
|
|
562
631
|
}
|
|
563
|
-
root.appendChild(
|
|
564
|
-
|
|
632
|
+
root.appendChild(h("div", "ai-usage-total", totalParts.join("")));
|
|
633
|
+
el.appendChild(root);
|
|
634
|
+
return el;
|
|
565
635
|
}
|
|
566
|
-
function renderAiUsageRing(
|
|
567
|
-
if (typeof document === "undefined") return;
|
|
636
|
+
function renderAiUsageRing(el, usage, opts = {}) {
|
|
637
|
+
if (typeof document === "undefined") return el;
|
|
568
638
|
const u = normalizeUsage(usage);
|
|
569
639
|
const used = u.total ?? (u.input ?? 0) + (u.output ?? 0) + (u.cacheRead ?? 0) + (u.cacheWrite ?? 0) + (u.reasoning ?? 0);
|
|
570
640
|
const hasLimit = typeof opts.total === "number" && opts.total > 0;
|
|
571
641
|
const pct = hasLimit ? Math.min(100, Math.max(0, Math.round(used / opts.total * 100))) : 0;
|
|
572
|
-
|
|
573
|
-
const btn =
|
|
642
|
+
el.textContent = "";
|
|
643
|
+
const btn = h("button", "ai-usage-ring");
|
|
574
644
|
btn.type = "button";
|
|
575
645
|
btn.classList.add(pct >= 85 ? "is-hot" : pct >= 60 ? "is-warn" : "is-ok");
|
|
576
646
|
btn.setAttribute("aria-haspopup", "true");
|
|
@@ -594,7 +664,7 @@ function renderAiUsageRing(elm, usage, opts = {}) {
|
|
|
594
664
|
svg2.appendChild(c);
|
|
595
665
|
}
|
|
596
666
|
btn.appendChild(svg2);
|
|
597
|
-
btn.appendChild(
|
|
667
|
+
btn.appendChild(h("span", "ai-usage-ring-pct", hasLimit ? `${pct}%` : formatTokens(used)));
|
|
598
668
|
let panel = null;
|
|
599
669
|
const closePanel = () => {
|
|
600
670
|
if (!panel) return;
|
|
@@ -608,10 +678,10 @@ function renderAiUsageRing(elm, usage, opts = {}) {
|
|
|
608
678
|
closePanel();
|
|
609
679
|
return;
|
|
610
680
|
}
|
|
611
|
-
const p =
|
|
681
|
+
const p = h("div", "popover ai-usage-popover");
|
|
612
682
|
p.hidden = true;
|
|
613
|
-
p.appendChild(
|
|
614
|
-
const body =
|
|
683
|
+
p.appendChild(h("div", "ai-usage-popover-title", opts.title ?? "\u4E0A\u4E0B\u6587\u7A97\u53E3"));
|
|
684
|
+
const body = h("div", "ai-usage-popover-body");
|
|
615
685
|
renderAiUsage(body, usage, opts);
|
|
616
686
|
p.appendChild(body);
|
|
617
687
|
openPopover(p, {
|
|
@@ -625,7 +695,8 @@ function renderAiUsageRing(elm, usage, opts = {}) {
|
|
|
625
695
|
});
|
|
626
696
|
panel = p;
|
|
627
697
|
});
|
|
628
|
-
|
|
698
|
+
el.appendChild(btn);
|
|
699
|
+
return el;
|
|
629
700
|
}
|
|
630
701
|
var svgWrap = (inner) => `<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">${inner}</svg>`;
|
|
631
702
|
var FILE_OUTLINE = '<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"/>';
|
|
@@ -673,8 +744,8 @@ function fileKindIcon(kind) {
|
|
|
673
744
|
}
|
|
674
745
|
function renderFileChip(file) {
|
|
675
746
|
const kind = fileKind(file.path);
|
|
676
|
-
const chip =
|
|
677
|
-
const icon =
|
|
747
|
+
const chip = h("span", `ai-file-chip ai-file-chip--${kind} is-${file.status ?? "modified"}`);
|
|
748
|
+
const icon = h("span", "ai-file-icon");
|
|
678
749
|
const svg2 = svgIcon(fileKindIcon(kind));
|
|
679
750
|
if (svg2) icon.appendChild(svg2);
|
|
680
751
|
chip.appendChild(icon);
|
|
@@ -682,21 +753,21 @@ function renderFileChip(file) {
|
|
|
682
753
|
return chip;
|
|
683
754
|
}
|
|
684
755
|
function renderSection(title) {
|
|
685
|
-
const section =
|
|
686
|
-
section.appendChild(
|
|
687
|
-
const body =
|
|
756
|
+
const section = h("section", "ai-context-section");
|
|
757
|
+
section.appendChild(h("h3", "", title));
|
|
758
|
+
const body = h("div", "ai-context-section-body");
|
|
688
759
|
section.appendChild(body);
|
|
689
760
|
return { section, body };
|
|
690
761
|
}
|
|
691
|
-
function renderAiContext(
|
|
692
|
-
if (typeof document === "undefined") return;
|
|
693
|
-
|
|
762
|
+
function renderAiContext(el, model) {
|
|
763
|
+
if (typeof document === "undefined") return el;
|
|
764
|
+
el.textContent = "";
|
|
694
765
|
const m = model ?? {};
|
|
695
|
-
const aside =
|
|
766
|
+
const aside = h("aside", "ai-context");
|
|
696
767
|
aside.hidden = true;
|
|
697
|
-
const head =
|
|
698
|
-
head.appendChild(
|
|
699
|
-
const closeBtn =
|
|
768
|
+
const head = h("div", "ai-context-head");
|
|
769
|
+
head.appendChild(h("span", "", "\u4E0A\u4E0B\u6587"));
|
|
770
|
+
const closeBtn = h("button", "", "\xD7");
|
|
700
771
|
closeBtn.type = "button";
|
|
701
772
|
closeBtn.setAttribute("data-ai-context-close", "");
|
|
702
773
|
closeBtn.setAttribute("aria-label", "\u5173\u95ED");
|
|
@@ -716,7 +787,7 @@ function renderAiContext(elm, model) {
|
|
|
716
787
|
const files = Array.isArray(m.files) ? m.files : [];
|
|
717
788
|
if (files.length > 0) {
|
|
718
789
|
const { section, body } = renderSection(`\u6587\u4EF6 (${files.length})`);
|
|
719
|
-
const chips =
|
|
790
|
+
const chips = h("div", "ai-files");
|
|
720
791
|
for (const f of files) chips.appendChild(renderFileChip(f));
|
|
721
792
|
body.appendChild(chips);
|
|
722
793
|
aside.appendChild(section);
|
|
@@ -726,17 +797,17 @@ function renderAiContext(elm, model) {
|
|
|
726
797
|
const { section, body } = renderSection(`MCP (${servers.length})`);
|
|
727
798
|
for (const s of servers) {
|
|
728
799
|
const ok = s.status !== "disconnected";
|
|
729
|
-
const row =
|
|
730
|
-
const icon =
|
|
800
|
+
const row = h("div", `ai-item is-${ok ? "done" : "error"}`);
|
|
801
|
+
const icon = h("span", "ai-item-icon");
|
|
731
802
|
const svg2 = svgIcon(ICON_SERVER);
|
|
732
803
|
if (svg2) icon.appendChild(svg2);
|
|
733
804
|
row.appendChild(icon);
|
|
734
|
-
const main =
|
|
735
|
-
main.appendChild(
|
|
805
|
+
const main = h("span", "ai-item-main");
|
|
806
|
+
main.appendChild(h("span", "ai-item-title", s.name));
|
|
736
807
|
row.appendChild(main);
|
|
737
|
-
const side =
|
|
738
|
-
side.appendChild(
|
|
739
|
-
side.appendChild(
|
|
808
|
+
const side = h("span", "ai-item-side");
|
|
809
|
+
side.appendChild(h("span", "ai-item-status"));
|
|
810
|
+
side.appendChild(h("span", "ai-item-meta", typeof s.tools === "number" ? `${s.tools} \u5DE5\u5177` : ""));
|
|
740
811
|
row.appendChild(side);
|
|
741
812
|
body.appendChild(row);
|
|
742
813
|
}
|
|
@@ -746,20 +817,21 @@ function renderAiContext(elm, model) {
|
|
|
746
817
|
if (skills.length > 0) {
|
|
747
818
|
const { section, body } = renderSection("Skills");
|
|
748
819
|
for (const s of skills) {
|
|
749
|
-
const row =
|
|
750
|
-
const icon =
|
|
820
|
+
const row = h("div", "ai-item");
|
|
821
|
+
const icon = h("span", "ai-item-icon");
|
|
751
822
|
const svg2 = svgIcon(ICON_SPARKLES);
|
|
752
823
|
if (svg2) icon.appendChild(svg2);
|
|
753
824
|
row.appendChild(icon);
|
|
754
|
-
const main =
|
|
755
|
-
main.appendChild(
|
|
756
|
-
if (s.description) main.appendChild(
|
|
825
|
+
const main = h("span", "ai-item-main");
|
|
826
|
+
main.appendChild(h("span", "ai-item-title", s.name));
|
|
827
|
+
if (s.description) main.appendChild(h("span", "ai-item-sub", s.description));
|
|
757
828
|
row.appendChild(main);
|
|
758
829
|
body.appendChild(row);
|
|
759
830
|
}
|
|
760
831
|
aside.appendChild(section);
|
|
761
832
|
}
|
|
762
|
-
|
|
833
|
+
el.appendChild(aside);
|
|
834
|
+
return el;
|
|
763
835
|
}
|
|
764
836
|
function isInline(drawer) {
|
|
765
837
|
return drawer.classList.contains("ai-context--inline");
|
|
@@ -805,6 +877,7 @@ function initAiContext(root) {
|
|
|
805
877
|
void drawer.offsetWidth;
|
|
806
878
|
drawer.classList.add("is-open");
|
|
807
879
|
syncTriggers(drawer, true);
|
|
880
|
+
dispatch(drawer, "icen:ai-context-open", {});
|
|
808
881
|
const closeBtn = drawer.querySelector("[data-ai-context-close]");
|
|
809
882
|
(closeBtn ?? drawer).focus?.();
|
|
810
883
|
}
|
|
@@ -812,6 +885,7 @@ function initAiContext(root) {
|
|
|
812
885
|
if (drawer.hidden && !drawer.classList.contains("is-open")) return;
|
|
813
886
|
drawer.classList.remove("is-open");
|
|
814
887
|
syncTriggers(drawer, false);
|
|
888
|
+
dispatch(drawer, "icen:ai-context-close", {});
|
|
815
889
|
const finish = () => {
|
|
816
890
|
drawer.hidden = true;
|
|
817
891
|
if (restoreFocus && lastTrigger && document.contains(lastTrigger)) {
|
|
@@ -943,55 +1017,55 @@ function buildAuditBody(body, entries, opts = {}) {
|
|
|
943
1017
|
g.requests++;
|
|
944
1018
|
groups.set(key, g);
|
|
945
1019
|
}
|
|
946
|
-
const totals =
|
|
1020
|
+
const totals = h("div", "ai-audit-totals");
|
|
947
1021
|
const bits = [`${entries.length} \u6B21\u8BF7\u6C42`];
|
|
948
1022
|
if (errors > 0) bits.push(`${errors} \u5931\u8D25`);
|
|
949
1023
|
bits.push(`${formatTokens(tokens)} tok`);
|
|
950
1024
|
if (cost > 0) bits.push(`$${(Math.round(cost * 1e6) / 1e6).toFixed(4)}`);
|
|
951
1025
|
if (ttftN > 0) bits.push(`TTFT ${formatDuration(Math.round(ttftSum / ttftN))}`);
|
|
952
|
-
totals.appendChild(
|
|
1026
|
+
totals.appendChild(h("span", "ai-audit-totals-text", bits.join(" \xB7 ")));
|
|
953
1027
|
body.appendChild(totals);
|
|
954
1028
|
const sorted = Array.from(groups.values()).sort((a, b) => b.tokens - a.tokens);
|
|
955
1029
|
if (sorted.length > 0) {
|
|
956
|
-
const list2 =
|
|
1030
|
+
const list2 = h("div", "ai-audit-models");
|
|
957
1031
|
for (const g of sorted) {
|
|
958
|
-
const row =
|
|
959
|
-
row.appendChild(
|
|
960
|
-
row.appendChild(
|
|
1032
|
+
const row = h("div", "ai-audit-model-row");
|
|
1033
|
+
row.appendChild(h("span", "ai-audit-model-name", g.key));
|
|
1034
|
+
row.appendChild(h("span", "ai-audit-model-meta", `${g.requests} \u6B21 \xB7 ${formatTokens(g.tokens)} tok${g.cost > 0 ? ` \xB7 $${g.cost.toFixed(4)}` : ""}`));
|
|
961
1035
|
list2.appendChild(row);
|
|
962
1036
|
}
|
|
963
1037
|
body.appendChild(list2);
|
|
964
1038
|
}
|
|
965
1039
|
const recent = entries.slice(-limit).reverse();
|
|
966
|
-
const list =
|
|
1040
|
+
const list = h("div", "ai-audit-list");
|
|
967
1041
|
for (const e of recent) {
|
|
968
|
-
const item =
|
|
969
|
-
item.appendChild(
|
|
970
|
-
const main =
|
|
971
|
-
main.appendChild(
|
|
1042
|
+
const item = h("div", `ai-audit-item is-${e.status === "ok" ? "done" : "error"}`);
|
|
1043
|
+
item.appendChild(h("span", "ai-audit-dot"));
|
|
1044
|
+
const main = h("span", "ai-audit-item-main");
|
|
1045
|
+
main.appendChild(h("span", "ai-audit-item-model", `${e.provider} \xB7 ${e.model}`));
|
|
972
1046
|
const metaBits = [timeOf(e.ts), formatDuration(e.durationMs)];
|
|
973
1047
|
if (typeof e.ttftMs === "number") metaBits.push(`TTFT ${formatDuration(e.ttftMs)}`);
|
|
974
1048
|
if ((e.usage?.total ?? 0) > 0) metaBits.push(`${formatTokens(e.usage?.total ?? 0)} tok`);
|
|
975
|
-
main.appendChild(
|
|
1049
|
+
main.appendChild(h("span", "ai-audit-item-meta", metaBits.filter(Boolean).join(" \xB7 ")));
|
|
976
1050
|
item.appendChild(main);
|
|
977
|
-
if (e.error) item.appendChild(
|
|
1051
|
+
if (e.error) item.appendChild(h("span", "ai-audit-item-error", e.error));
|
|
978
1052
|
list.appendChild(item);
|
|
979
1053
|
}
|
|
980
1054
|
body.appendChild(list);
|
|
981
1055
|
}
|
|
982
|
-
function renderAiAudit(
|
|
983
|
-
if (typeof document === "undefined") return;
|
|
984
|
-
|
|
985
|
-
const root =
|
|
1056
|
+
function renderAiAudit(el, source, opts = {}) {
|
|
1057
|
+
if (typeof document === "undefined") return el;
|
|
1058
|
+
el.textContent = "";
|
|
1059
|
+
const root = h("div", "ai-audit");
|
|
986
1060
|
const entries = auditEntriesOf(source);
|
|
987
1061
|
if (entries.length === 0) {
|
|
988
|
-
root.appendChild(
|
|
989
|
-
|
|
990
|
-
return;
|
|
1062
|
+
root.appendChild(h("div", "ai-audit-empty", "\u6682\u65E0\u5BA1\u8BA1\u8BB0\u5F55"));
|
|
1063
|
+
el.appendChild(root);
|
|
1064
|
+
return el;
|
|
991
1065
|
}
|
|
992
1066
|
buildAuditBody(root, entries, { limit: opts.limit });
|
|
993
1067
|
if (opts.clearButton !== false) {
|
|
994
|
-
const clear =
|
|
1068
|
+
const clear = h("button", "btn btn-sm ai-audit-clear", "\u6E05\u9664\u5BA1\u8BA1");
|
|
995
1069
|
clear.type = "button";
|
|
996
1070
|
clear.addEventListener("click", () => {
|
|
997
1071
|
if (opts.onClear) opts.onClear();
|
|
@@ -999,7 +1073,8 @@ function renderAiAudit(elm, source, opts = {}) {
|
|
|
999
1073
|
});
|
|
1000
1074
|
root.appendChild(clear);
|
|
1001
1075
|
}
|
|
1002
|
-
|
|
1076
|
+
el.appendChild(root);
|
|
1077
|
+
return el;
|
|
1003
1078
|
}
|
|
1004
1079
|
export {
|
|
1005
1080
|
initAiContext,
|