@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.
Files changed (106) hide show
  1. package/AGENTS.md +15 -9
  2. package/README.md +8 -3
  3. package/dist/base.css +4 -3
  4. package/dist/behaviors/accordion.d.mts +5 -2
  5. package/dist/behaviors/accordion.mjs +36 -11
  6. package/dist/behaviors/ai-chat.d.mts +3 -1
  7. package/dist/behaviors/ai-chat.mjs +66 -43
  8. package/dist/behaviors/ai-composer.d.mts +28 -3
  9. package/dist/behaviors/ai-composer.mjs +291 -209
  10. package/dist/behaviors/ai-core.d.mts +9 -4
  11. package/dist/behaviors/ai-core.mjs +21 -8
  12. package/dist/behaviors/ai-diff.d.mts +2 -2
  13. package/dist/behaviors/ai-diff.mjs +172 -41
  14. package/dist/behaviors/ai-panel.d.mts +11 -8
  15. package/dist/behaviors/ai-panel.mjs +185 -110
  16. package/dist/behaviors/ai-provider.mjs +54 -48
  17. package/dist/behaviors/ai-tool.d.mts +12 -4
  18. package/dist/behaviors/ai-tool.mjs +311 -263
  19. package/dist/behaviors/ai-tools.d.mts +9 -6
  20. package/dist/behaviors/ai-tools.mjs +259 -155
  21. package/dist/behaviors/back-top.mjs +16 -1
  22. package/dist/behaviors/carousel.d.mts +5 -2
  23. package/dist/behaviors/carousel.mjs +53 -26
  24. package/dist/behaviors/charts.d.mts +68 -22
  25. package/dist/behaviors/charts.mjs +221 -150
  26. package/dist/behaviors/command-palette.d.mts +40 -6
  27. package/dist/behaviors/command-palette.mjs +143 -15
  28. package/dist/behaviors/context-menu.d.mts +21 -4
  29. package/dist/behaviors/context-menu.mjs +122 -38
  30. package/dist/behaviors/controls.d.mts +59 -0
  31. package/dist/behaviors/controls.mjs +601 -0
  32. package/dist/behaviors/copy.d.mts +5 -2
  33. package/dist/behaviors/copy.mjs +37 -12
  34. package/dist/behaviors/datatable.d.mts +12 -0
  35. package/dist/behaviors/datatable.mjs +226 -43
  36. package/dist/behaviors/date-picker.d.mts +2 -2
  37. package/dist/behaviors/date-picker.mjs +171 -32
  38. package/dist/behaviors/dropdown.d.mts +6 -2
  39. package/dist/behaviors/dropdown.mjs +164 -25
  40. package/dist/behaviors/events.d.mts +330 -0
  41. package/dist/behaviors/events.mjs +208 -0
  42. package/dist/behaviors/input.mjs +25 -12
  43. package/dist/behaviors/modal.d.mts +24 -5
  44. package/dist/behaviors/modal.mjs +76 -10
  45. package/dist/behaviors/nav.d.mts +1 -1
  46. package/dist/behaviors/nav.mjs +33 -7
  47. package/dist/behaviors/notification.d.mts +20 -4
  48. package/dist/behaviors/notification.mjs +53 -73
  49. package/dist/behaviors/popover.d.mts +39 -3
  50. package/dist/behaviors/popover.mjs +90 -4
  51. package/dist/behaviors/select.d.mts +4 -2
  52. package/dist/behaviors/select.mjs +161 -24
  53. package/dist/behaviors/sidebar.d.mts +5 -2
  54. package/dist/behaviors/sidebar.mjs +43 -13
  55. package/dist/behaviors/slider.d.mts +5 -2
  56. package/dist/behaviors/slider.mjs +54 -43
  57. package/dist/behaviors/split-pane.d.mts +6 -2
  58. package/dist/behaviors/split-pane.mjs +62 -21
  59. package/dist/behaviors/tabs.d.mts +3 -2
  60. package/dist/behaviors/tabs.mjs +65 -14
  61. package/dist/behaviors/tag-input.d.mts +5 -2
  62. package/dist/behaviors/tag-input.mjs +61 -21
  63. package/dist/behaviors/theme.d.mts +13 -2
  64. package/dist/behaviors/theme.mjs +48 -4
  65. package/dist/behaviors/toast.d.mts +30 -8
  66. package/dist/behaviors/toast.mjs +74 -7
  67. package/dist/behaviors/tree.d.mts +5 -2
  68. package/dist/behaviors/tree.mjs +38 -15
  69. package/dist/behaviors/upload.mjs +21 -23
  70. package/dist/cli.mjs +1 -1
  71. package/dist/components/ai-panel.css +1 -1
  72. package/dist/components/ai-tool-call.mjs +1 -1
  73. package/dist/components/badge.css +1 -1
  74. package/dist/components/breadcrumb.css +0 -32
  75. package/dist/components/chart-calendar.mjs +1 -2
  76. package/dist/components/chart-pie.css +0 -6
  77. package/dist/components/charts.css +1 -1
  78. package/dist/components/content.css +3 -16
  79. package/dist/components/datatable.css +1 -1
  80. package/dist/components/date-picker.css +1 -1
  81. package/dist/components/notification.css +2 -3
  82. package/dist/components/pagination.css +3 -1
  83. package/dist/components/pagination.mjs +1 -0
  84. package/dist/components/rating.mjs +1 -0
  85. package/dist/components/segmented.css +9 -1
  86. package/dist/components/segmented.mjs +1 -1
  87. package/dist/components/select.css +67 -4
  88. package/dist/components/steps.css +13 -3
  89. package/dist/components/steps.mjs +1 -0
  90. package/dist/components/switch.css +3 -1
  91. package/dist/components/switch.mjs +1 -0
  92. package/dist/components/table.mjs +1 -0
  93. package/dist/components/tabs.css +23 -0
  94. package/dist/components/tag-input.css +1 -6
  95. package/dist/components/upload.css +0 -15
  96. package/dist/index.d.mts +12 -10
  97. package/dist/index.mjs +2700 -1460
  98. package/dist/registry.json +42 -13
  99. package/dist/retro-effects.css +3 -16
  100. package/dist/tokens/retro-effects.css +3 -16
  101. package/dist/tokens/style-profiles.css +3 -2
  102. package/dist/tokens/typography.css +2 -0
  103. package/dist/tokens.css +5 -2
  104. package/dist/ui.css +138 -98
  105. package/docs/spec/ai-native.md +7 -4
  106. package/package.json +1 -1
@@ -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(
@@ -159,9 +168,9 @@ function formatDuration(ms) {
159
168
  const m = Math.floor(ms / 6e4);
160
169
  const s = Math.round(ms % 6e4 / 1e3);
161
170
  if (ms < 36e5) return s > 0 ? `${m}m${s}s` : `${m}m`;
162
- const h = Math.floor(ms / 36e5);
171
+ const h2 = Math.floor(ms / 36e5);
163
172
  const rm = Math.round(ms % 36e5 / 6e4);
164
- return rm > 0 ? `${h}h${rm}m` : `${h}h`;
173
+ return rm > 0 ? `${h2}h${rm}m` : `${h2}h`;
165
174
  }
166
175
  function aiContentUrl(part) {
167
176
  if (part.type === "text" || part.type === "resource-link") return void 0;
@@ -175,6 +184,12 @@ function contextEstimate(usage) {
175
184
  const u = normalizeUsage(usage);
176
185
  return (u.input ?? 0) + (u.cacheRead ?? 0) + (u.cacheWrite ?? 0) + (u.output ?? 0) + (u.reasoning ?? 0);
177
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;
192
+ }
178
193
  function svgIcon(svg2) {
179
194
  if (typeof document === "undefined" || typeof DOMParser === "undefined") return null;
180
195
  let parsed;
@@ -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 = (el3) => {
191
- for (const attr of Array.from(el3.attributes)) {
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")) el3.removeAttribute(attr.name);
208
+ if (name.startsWith("on")) el.removeAttribute(attr.name);
194
209
  if ((name === "href" || name === "xlink:href") && attr.value.trim().toLowerCase().startsWith("javascript:")) {
195
- el3.removeAttribute(attr.name);
210
+ el.removeAttribute(attr.name);
196
211
  }
197
212
  }
198
- for (const child of Array.from(el3.children)) {
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(el3) {
267
- const r = el3.getBoundingClientRect();
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, ...layoutOpts } = options;
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,17 +445,18 @@ function closePopover(panel) {
369
445
  state.onClose?.();
370
446
  }
371
447
 
372
- // src/behaviors/ai-panel.ts
373
- function el(tag, className, text) {
374
- const node = document.createElement(tag);
375
- if (className) node.className = className;
376
- if (text !== void 0) node.textContent = text;
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 buildTodoHead(done, total) {
380
- const head = el("div", "ai-todo-head");
381
- head.appendChild(el("span", "ai-todo-progress", `${done}/${total}`));
382
- const bar = el("div", "ai-todo-bar");
457
+ const head = h("div", "ai-todo-head");
458
+ head.appendChild(h("span", "ai-todo-progress", `${done}/${total}`));
459
+ const bar = h("div", "ai-todo-bar");
383
460
  bar.setAttribute("role", "progressbar");
384
461
  bar.setAttribute("aria-valuemin", "0");
385
462
  bar.setAttribute("aria-valuemax", String(total));
@@ -392,26 +469,27 @@ function buildTodoHead(done, total) {
392
469
  return head;
393
470
  }
394
471
  function buildTodoItem(item, index) {
395
- const row = el("div", `ai-todo-item is-${item.status}`);
472
+ const row = h("div", `ai-todo-item is-${item.status}`);
396
473
  row.dataset.index = String(index);
397
- row.appendChild(el("span", "ai-item-status"));
398
- row.appendChild(el("span", "ai-todo-text", item.content));
399
- row.appendChild(el("span", "ai-todo-active", item.activeForm ?? item.content));
474
+ row.appendChild(h("span", "ai-item-status"));
475
+ row.appendChild(h("span", "ai-todo-text", item.content));
476
+ row.appendChild(h("span", "ai-todo-active", item.activeForm ?? item.content));
400
477
  return row;
401
478
  }
402
- function renderAiTodo(elm, items) {
403
- if (typeof document === "undefined") return;
404
- elm.textContent = "";
405
- const root = el("div", "ai-todo");
479
+ function renderAiTodo(el, items) {
480
+ if (typeof document === "undefined") return el;
481
+ el.textContent = "";
482
+ const root = h("div", "ai-todo");
406
483
  const list = Array.isArray(items) ? items : [];
407
484
  const done = list.filter((i) => i.status === "done").length;
408
485
  root.appendChild(buildTodoHead(done, list.length));
409
486
  list.forEach((item, i) => root.appendChild(buildTodoItem(item, i)));
410
487
  if (list.length > 0 && done === list.length) root.classList.add("is-complete");
411
- elm.appendChild(root);
488
+ el.appendChild(root);
489
+ return el;
412
490
  }
413
- function renderAiUsage(elm, usage, opts = {}) {
414
- if (typeof document === "undefined") return;
491
+ function renderAiUsage(el, usage, opts = {}) {
492
+ if (typeof document === "undefined") return el;
415
493
  const u = normalizeUsage(usage);
416
494
  const segs = [
417
495
  { key: "input", className: "ai-usage-seg--input", label: "\u8F93\u5165", value: u.input ?? 0 },
@@ -423,9 +501,9 @@ function renderAiUsage(elm, usage, opts = {}) {
423
501
  const activeSegs = segs.filter((s) => s.value > 0);
424
502
  const sum2 = activeSegs.reduce((acc, s) => acc + s.value, 0);
425
503
  const total = u.total ?? sum2;
426
- elm.textContent = "";
427
- const root = el("div", "ai-usage");
428
- const bar = el("div", "ai-usage-bar");
504
+ el.textContent = "";
505
+ const root = h("div", "ai-usage");
506
+ const bar = h("div", "ai-usage-bar");
429
507
  const summary = activeSegs.length > 0 ? activeSegs.map((s) => `${s.label} ${formatTokens(s.value)}`).join("\uFF0C") : "\u6682\u65E0\u7528\u91CF";
430
508
  bar.setAttribute("role", "img");
431
509
  bar.setAttribute("aria-label", `\u7528\u91CF\uFF1A${summary}`);
@@ -437,15 +515,15 @@ function renderAiUsage(elm, usage, opts = {}) {
437
515
  }
438
516
  root.appendChild(bar);
439
517
  if (activeSegs.length > 0) {
440
- const legend = el("div", "ai-usage-legend");
518
+ const legend = h("div", "ai-usage-legend");
441
519
  for (const s of activeSegs) {
442
- const item = el("span", "ai-usage-legend-item");
520
+ const item = h("span", "ai-usage-legend-item");
443
521
  const dot = document.createElement("i");
444
522
  dot.className = `ai-usage-dot ${s.className}`;
445
523
  dot.setAttribute("aria-hidden", "true");
446
524
  item.appendChild(dot);
447
- item.appendChild(el("span", "ai-usage-legend-label", s.label));
448
- item.appendChild(el("span", "ai-usage-legend-value", formatTokens(s.value)));
525
+ item.appendChild(h("span", "ai-usage-legend-label", s.label));
526
+ item.appendChild(h("span", "ai-usage-legend-value", formatTokens(s.value)));
449
527
  legend.appendChild(item);
450
528
  }
451
529
  root.appendChild(legend);
@@ -458,17 +536,18 @@ function renderAiUsage(elm, usage, opts = {}) {
458
536
  if (typeof opts.cost === "number" && Number.isFinite(opts.cost)) {
459
537
  totalParts.push(` \xB7 $${opts.cost.toFixed(4)}`);
460
538
  }
461
- root.appendChild(el("div", "ai-usage-total", totalParts.join("")));
462
- elm.appendChild(root);
539
+ root.appendChild(h("div", "ai-usage-total", totalParts.join("")));
540
+ el.appendChild(root);
541
+ return el;
463
542
  }
464
- function renderAiUsageRing(elm, usage, opts = {}) {
465
- if (typeof document === "undefined") return;
543
+ function renderAiUsageRing(el, usage, opts = {}) {
544
+ if (typeof document === "undefined") return el;
466
545
  const u = normalizeUsage(usage);
467
546
  const used = u.total ?? (u.input ?? 0) + (u.output ?? 0) + (u.cacheRead ?? 0) + (u.cacheWrite ?? 0) + (u.reasoning ?? 0);
468
547
  const hasLimit = typeof opts.total === "number" && opts.total > 0;
469
548
  const pct = hasLimit ? Math.min(100, Math.max(0, Math.round(used / opts.total * 100))) : 0;
470
- elm.textContent = "";
471
- const btn = el("button", "ai-usage-ring");
549
+ el.textContent = "";
550
+ const btn = h("button", "ai-usage-ring");
472
551
  btn.type = "button";
473
552
  btn.classList.add(pct >= 85 ? "is-hot" : pct >= 60 ? "is-warn" : "is-ok");
474
553
  btn.setAttribute("aria-haspopup", "true");
@@ -492,7 +571,7 @@ function renderAiUsageRing(elm, usage, opts = {}) {
492
571
  svg2.appendChild(c);
493
572
  }
494
573
  btn.appendChild(svg2);
495
- btn.appendChild(el("span", "ai-usage-ring-pct", hasLimit ? `${pct}%` : formatTokens(used)));
574
+ btn.appendChild(h("span", "ai-usage-ring-pct", hasLimit ? `${pct}%` : formatTokens(used)));
496
575
  let panel = null;
497
576
  const closePanel = () => {
498
577
  if (!panel) return;
@@ -506,10 +585,10 @@ function renderAiUsageRing(elm, usage, opts = {}) {
506
585
  closePanel();
507
586
  return;
508
587
  }
509
- const p = el("div", "popover ai-usage-popover");
588
+ const p = h("div", "popover ai-usage-popover");
510
589
  p.hidden = true;
511
- p.appendChild(el("div", "ai-usage-popover-title", opts.title ?? "\u4E0A\u4E0B\u6587\u7A97\u53E3"));
512
- const body = el("div", "ai-usage-popover-body");
590
+ p.appendChild(h("div", "ai-usage-popover-title", opts.title ?? "\u4E0A\u4E0B\u6587\u7A97\u53E3"));
591
+ const body = h("div", "ai-usage-popover-body");
513
592
  renderAiUsage(body, usage, opts);
514
593
  p.appendChild(body);
515
594
  openPopover(p, {
@@ -523,7 +602,8 @@ function renderAiUsageRing(elm, usage, opts = {}) {
523
602
  });
524
603
  panel = p;
525
604
  });
526
- elm.appendChild(btn);
605
+ el.appendChild(btn);
606
+ return el;
527
607
  }
528
608
  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>`;
529
609
  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"/>';
@@ -546,18 +626,18 @@ var ICON_SPARKLES = svgWrap(
546
626
  // src/behaviors/ai-chat.ts
547
627
  var ICON_COPY = '<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"><rect x="8" y="2" width="8" height="4" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/></svg>';
548
628
  var ICON_RETRY = '<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"><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"/><path d="M3 3v5h5"/></svg>';
549
- function createAiStream(el3) {
550
- const reasoning = el3.closest(".ai-reasoning");
629
+ function createAiStream(el) {
630
+ const reasoning = el.closest(".ai-reasoning");
551
631
  const startedAt = Date.now();
552
632
  let state = "streaming";
553
- el3.classList.add("is-streaming");
554
- el3.setAttribute("aria-busy", "true");
633
+ el.classList.add("is-streaming");
634
+ el.setAttribute("aria-busy", "true");
555
635
  reasoning?.classList.add("is-streaming");
556
636
  const settle = (finished) => {
557
637
  if (state !== "streaming") return;
558
638
  state = finished ? "done" : "cancelled";
559
- el3.classList.remove("is-streaming");
560
- el3.removeAttribute("aria-busy");
639
+ el.classList.remove("is-streaming");
640
+ el.removeAttribute("aria-busy");
561
641
  if (!reasoning) return;
562
642
  reasoning.classList.remove("is-streaming");
563
643
  if (!finished) return;
@@ -571,7 +651,7 @@ function createAiStream(el3) {
571
651
  return {
572
652
  append(text) {
573
653
  if (state !== "streaming" || !text) return;
574
- el3.textContent += text;
654
+ el.textContent += text;
575
655
  },
576
656
  done() {
577
657
  settle(true);
@@ -582,9 +662,9 @@ function createAiStream(el3) {
582
662
  fail() {
583
663
  if (state !== "streaming") return;
584
664
  state = "error";
585
- el3.classList.remove("is-streaming");
586
- el3.removeAttribute("aria-busy");
587
- el3.classList.add("is-error");
665
+ el.classList.remove("is-streaming");
666
+ el.removeAttribute("aria-busy");
667
+ el.classList.add("is-error");
588
668
  reasoning?.classList.remove("is-streaming");
589
669
  }
590
670
  };
@@ -600,15 +680,9 @@ function safeHref(uri) {
600
680
  if (s.startsWith("javascript:") || s.startsWith("data:text/html")) return void 0;
601
681
  return uri;
602
682
  }
603
- function el2(tag, className, text) {
604
- const node = document.createElement(tag);
605
- if (className) node.className = className;
606
- if (text != null) node.textContent = text;
607
- return node;
608
- }
609
683
  function renderAiContentPart(part) {
610
684
  if (part.type === "text") {
611
- const span = el2("span", "ai-msg-text");
685
+ const span = h("span", "ai-msg-text");
612
686
  span.textContent = part.text;
613
687
  if (part.state === "streaming") span.classList.add("is-streaming");
614
688
  return span;
@@ -643,8 +717,8 @@ function renderAiContentPart(part) {
643
717
  return video;
644
718
  }
645
719
  if (part.type === "file") {
646
- const chip = el2("span", "ai-msg-file");
647
- chip.appendChild(el2("span", "ai-msg-file-name", part.filename ?? "\u6587\u4EF6"));
720
+ const chip = h("span", "ai-msg-file");
721
+ chip.appendChild(h("span", "ai-msg-file-name", part.filename ?? "\u6587\u4EF6"));
648
722
  const href2 = part.url ? safeHref(part.url) : void 0;
649
723
  if (href2 && /^(https?:|data:)/i.test(href2)) {
650
724
  const a2 = document.createElement("a");
@@ -665,9 +739,18 @@ function renderAiContentPart(part) {
665
739
  return a;
666
740
  }
667
741
  function renderAiMessage(scrollEl, model) {
668
- const row = el2("div", `ai-msg ai-msg--${model.role}`);
669
- const avatar = el2("span", "ai-msg-avatar", ROLE_AVATAR[model.role] ?? "");
670
- const body = el2("div", "ai-msg-body");
742
+ if (typeof document === "undefined") {
743
+ return {
744
+ el: scrollEl,
745
+ body: scrollEl,
746
+ setMeta: () => void 0,
747
+ setError: () => void 0,
748
+ stream: () => createAiStream(scrollEl)
749
+ };
750
+ }
751
+ const row = h("div", `ai-msg ai-msg--${model.role}`);
752
+ const avatar = h("span", "ai-msg-avatar", ROLE_AVATAR[model.role] ?? "");
753
+ const body = h("div", "ai-msg-body");
671
754
  row.appendChild(avatar);
672
755
  row.appendChild(body);
673
756
  const parts = typeof model.content === "string" ? [{ type: "text", text: model.content }] : model.content;
@@ -675,18 +758,18 @@ function renderAiMessage(scrollEl, model) {
675
758
  const node = renderAiContentPart(part);
676
759
  if (node) body.appendChild(node);
677
760
  }
678
- if (model.model) body.appendChild(el2("span", "ai-msg-model", model.model));
679
- const metaEl = el2("div", "ai-msg-meta");
761
+ if (model.model) body.appendChild(h("span", "ai-msg-model", model.model));
762
+ const metaEl = h("div", "ai-msg-meta");
680
763
  if (model.meta != null) metaEl.textContent = model.meta;
681
764
  row.appendChild(metaEl);
682
- const actions = el2("div", "ai-msg-actions");
683
- const copyBtn = el2("button", "ai-msg-action");
765
+ const actions = h("div", "ai-msg-actions");
766
+ const copyBtn = h("button", "ai-msg-action");
684
767
  copyBtn.type = "button";
685
768
  copyBtn.setAttribute("data-ai-msg-action", "copy");
686
769
  copyBtn.setAttribute("aria-label", "\u590D\u5236");
687
770
  const copyIcon = svgIcon(ICON_COPY);
688
771
  if (copyIcon) copyBtn.appendChild(copyIcon);
689
- const retryBtn = el2("button", "ai-msg-action");
772
+ const retryBtn = h("button", "ai-msg-action");
690
773
  retryBtn.type = "button";
691
774
  retryBtn.setAttribute("data-ai-msg-action", "retry");
692
775
  retryBtn.setAttribute("aria-label", "\u91CD\u8BD5");
@@ -704,13 +787,13 @@ function renderAiMessage(scrollEl, model) {
704
787
  row.classList.add("ai-msg--error");
705
788
  let errEl = row.querySelector(".ai-msg-error-text");
706
789
  if (!errEl) {
707
- errEl = el2("div", "ai-msg-error-text");
790
+ errEl = h("div", "ai-msg-error-text");
708
791
  body.appendChild(errEl);
709
792
  }
710
793
  errEl.textContent = message;
711
794
  },
712
795
  stream() {
713
- const span = el2("span", "ai-msg-stream-text");
796
+ const span = h("span", "ai-msg-stream-text");
714
797
  body.appendChild(span);
715
798
  return createAiStream(span);
716
799
  }
@@ -744,19 +827,35 @@ var ICON_DOC = svgWrap2(
744
827
  var ICON_BOT2 = svgWrap2(
745
828
  '<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"/>'
746
829
  );
747
- var REF_KIND_LABEL = {
748
- file: "\u6587\u4EF6",
749
- folder: "\u6587\u4EF6\u5939",
750
- doc: "\u6587\u6863",
751
- agent: "\u4EE3\u7406"
752
- };
753
- var REF_KIND_ICON = {
754
- file: ICON_FILE3,
755
- folder: ICON_FOLDER,
756
- doc: ICON_DOC,
757
- agent: ICON_BOT2
830
+ var REF_KIND_BUILTIN = {
831
+ file: { label: "\u6587\u4EF6", icon: ICON_FILE3, order: 0 },
832
+ folder: { label: "\u6587\u4EF6\u5939", icon: ICON_FOLDER, order: 1 },
833
+ doc: { label: "\u6587\u6863", icon: ICON_DOC, order: 2 },
834
+ agent: { label: "\u4EE3\u7406", icon: ICON_BOT2, order: 3 }
758
835
  };
759
- var REF_KIND_ORDER = ["file", "folder", "doc", "agent"];
836
+ var REF_KIND_ORDER_LAST = Number.MAX_SAFE_INTEGER;
837
+ var REF_KIND_REGISTRY = /* @__PURE__ */ new Map();
838
+ function registerRefKind(kind, def) {
839
+ const key = kind.trim();
840
+ if (key) REF_KIND_REGISTRY.set(key, def);
841
+ }
842
+ function getRefKindDef(kind) {
843
+ const reg = REF_KIND_REGISTRY.get(kind);
844
+ const builtin = REF_KIND_BUILTIN[kind];
845
+ return {
846
+ label: reg?.label ?? builtin?.label ?? kind,
847
+ icon: reg?.icon ?? builtin?.icon ?? ICON_DOC,
848
+ order: reg && typeof reg.order === "number" && Number.isFinite(reg.order) ? reg.order : builtin?.order ?? REF_KIND_ORDER_LAST
849
+ };
850
+ }
851
+ function knownRefKinds(sources) {
852
+ const kinds = new Set(Object.keys(REF_KIND_BUILTIN));
853
+ for (const key of REF_KIND_REGISTRY.keys()) kinds.add(key);
854
+ for (const s of sources) {
855
+ if (typeof s?.kind === "string" && s.kind) kinds.add(s.kind);
856
+ }
857
+ return Array.from(kinds).sort((a, b) => getRefKindDef(a).order - getRefKindDef(b).order);
858
+ }
760
859
  var RUNNING = /* @__PURE__ */ new WeakMap();
761
860
  var QUEUES = /* @__PURE__ */ new WeakMap();
762
861
  var MODELS = /* @__PURE__ */ new WeakMap();
@@ -765,18 +864,19 @@ var COMMANDS = /* @__PURE__ */ new WeakMap();
765
864
  var REF_SOURCES = /* @__PURE__ */ new WeakMap();
766
865
  var ACTIVE_REFS = /* @__PURE__ */ new WeakMap();
767
866
  var USAGE = /* @__PURE__ */ new WeakMap();
768
- var TODO = /* @__PURE__ */ new WeakMap();
867
+ var TODO_STATES = /* @__PURE__ */ new WeakMap();
769
868
  var HISTORIES = /* @__PURE__ */ new WeakMap();
770
869
  var POPUP = /* @__PURE__ */ new WeakMap();
771
870
  var SUBMITS = /* @__PURE__ */ new WeakMap();
871
+ var POPUP_SIZING = /* @__PURE__ */ new WeakMap();
772
872
  function isBrowser() {
773
873
  return typeof document !== "undefined" && typeof window !== "undefined";
774
874
  }
775
875
  function emit(target, name, detail) {
776
- target.dispatchEvent(new CustomEvent(name, { detail, bubbles: true }));
876
+ emitIcen(target, name, detail);
777
877
  }
778
- function resolveComposer(el3) {
779
- return el3.closest("[data-ai-composer]");
878
+ function resolveComposer(el) {
879
+ return el.closest("[data-ai-composer]");
780
880
  }
781
881
  function getTextarea(composer) {
782
882
  return composer.querySelector(".ai-composer-input");
@@ -841,8 +941,8 @@ function renderQueueChips(composer) {
841
941
  queueEl.replaceChildren(...queue.map((text, i) => makeChip(text, true, `\u79FB\u9664\u6392\u961F\u6D88\u606F ${i + 1}`)));
842
942
  queueEl.hidden = queue.length === 0;
843
943
  }
844
- function setComposerRunning(el3, running) {
845
- const composer = resolveComposer(el3);
944
+ function setComposerRunning(el, running) {
945
+ const composer = resolveComposer(el);
846
946
  if (!composer) return;
847
947
  RUNNING.set(composer, running);
848
948
  composer.classList.toggle("is-running", running);
@@ -1006,6 +1106,13 @@ function settlePopup(s) {
1006
1106
  s.emptyEl.hidden = visibleOptions(s).length > 0;
1007
1107
  setPopupFocus(s, 0);
1008
1108
  }
1109
+ function filterPopupOptions(s, q) {
1110
+ const needle = q.trim().toLowerCase();
1111
+ for (const opt of s.options) {
1112
+ opt.hidden = needle !== "" && !(opt.dataset.hay ?? "").includes(needle);
1113
+ }
1114
+ settlePopup(s);
1115
+ }
1009
1116
  function popupKeydown(composer, s, e) {
1010
1117
  if (e.isComposing) return;
1011
1118
  switch (e.key) {
@@ -1045,13 +1152,16 @@ function closePopup(composer, restoreFocus = true) {
1045
1152
  }
1046
1153
  if (s.mode === "model") {
1047
1154
  composer.querySelector("[data-ai-model-open]")?.setAttribute("aria-expanded", "false");
1048
- }
1049
- if (s.mode !== "model") {
1155
+ } else {
1050
1156
  getTextarea(composer)?.setAttribute("aria-expanded", "false");
1051
1157
  }
1052
1158
  closePopover(s.panel);
1053
1159
  if (restoreFocus && s.returnFocus && s.returnFocus.isConnected) s.returnFocus.focus();
1054
1160
  }
1161
+ function resolvePopupSizing(composer, defaults) {
1162
+ const explicit = resolvePanelSizing(composer, POPUP_SIZING.get(composer));
1163
+ return { ...defaults, ...explicit };
1164
+ }
1055
1165
  function mountPopup(composer, init) {
1056
1166
  closePopup(composer, false);
1057
1167
  const panel = document.createElement("div");
@@ -1076,9 +1186,7 @@ function mountPopup(composer, init) {
1076
1186
  side: "top",
1077
1187
  align: "start",
1078
1188
  offset: 8,
1079
- minWidth: init.minWidth,
1080
- maxWidth: init.maxWidth,
1081
- maxHeight: init.maxHeight,
1189
+ sizing: resolvePopupSizing(composer, init.sizing),
1082
1190
  onClose: () => {
1083
1191
  if (POPUP.get(composer) === session) POPUP.delete(composer);
1084
1192
  panel.remove();
@@ -1101,6 +1209,16 @@ function mountPopup(composer, init) {
1101
1209
  });
1102
1210
  return session;
1103
1211
  }
1212
+ function appendPopupEmpty(s, text) {
1213
+ s.emptyEl.className = "ai-composer-popup-empty";
1214
+ s.emptyEl.textContent = text;
1215
+ s.emptyEl.hidden = true;
1216
+ s.panel.appendChild(s.emptyEl);
1217
+ }
1218
+ function popupSizingFor(box) {
1219
+ const w = box.getBoundingClientRect().width;
1220
+ return { minWidth: Math.min(Math.max(w, 200), 320), maxWidth: 360, maxHeight: 320 };
1221
+ }
1104
1222
  function openModelPopup(composer) {
1105
1223
  const cfg = MODELS.get(composer);
1106
1224
  if (!cfg) return;
@@ -1110,9 +1228,7 @@ function openModelPopup(composer) {
1110
1228
  const session = mountPopup(composer, {
1111
1229
  mode: "model",
1112
1230
  anchor,
1113
- minWidth: 220,
1114
- maxWidth: 320,
1115
- maxHeight: 340,
1231
+ sizing: { minWidth: 220, maxWidth: 320, maxHeight: 340 },
1116
1232
  returnFocus: btn ?? ta,
1117
1233
  pick: (opt) => {
1118
1234
  const provider = opt.dataset.provider ?? "";
@@ -1164,6 +1280,7 @@ function openModelPopup(composer) {
1164
1280
  opt.dataset.providerLabel = p.label;
1165
1281
  opt.dataset.model = m.id;
1166
1282
  opt.dataset.label = m.label;
1283
+ opt.dataset.hay = `${m.label} ${p.label}`.toLowerCase();
1167
1284
  if (typeof m.context === "number") opt.dataset.context = String(m.context);
1168
1285
  const selected = cfg.current.provider === p.id && cfg.current.model === m.id;
1169
1286
  opt.setAttribute("aria-selected", String(selected));
@@ -1188,10 +1305,7 @@ function openModelPopup(composer) {
1188
1305
  s.groups.push(group);
1189
1306
  }
1190
1307
  s.panel.appendChild(list);
1191
- s.emptyEl.className = "ai-composer-popup-empty";
1192
- s.emptyEl.textContent = "\u65E0\u5339\u914D\u6A21\u578B";
1193
- s.emptyEl.hidden = true;
1194
- s.panel.appendChild(s.emptyEl);
1308
+ appendPopupEmpty(s, "\u65E0\u5339\u914D\u6A21\u578B");
1195
1309
  const foot = document.createElement("div");
1196
1310
  foot.className = "ai-composer-popup-foot";
1197
1311
  const usageCfg = USAGE.get(composer);
@@ -1211,12 +1325,7 @@ function openModelPopup(composer) {
1211
1325
  s.keydownHandler = (e) => popupKeydown(composer, s, e);
1212
1326
  input.addEventListener("keydown", s.keydownHandler);
1213
1327
  input.addEventListener("input", () => {
1214
- const q = input.value.trim().toLowerCase();
1215
- for (const opt of s.options) {
1216
- const hay = `${opt.dataset.label ?? ""} ${opt.dataset.providerLabel ?? ""}`.toLowerCase();
1217
- opt.hidden = q !== "" && !hay.includes(q);
1218
- }
1219
- settlePopup(s);
1328
+ filterPopupOptions(s, input.value);
1220
1329
  });
1221
1330
  }
1222
1331
  });
@@ -1228,13 +1337,10 @@ function openCommandPopup(composer, token) {
1228
1337
  if (!commands || commands.length === 0) return;
1229
1338
  const box = composer.querySelector(".ai-composer-box") ?? composer;
1230
1339
  const ta = getTextarea(composer);
1231
- const boxRect = box.getBoundingClientRect();
1232
1340
  const session = mountPopup(composer, {
1233
1341
  mode: "command",
1234
1342
  anchor: box,
1235
- minWidth: Math.min(Math.max(boxRect.width, 200), 320),
1236
- maxWidth: 360,
1237
- maxHeight: 320,
1343
+ sizing: popupSizingFor(box),
1238
1344
  returnFocus: ta,
1239
1345
  pick: (opt) => {
1240
1346
  const name = opt.dataset.name ?? "";
@@ -1286,25 +1392,15 @@ function openCommandPopup(composer, token) {
1286
1392
  list.appendChild(group);
1287
1393
  s.groups.push(group);
1288
1394
  s.panel.appendChild(list);
1289
- s.emptyEl.className = "ai-composer-popup-empty";
1290
- s.emptyEl.textContent = "\u65E0\u5339\u914D\u547D\u4EE4";
1291
- s.emptyEl.hidden = true;
1292
- s.panel.appendChild(s.emptyEl);
1395
+ appendPopupEmpty(s, "\u65E0\u5339\u914D\u547D\u4EE4");
1293
1396
  }
1294
1397
  });
1295
1398
  ta?.setAttribute("aria-expanded", "true");
1296
- filterCommandOptions(session, token);
1399
+ filterPopupOptions(session, token);
1297
1400
  }
1298
1401
  function commandToken(value) {
1299
1402
  return value.slice(1).split(/\s/, 1)[0] ?? "";
1300
1403
  }
1301
- function filterCommandOptions(s, token) {
1302
- const q = token.trim().toLowerCase();
1303
- for (const opt of s.options) {
1304
- opt.hidden = q !== "" && !(opt.dataset.hay ?? "").includes(q);
1305
- }
1306
- settlePopup(s);
1307
- }
1308
1404
  function updateCommandPopup(composer, s) {
1309
1405
  const ta = getTextarea(composer);
1310
1406
  const value = ta?.value ?? "";
@@ -1313,20 +1409,17 @@ function updateCommandPopup(composer, s) {
1313
1409
  maybeOpenTriggerPopups(composer);
1314
1410
  return;
1315
1411
  }
1316
- filterCommandOptions(s, commandToken(value));
1412
+ filterPopupOptions(s, commandToken(value));
1317
1413
  }
1318
1414
  function openRefPopup(composer, range, filter) {
1319
1415
  const sources = REF_SOURCES.get(composer);
1320
1416
  if (!sources || sources.length === 0) return;
1321
1417
  const box = composer.querySelector(".ai-composer-box") ?? composer;
1322
1418
  const ta = getTextarea(composer);
1323
- const boxRect = box.getBoundingClientRect();
1324
1419
  const session = mountPopup(composer, {
1325
1420
  mode: "ref",
1326
1421
  anchor: box,
1327
- minWidth: Math.min(Math.max(boxRect.width, 200), 320),
1328
- maxWidth: 360,
1329
- maxHeight: 320,
1422
+ sizing: popupSizingFor(box),
1330
1423
  returnFocus: ta,
1331
1424
  refRange: range,
1332
1425
  pick: (opt) => {
@@ -1349,7 +1442,8 @@ function openRefPopup(composer, range, filter) {
1349
1442
  list.className = "ai-composer-popup-list";
1350
1443
  list.setAttribute("role", "listbox");
1351
1444
  list.setAttribute("aria-label", "\u5F15\u7528");
1352
- for (const kind of REF_KIND_ORDER) {
1445
+ for (const kind of knownRefKinds(sources)) {
1446
+ const def = getRefKindDef(kind);
1353
1447
  const items = sources.filter((r) => r.kind === kind);
1354
1448
  if (items.length === 0) continue;
1355
1449
  const group = document.createElement("div");
@@ -1357,7 +1451,7 @@ function openRefPopup(composer, range, filter) {
1357
1451
  group.dataset.kind = kind;
1358
1452
  group.appendChild(Object.assign(document.createElement("div"), {
1359
1453
  className: "ai-composer-popup-group-label",
1360
- textContent: REF_KIND_LABEL[kind]
1454
+ textContent: def.label
1361
1455
  }));
1362
1456
  for (const r of items) {
1363
1457
  const opt = document.createElement("button");
@@ -1368,7 +1462,7 @@ function openRefPopup(composer, range, filter) {
1368
1462
  opt.dataset.hay = `${r.label} ${r.sub ?? ""}`.toLowerCase();
1369
1463
  const icon = document.createElement("span");
1370
1464
  icon.className = "ai-composer-popup-option-icon";
1371
- const svg2 = svgIcon(REF_KIND_ICON[kind]);
1465
+ const svg2 = svgIcon(def.icon);
1372
1466
  if (svg2) icon.appendChild(svg2);
1373
1467
  const label = document.createElement("span");
1374
1468
  label.className = "ai-composer-popup-option-label";
@@ -1387,21 +1481,11 @@ function openRefPopup(composer, range, filter) {
1387
1481
  s.groups.push(group);
1388
1482
  }
1389
1483
  s.panel.appendChild(list);
1390
- s.emptyEl.className = "ai-composer-popup-empty";
1391
- s.emptyEl.textContent = "\u65E0\u5339\u914D\u5F15\u7528";
1392
- s.emptyEl.hidden = true;
1393
- s.panel.appendChild(s.emptyEl);
1484
+ appendPopupEmpty(s, "\u65E0\u5339\u914D\u5F15\u7528");
1394
1485
  }
1395
1486
  });
1396
1487
  ta?.setAttribute("aria-expanded", "true");
1397
- filterRefOptions(session, filter);
1398
- }
1399
- function filterRefOptions(s, filter) {
1400
- const q = filter.trim().toLowerCase();
1401
- for (const opt of s.options) {
1402
- opt.hidden = q !== "" && !(opt.dataset.hay ?? "").includes(q);
1403
- }
1404
- settlePopup(s);
1488
+ filterPopupOptions(session, filter);
1405
1489
  }
1406
1490
  function updateRefPopup(composer, s) {
1407
1491
  const ta = getTextarea(composer);
@@ -1412,7 +1496,7 @@ function updateRefPopup(composer, s) {
1412
1496
  return;
1413
1497
  }
1414
1498
  s.refRange = { start: t.start, end: t.end };
1415
- filterRefOptions(s, t.filter);
1499
+ filterPopupOptions(s, t.filter);
1416
1500
  }
1417
1501
  function findRefToken(ta) {
1418
1502
  const value = ta.value;
@@ -1492,21 +1576,21 @@ function removeActiveRef(composer, source) {
1492
1576
  emit(composer, "icen:ai-ref", { action: "remove", ref: source });
1493
1577
  }
1494
1578
  function pushHistory(composer, text) {
1495
- const h = HISTORIES.get(composer) ?? { items: [], index: -1 };
1496
- if (h.items[h.items.length - 1] !== text) {
1497
- h.items.push(text);
1498
- if (h.items.length > HISTORY_LIMIT) h.items.shift();
1579
+ const h2 = HISTORIES.get(composer) ?? { items: [], index: -1 };
1580
+ if (h2.items[h2.items.length - 1] !== text) {
1581
+ h2.items.push(text);
1582
+ if (h2.items.length > HISTORY_LIMIT) h2.items.shift();
1499
1583
  }
1500
- h.index = -1;
1501
- HISTORIES.set(composer, h);
1584
+ h2.index = -1;
1585
+ HISTORIES.set(composer, h2);
1502
1586
  }
1503
1587
  function setupComposer(composer) {
1504
- const el3 = composer;
1505
- if (el3.__icenAiComposerInit) return void 0;
1506
- el3.__icenAiComposerInit = true;
1588
+ const el = composer;
1589
+ if (el.__icenAiComposerInit) return void 0;
1590
+ el.__icenAiComposerInit = true;
1507
1591
  const ta = getTextarea(composer);
1508
1592
  if (!ta) {
1509
- el3.__icenAiComposerInit = false;
1593
+ el.__icenAiComposerInit = false;
1510
1594
  return void 0;
1511
1595
  }
1512
1596
  const queueEl = composer.querySelector(".ai-composer-queue");
@@ -1541,7 +1625,7 @@ function setupComposer(composer) {
1541
1625
  const queue = QUEUES.get(composer) ?? [];
1542
1626
  if (queue.length === 0) return;
1543
1627
  const index = queue.length - 1;
1544
- const text = queue.pop();
1628
+ const text = queue.pop() ?? "";
1545
1629
  QUEUES.set(composer, queue);
1546
1630
  renderQueue();
1547
1631
  ta.value = text;
@@ -1549,20 +1633,20 @@ function setupComposer(composer) {
1549
1633
  emit(composer, "icen:ai-dequeue", { index });
1550
1634
  };
1551
1635
  const browseHistory = (delta) => {
1552
- const h = HISTORIES.get(composer);
1553
- if (!h || h.items.length === 0) return;
1636
+ const h2 = HISTORIES.get(composer);
1637
+ if (!h2 || h2.items.length === 0) return;
1554
1638
  if (delta === -1) {
1555
- h.index = h.index === -1 ? h.items.length - 1 : Math.max(0, h.index - 1);
1639
+ h2.index = h2.index === -1 ? h2.items.length - 1 : Math.max(0, h2.index - 1);
1556
1640
  } else {
1557
- h.index += 1;
1558
- if (h.index >= h.items.length) {
1559
- h.index = -1;
1641
+ h2.index += 1;
1642
+ if (h2.index >= h2.items.length) {
1643
+ h2.index = -1;
1560
1644
  ta.value = "";
1561
1645
  autosizeTa(ta);
1562
1646
  return;
1563
1647
  }
1564
1648
  }
1565
- ta.value = h.items[h.index];
1649
+ ta.value = h2.items[h2.index] ?? "";
1566
1650
  autosizeTa(ta);
1567
1651
  };
1568
1652
  const onKeydown = (e) => {
@@ -1578,15 +1662,15 @@ function setupComposer(composer) {
1578
1662
  return;
1579
1663
  }
1580
1664
  if (e.key === "ArrowUp") {
1581
- const h = HISTORIES.get(composer);
1582
- const browsing = (h?.index ?? -1) >= 0;
1665
+ const h2 = HISTORIES.get(composer);
1666
+ const browsing = (h2?.index ?? -1) >= 0;
1583
1667
  if (ta.value === "") {
1584
1668
  if (isRunning() && (QUEUES.get(composer) ?? []).length > 0) {
1585
1669
  e.preventDefault();
1586
1670
  takeBackLastQueued();
1587
1671
  return;
1588
1672
  }
1589
- if (h && h.items.length > 0) {
1673
+ if (h2 && h2.items.length > 0) {
1590
1674
  e.preventDefault();
1591
1675
  browseHistory(-1);
1592
1676
  }
@@ -1597,8 +1681,8 @@ function setupComposer(composer) {
1597
1681
  return;
1598
1682
  }
1599
1683
  if (e.key === "ArrowDown") {
1600
- const h = HISTORIES.get(composer);
1601
- if (h && h.index >= 0) {
1684
+ const h2 = HISTORIES.get(composer);
1685
+ if (h2 && h2.index >= 0) {
1602
1686
  e.preventDefault();
1603
1687
  browseHistory(1);
1604
1688
  }
@@ -1606,8 +1690,8 @@ function setupComposer(composer) {
1606
1690
  };
1607
1691
  const onInput = () => {
1608
1692
  autosizeTa(ta);
1609
- const h = HISTORIES.get(composer);
1610
- if (h && h.index >= 0 && ta.value !== h.items[h.index]) h.index = -1;
1693
+ const h2 = HISTORIES.get(composer);
1694
+ if (h2 && h2.index >= 0 && ta.value !== h2.items[h2.index]) h2.index = -1;
1611
1695
  const session = POPUP.get(composer);
1612
1696
  if (session) {
1613
1697
  if (session.mode === "command") updateCommandPopup(composer, session);
@@ -1735,7 +1819,7 @@ function setupComposer(composer) {
1735
1819
  composer.querySelector("[data-ai-attach]")?.removeEventListener("click", onAttachClick);
1736
1820
  fileInput.removeEventListener("change", onFileChange);
1737
1821
  fileInput.remove();
1738
- el3.__icenAiComposerInit = false;
1822
+ el.__icenAiComposerInit = false;
1739
1823
  };
1740
1824
  }
1741
1825
  function initAiComposer(root) {
@@ -1757,9 +1841,9 @@ function initAiComposer(root) {
1757
1841
  cleanups.length = 0;
1758
1842
  };
1759
1843
  }
1760
- function setComposerModels(el3, providers, current) {
1844
+ function setComposerModels(el, providers, current) {
1761
1845
  if (!isBrowser()) return;
1762
- const composer = resolveComposer(el3);
1846
+ const composer = resolveComposer(el);
1763
1847
  if (!composer) return;
1764
1848
  const list = (Array.isArray(providers) ? providers : []).filter(
1765
1849
  (p) => p && Array.isArray(p.models) && p.models.length > 0
@@ -1786,9 +1870,9 @@ function setComposerModels(el3, providers, current) {
1786
1870
  updateModelButton(composer);
1787
1871
  refreshUsageRing(composer);
1788
1872
  }
1789
- function setComposerCommands(el3, commands) {
1873
+ function setComposerCommands(el, commands) {
1790
1874
  if (!isBrowser()) return;
1791
- const composer = resolveComposer(el3);
1875
+ const composer = resolveComposer(el);
1792
1876
  if (!composer) return;
1793
1877
  const list = (Array.isArray(commands) ? commands : []).filter((c) => c && typeof c.name === "string" && c.name);
1794
1878
  if (list.length === 0) {
@@ -1799,9 +1883,9 @@ function setComposerCommands(el3, commands) {
1799
1883
  }
1800
1884
  COMMANDS.set(composer, list);
1801
1885
  }
1802
- function setComposerRefs(el3, sources) {
1886
+ function setComposerRefs(el, sources) {
1803
1887
  if (!isBrowser()) return;
1804
- const composer = resolveComposer(el3);
1888
+ const composer = resolveComposer(el);
1805
1889
  if (!composer) return;
1806
1890
  const list = (Array.isArray(sources) ? sources : []).filter((s) => s && typeof s.label === "string" && s.label);
1807
1891
  REF_SOURCES.set(composer, list);
@@ -1812,9 +1896,9 @@ function setComposerRefs(el3, sources) {
1812
1896
  ensureRefsRow(composer);
1813
1897
  renderRefs(composer);
1814
1898
  }
1815
- function setComposerUsage(el3, usage, opts = {}) {
1899
+ function setComposerUsage(el, usage, opts = {}) {
1816
1900
  if (!isBrowser()) return;
1817
- const composer = resolveComposer(el3);
1901
+ const composer = resolveComposer(el);
1818
1902
  if (!composer) return;
1819
1903
  if (!usage) {
1820
1904
  USAGE.delete(composer);
@@ -1827,24 +1911,24 @@ function setComposerUsage(el3, usage, opts = {}) {
1827
1911
  syncToolbar(composer);
1828
1912
  refreshUsageRing(composer);
1829
1913
  }
1830
- function setComposerTodo(el3, items) {
1914
+ function setComposerTodo(el, items) {
1831
1915
  if (!isBrowser()) return;
1832
- const composer = resolveComposer(el3);
1916
+ const composer = resolveComposer(el);
1833
1917
  if (!composer) return;
1834
1918
  let chip = composer.querySelector(".ai-composer-todo");
1835
1919
  if (!items || items.length === 0) {
1836
- TODO.delete(composer);
1920
+ TODO_STATES.delete(composer);
1837
1921
  chip?.remove();
1838
1922
  return;
1839
1923
  }
1840
- TODO.set(composer, items);
1924
+ TODO_STATES.set(composer, items);
1841
1925
  if (!chip) {
1842
- chip = document.createElement("button");
1843
- chip.type = "button";
1844
- chip.className = "ai-composer-todo";
1845
- chip.setAttribute("aria-haspopup", "true");
1846
- chip.addEventListener("click", () => {
1847
- const cur = TODO.get(composer);
1926
+ const btn = document.createElement("button");
1927
+ btn.type = "button";
1928
+ btn.className = "ai-composer-todo";
1929
+ btn.setAttribute("aria-haspopup", "true");
1930
+ btn.addEventListener("click", () => {
1931
+ const cur = TODO_STATES.get(composer);
1848
1932
  if (!cur) return;
1849
1933
  const p = document.createElement("div");
1850
1934
  p.className = "popover ai-composer-todo-pop";
@@ -1857,12 +1941,13 @@ function setComposerTodo(el3, items) {
1857
1941
  renderAiTodo(body, cur);
1858
1942
  p.append(title, body);
1859
1943
  openPopover(p, {
1860
- anchor: chip,
1944
+ anchor: btn,
1861
1945
  side: "top",
1862
1946
  align: "end",
1863
1947
  onClose: () => p.remove()
1864
1948
  });
1865
1949
  });
1950
+ chip = btn;
1866
1951
  const icon = svgIcon(
1867
1952
  '<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"><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"/></svg>'
1868
1953
  );
@@ -1885,21 +1970,14 @@ function setComposerTodo(el3, items) {
1885
1970
  if (label) label.textContent = `${done}/${items.length}`;
1886
1971
  }
1887
1972
  var BOUND = /* @__PURE__ */ new WeakSet();
1888
- var USAGE_KEYS = ["input", "output", "cacheRead", "cacheWrite", "reasoning", "total"];
1889
- function accumulateUsage(acc, add) {
1890
- if (!add) return;
1891
- for (const k of USAGE_KEYS) {
1892
- const v = add[k];
1893
- if (typeof v === "number" && Number.isFinite(v)) acc[k] = (acc[k] ?? 0) + v;
1894
- }
1895
- }
1896
- function bindComposer(el3, opts = {}) {
1973
+ function bindComposer(el, opts = {}) {
1897
1974
  const noop = () => void 0;
1898
1975
  if (!isBrowser()) return { unbind: noop };
1899
- const composer = resolveComposer(el3);
1976
+ const composer = resolveComposer(el);
1900
1977
  if (!composer) return { unbind: noop };
1901
1978
  if (BOUND.has(composer)) return { unbind: noop };
1902
1979
  BOUND.add(composer);
1980
+ if (opts.popoverSizing) POPUP_SIZING.set(composer, opts.popoverSizing);
1903
1981
  const disposers = [];
1904
1982
  const usageCfg = opts.usage;
1905
1983
  const manageRunning = opts.running ?? true;
@@ -1922,7 +2000,7 @@ function bindComposer(el3, opts = {}) {
1922
2000
  total: contextEstimate(u)
1923
2001
  };
1924
2002
  } else if (usageCfg.from === "billing") {
1925
- accumulateUsage(billing, detail.usage);
2003
+ billing = addUsage(billing, detail.usage);
1926
2004
  usage = billing;
1927
2005
  } else {
1928
2006
  usage = usageCfg.from.summary();
@@ -1935,6 +2013,9 @@ function bindComposer(el3, opts = {}) {
1935
2013
  document.addEventListener("icen:ai-done", onDone);
1936
2014
  disposers.push(() => document.removeEventListener("icen:ai-done", onDone));
1937
2015
  }
2016
+ if (opts.client && !opts.messages) {
2017
+ console.warn("[ai-composer] client \u6A21\u5F0F\u9700\u8981 opts.messages\uFF0C\u5DF2\u964D\u7EA7\u4E3A\u7EAF\u72B6\u6001\u7ED1\u5B9A");
2018
+ }
1938
2019
  if (opts.client && opts.messages) {
1939
2020
  const client = opts.client;
1940
2021
  const messages = opts.messages;
@@ -1975,7 +2056,7 @@ function bindComposer(el3, opts = {}) {
1975
2056
  setComposerRunning(composer, false);
1976
2057
  const queue = QUEUES.get(composer) ?? [];
1977
2058
  if (queue.length > 0) {
1978
- const next = queue.shift();
2059
+ const next = queue.shift() ?? "";
1979
2060
  QUEUES.set(composer, queue);
1980
2061
  renderQueueChips(composer);
1981
2062
  emit(composer, "icen:ai-dequeue", { index: 0 });
@@ -2023,6 +2104,7 @@ function bindComposer(el3, opts = {}) {
2023
2104
  export {
2024
2105
  bindComposer,
2025
2106
  initAiComposer,
2107
+ registerRefKind,
2026
2108
  setComposerCommands,
2027
2109
  setComposerModels,
2028
2110
  setComposerRefs,