@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.
Files changed (228) hide show
  1. package/AGENTS.md +63 -14
  2. package/README.md +17 -9
  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-branch.d.mts +19 -0
  7. package/dist/behaviors/ai-branch.mjs +217 -0
  8. package/dist/behaviors/ai-chat.d.mts +11 -7
  9. package/dist/behaviors/ai-chat.mjs +134 -51
  10. package/dist/behaviors/ai-composer.d.mts +28 -3
  11. package/dist/behaviors/ai-composer.mjs +399 -229
  12. package/dist/behaviors/ai-core.d.mts +9 -4
  13. package/dist/behaviors/ai-core.mjs +21 -8
  14. package/dist/behaviors/ai-diff.d.mts +2 -2
  15. package/dist/behaviors/ai-diff.mjs +172 -41
  16. package/dist/behaviors/ai-feedback.d.mts +26 -0
  17. package/dist/behaviors/ai-feedback.mjs +303 -0
  18. package/dist/behaviors/ai-panel.d.mts +26 -9
  19. package/dist/behaviors/ai-panel.mjs +211 -110
  20. package/dist/behaviors/ai-provider.mjs +54 -48
  21. package/dist/behaviors/ai-threads.d.mts +38 -0
  22. package/dist/behaviors/ai-threads.mjs +319 -0
  23. package/dist/behaviors/ai-tool.d.mts +12 -4
  24. package/dist/behaviors/ai-tool.mjs +316 -263
  25. package/dist/behaviors/ai-tools.d.mts +9 -6
  26. package/dist/behaviors/ai-tools.mjs +264 -155
  27. package/dist/behaviors/back-top.mjs +16 -1
  28. package/dist/behaviors/carousel.d.mts +5 -2
  29. package/dist/behaviors/carousel.mjs +53 -26
  30. package/dist/behaviors/charts.d.mts +170 -22
  31. package/dist/behaviors/charts.mjs +1016 -150
  32. package/dist/behaviors/command-palette.d.mts +40 -6
  33. package/dist/behaviors/command-palette.mjs +143 -15
  34. package/dist/behaviors/context-menu.d.mts +21 -4
  35. package/dist/behaviors/context-menu.mjs +122 -38
  36. package/dist/behaviors/controls.d.mts +59 -0
  37. package/dist/behaviors/controls.mjs +601 -0
  38. package/dist/behaviors/copy.d.mts +5 -2
  39. package/dist/behaviors/copy.mjs +37 -12
  40. package/dist/behaviors/datatable.d.mts +12 -0
  41. package/dist/behaviors/datatable.mjs +226 -43
  42. package/dist/behaviors/date-picker.d.mts +2 -2
  43. package/dist/behaviors/date-picker.mjs +171 -32
  44. package/dist/behaviors/dropdown.d.mts +6 -2
  45. package/dist/behaviors/dropdown.mjs +164 -25
  46. package/dist/behaviors/events.d.mts +523 -0
  47. package/dist/behaviors/events.mjs +208 -0
  48. package/dist/behaviors/input.mjs +25 -12
  49. package/dist/behaviors/kb-access.d.mts +34 -0
  50. package/dist/behaviors/kb-access.mjs +569 -0
  51. package/dist/behaviors/kb-acl.d.mts +50 -0
  52. package/dist/behaviors/kb-acl.mjs +405 -0
  53. package/dist/behaviors/kb-answer.d.mts +38 -0
  54. package/dist/behaviors/kb-answer.mjs +2055 -0
  55. package/dist/behaviors/kb-audit.d.mts +23 -0
  56. package/dist/behaviors/kb-audit.mjs +375 -0
  57. package/dist/behaviors/kb-canvas.d.mts +44 -0
  58. package/dist/behaviors/kb-canvas.mjs +589 -0
  59. package/dist/behaviors/kb-chain.d.mts +15 -0
  60. package/dist/behaviors/kb-chain.mjs +270 -0
  61. package/dist/behaviors/kb-checkpoint.d.mts +16 -0
  62. package/dist/behaviors/kb-checkpoint.mjs +342 -0
  63. package/dist/behaviors/kb-chunks.d.mts +31 -0
  64. package/dist/behaviors/kb-chunks.mjs +475 -0
  65. package/dist/behaviors/kb-citation.d.mts +38 -0
  66. package/dist/behaviors/kb-citation.mjs +677 -0
  67. package/dist/behaviors/kb-clarify.d.mts +20 -0
  68. package/dist/behaviors/kb-clarify.mjs +248 -0
  69. package/dist/behaviors/kb-conflict.d.mts +22 -0
  70. package/dist/behaviors/kb-conflict.mjs +284 -0
  71. package/dist/behaviors/kb-connector.d.mts +17 -0
  72. package/dist/behaviors/kb-connector.mjs +375 -0
  73. package/dist/behaviors/kb-core.d.mts +529 -0
  74. package/dist/behaviors/kb-core.mjs +890 -0
  75. package/dist/behaviors/kb-eval.d.mts +22 -0
  76. package/dist/behaviors/kb-eval.mjs +408 -0
  77. package/dist/behaviors/kb-explain.d.mts +11 -0
  78. package/dist/behaviors/kb-explain.mjs +243 -0
  79. package/dist/behaviors/kb-filter.d.mts +31 -0
  80. package/dist/behaviors/kb-filter.mjs +587 -0
  81. package/dist/behaviors/kb-gap.d.mts +27 -0
  82. package/dist/behaviors/kb-gap.mjs +273 -0
  83. package/dist/behaviors/kb-hittest.d.mts +34 -0
  84. package/dist/behaviors/kb-hittest.mjs +409 -0
  85. package/dist/behaviors/kb-metadata.d.mts +29 -0
  86. package/dist/behaviors/kb-metadata.mjs +326 -0
  87. package/dist/behaviors/kb-passage.d.mts +41 -0
  88. package/dist/behaviors/kb-passage.mjs +529 -0
  89. package/dist/behaviors/kb-pipeline.d.mts +18 -0
  90. package/dist/behaviors/kb-pipeline.mjs +381 -0
  91. package/dist/behaviors/kb-qa.d.mts +26 -0
  92. package/dist/behaviors/kb-qa.mjs +332 -0
  93. package/dist/behaviors/kb-rerank.d.mts +20 -0
  94. package/dist/behaviors/kb-rerank.mjs +315 -0
  95. package/dist/behaviors/kb-retrieval.d.mts +49 -0
  96. package/dist/behaviors/kb-retrieval.mjs +746 -0
  97. package/dist/behaviors/kb-review.d.mts +44 -0
  98. package/dist/behaviors/kb-review.mjs +451 -0
  99. package/dist/behaviors/kb-sandbox.d.mts +33 -0
  100. package/dist/behaviors/kb-sandbox.mjs +325 -0
  101. package/dist/behaviors/kb-segment.d.mts +32 -0
  102. package/dist/behaviors/kb-segment.mjs +499 -0
  103. package/dist/behaviors/kb-sources.d.mts +27 -0
  104. package/dist/behaviors/kb-sources.mjs +373 -0
  105. package/dist/behaviors/kb-sql.d.mts +43 -0
  106. package/dist/behaviors/kb-sql.mjs +948 -0
  107. package/dist/behaviors/kb-trace.d.mts +16 -0
  108. package/dist/behaviors/kb-trace.mjs +414 -0
  109. package/dist/behaviors/kb-visibility.d.mts +44 -0
  110. package/dist/behaviors/kb-visibility.mjs +403 -0
  111. package/dist/behaviors/kb-who-can.d.mts +32 -0
  112. package/dist/behaviors/kb-who-can.mjs +379 -0
  113. package/dist/behaviors/modal.d.mts +24 -5
  114. package/dist/behaviors/modal.mjs +76 -10
  115. package/dist/behaviors/nav.d.mts +1 -1
  116. package/dist/behaviors/nav.mjs +33 -7
  117. package/dist/behaviors/notification.d.mts +20 -4
  118. package/dist/behaviors/notification.mjs +53 -73
  119. package/dist/behaviors/popover.d.mts +39 -3
  120. package/dist/behaviors/popover.mjs +90 -4
  121. package/dist/behaviors/select.d.mts +4 -2
  122. package/dist/behaviors/select.mjs +161 -24
  123. package/dist/behaviors/sidebar.d.mts +5 -2
  124. package/dist/behaviors/sidebar.mjs +43 -13
  125. package/dist/behaviors/slider.d.mts +5 -2
  126. package/dist/behaviors/slider.mjs +54 -43
  127. package/dist/behaviors/split-pane.d.mts +6 -2
  128. package/dist/behaviors/split-pane.mjs +62 -21
  129. package/dist/behaviors/tabs.d.mts +3 -2
  130. package/dist/behaviors/tabs.mjs +65 -14
  131. package/dist/behaviors/tag-input.d.mts +5 -2
  132. package/dist/behaviors/tag-input.mjs +61 -21
  133. package/dist/behaviors/theme.d.mts +13 -2
  134. package/dist/behaviors/theme.mjs +48 -4
  135. package/dist/behaviors/toast.d.mts +30 -8
  136. package/dist/behaviors/toast.mjs +74 -7
  137. package/dist/behaviors/tree.d.mts +5 -2
  138. package/dist/behaviors/tree.mjs +38 -15
  139. package/dist/behaviors/upload.mjs +21 -23
  140. package/dist/cli.mjs +1 -1
  141. package/dist/components/ai-branch.mjs +3 -0
  142. package/dist/components/ai-chat.css +295 -0
  143. package/dist/components/ai-feedback.mjs +3 -0
  144. package/dist/components/ai-panel.css +218 -1
  145. package/dist/components/ai-threads.mjs +3 -0
  146. package/dist/components/ai-tool-call.mjs +1 -1
  147. package/dist/components/badge.css +1 -1
  148. package/dist/components/breadcrumb.css +0 -32
  149. package/dist/components/chart-calendar.mjs +1 -2
  150. package/dist/components/chart-graph.css +57 -0
  151. package/dist/components/chart-graph.mjs +4 -0
  152. package/dist/components/chart-map.mjs +3 -0
  153. package/dist/components/chart-pie.css +0 -6
  154. package/dist/components/chart-scatter.mjs +1 -1
  155. package/dist/components/charts.css +33 -1
  156. package/dist/components/charts.mjs +1 -0
  157. package/dist/components/content.css +3 -16
  158. package/dist/components/datatable.css +1 -1
  159. package/dist/components/date-picker.css +1 -1
  160. package/dist/components/kb-access.mjs +4 -0
  161. package/dist/components/kb-acl.mjs +4 -0
  162. package/dist/components/kb-agent.css +481 -0
  163. package/dist/components/kb-answer.mjs +4 -0
  164. package/dist/components/kb-audit.mjs +4 -0
  165. package/dist/components/kb-canvas.mjs +4 -0
  166. package/dist/components/kb-chain.mjs +4 -0
  167. package/dist/components/kb-checkpoint.mjs +4 -0
  168. package/dist/components/kb-chunks.mjs +4 -0
  169. package/dist/components/kb-citation.mjs +4 -0
  170. package/dist/components/kb-clarify.mjs +4 -0
  171. package/dist/components/kb-conflict.mjs +4 -0
  172. package/dist/components/kb-connector.mjs +4 -0
  173. package/dist/components/kb-data.css +649 -0
  174. package/dist/components/kb-eval.mjs +4 -0
  175. package/dist/components/kb-explain.mjs +4 -0
  176. package/dist/components/kb-filter.mjs +4 -0
  177. package/dist/components/kb-gap.mjs +4 -0
  178. package/dist/components/kb-ground.css +566 -0
  179. package/dist/components/kb-hittest.mjs +4 -0
  180. package/dist/components/kb-ingest.css +898 -0
  181. package/dist/components/kb-metadata.mjs +4 -0
  182. package/dist/components/kb-ops.css +548 -0
  183. package/dist/components/kb-passage.mjs +4 -0
  184. package/dist/components/kb-perm.css +444 -0
  185. package/dist/components/kb-pipeline.mjs +4 -0
  186. package/dist/components/kb-qa.mjs +4 -0
  187. package/dist/components/kb-rerank.mjs +4 -0
  188. package/dist/components/kb-retrieval.mjs +4 -0
  189. package/dist/components/kb-review.mjs +4 -0
  190. package/dist/components/kb-sandbox.mjs +4 -0
  191. package/dist/components/kb-search.css +940 -0
  192. package/dist/components/kb-segment.mjs +4 -0
  193. package/dist/components/kb-sources.mjs +4 -0
  194. package/dist/components/kb-sql.mjs +4 -0
  195. package/dist/components/kb-trace.mjs +4 -0
  196. package/dist/components/kb-visibility.mjs +4 -0
  197. package/dist/components/kb-who-can.mjs +4 -0
  198. package/dist/components/kb.css +252 -0
  199. package/dist/components/kb.mjs +9 -0
  200. package/dist/components/notification.css +2 -3
  201. package/dist/components/pagination.css +3 -1
  202. package/dist/components/pagination.mjs +1 -0
  203. package/dist/components/rating.mjs +1 -0
  204. package/dist/components/segmented.css +9 -1
  205. package/dist/components/segmented.mjs +1 -1
  206. package/dist/components/select.css +67 -4
  207. package/dist/components/steps.css +13 -3
  208. package/dist/components/steps.mjs +1 -0
  209. package/dist/components/switch.css +3 -1
  210. package/dist/components/switch.mjs +1 -0
  211. package/dist/components/table.mjs +1 -0
  212. package/dist/components/tabs.css +23 -0
  213. package/dist/components/tag-input.css +1 -6
  214. package/dist/components/upload.css +0 -15
  215. package/dist/index.d.mts +48 -11
  216. package/dist/index.mjs +13205 -3615
  217. package/dist/registry.json +366 -15
  218. package/dist/retro-effects.css +3 -16
  219. package/dist/tokens/retro-effects.css +3 -16
  220. package/dist/tokens/style-profiles.css +3 -2
  221. package/dist/tokens/typography.css +2 -0
  222. package/dist/tokens.css +5 -2
  223. package/dist/ui.css +5713 -267
  224. package/docs/research/2026-10-03-ai-kb-components.md +392 -0
  225. package/docs/research/2026-10-03-kb-permissions.md +992 -0
  226. package/docs/spec/ai-native.md +7 -4
  227. package/docs/spec/kb-family.md +443 -0
  228. 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(
@@ -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));
@@ -391,27 +468,46 @@ function buildTodoHead(done, total) {
391
468
  head.appendChild(bar);
392
469
  return head;
393
470
  }
471
+ var ICON_TODO_CHECK = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="m3.5 12.5 5 5L20.5 6" pathLength="1"/></svg>';
394
472
  function buildTodoItem(item, index) {
395
- const row = el("div", `ai-todo-item is-${item.status}`);
473
+ const row = h("div", `ai-todo-item is-${item.status}`);
396
474
  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));
475
+ row.appendChild(h("span", "ai-item-status"));
476
+ const check = h("span", "ai-todo-check is-anim");
477
+ check.setAttribute("aria-hidden", "true");
478
+ const checkSvg = svgIcon(ICON_TODO_CHECK);
479
+ if (checkSvg) check.appendChild(checkSvg);
480
+ row.appendChild(check);
481
+ row.appendChild(h("span", "ai-todo-text", item.content));
482
+ row.appendChild(h("span", "ai-todo-active", item.activeForm ?? item.content));
400
483
  return row;
401
484
  }
402
- function renderAiTodo(elm, items) {
403
- if (typeof document === "undefined") return;
404
- elm.textContent = "";
405
- const root = el("div", "ai-todo");
485
+ function renderAiTodo(el, items) {
486
+ if (typeof document === "undefined") return el;
487
+ el.textContent = "";
488
+ const root = h("div", "ai-todo");
406
489
  const list = Array.isArray(items) ? items : [];
407
490
  const done = list.filter((i) => i.status === "done").length;
408
491
  root.appendChild(buildTodoHead(done, list.length));
409
492
  list.forEach((item, i) => root.appendChild(buildTodoItem(item, i)));
410
493
  if (list.length > 0 && done === list.length) root.classList.add("is-complete");
411
- elm.appendChild(root);
494
+ el.appendChild(root);
495
+ return el;
412
496
  }
413
- function renderAiUsage(elm, usage, opts = {}) {
414
- if (typeof document === "undefined") return;
497
+ function metricField(src, key) {
498
+ return typeof src === "object" && src !== null ? src[key] : void 0;
499
+ }
500
+ function metricNum(v) {
501
+ return typeof v === "number" && Number.isFinite(v) ? v : void 0;
502
+ }
503
+ function metricStr(v) {
504
+ return typeof v === "string" && v ? v : void 0;
505
+ }
506
+ function trimNum(v) {
507
+ return String(Math.round(v * 10) / 10);
508
+ }
509
+ function renderAiUsage(el, usage, opts = {}) {
510
+ if (typeof document === "undefined") return el;
415
511
  const u = normalizeUsage(usage);
416
512
  const segs = [
417
513
  { key: "input", className: "ai-usage-seg--input", label: "\u8F93\u5165", value: u.input ?? 0 },
@@ -423,9 +519,9 @@ function renderAiUsage(elm, usage, opts = {}) {
423
519
  const activeSegs = segs.filter((s) => s.value > 0);
424
520
  const sum2 = activeSegs.reduce((acc, s) => acc + s.value, 0);
425
521
  const total = u.total ?? sum2;
426
- elm.textContent = "";
427
- const root = el("div", "ai-usage");
428
- const bar = el("div", "ai-usage-bar");
522
+ el.textContent = "";
523
+ const root = h("div", "ai-usage");
524
+ const bar = h("div", "ai-usage-bar");
429
525
  const summary = activeSegs.length > 0 ? activeSegs.map((s) => `${s.label} ${formatTokens(s.value)}`).join("\uFF0C") : "\u6682\u65E0\u7528\u91CF";
430
526
  bar.setAttribute("role", "img");
431
527
  bar.setAttribute("aria-label", `\u7528\u91CF\uFF1A${summary}`);
@@ -437,19 +533,27 @@ function renderAiUsage(elm, usage, opts = {}) {
437
533
  }
438
534
  root.appendChild(bar);
439
535
  if (activeSegs.length > 0) {
440
- const legend = el("div", "ai-usage-legend");
536
+ const legend = h("div", "ai-usage-legend");
441
537
  for (const s of activeSegs) {
442
- const item = el("span", "ai-usage-legend-item");
538
+ const item = h("span", "ai-usage-legend-item");
443
539
  const dot = document.createElement("i");
444
540
  dot.className = `ai-usage-dot ${s.className}`;
445
541
  dot.setAttribute("aria-hidden", "true");
446
542
  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)));
543
+ item.appendChild(h("span", "ai-usage-legend-label", s.label));
544
+ item.appendChild(h("span", "ai-usage-legend-value", formatTokens(s.value)));
449
545
  legend.appendChild(item);
450
546
  }
451
547
  root.appendChild(legend);
452
548
  }
549
+ const ttft = metricNum(opts.metrics?.ttftMs) ?? metricNum(metricField(usage, "ttftMs"));
550
+ const tps = metricNum(opts.metrics?.tokensPerSec) ?? metricNum(metricField(usage, "tokensPerSec"));
551
+ const finish = metricStr(opts.metrics?.finishReason) ?? metricStr(metricField(usage, "finishReason"));
552
+ const metricBits = [];
553
+ if (ttft != null) metricBits.push(`TTFT ${formatDuration(ttft)}`);
554
+ if (tps != null && tps > 0) metricBits.push(`${trimNum(tps)} tok/s`);
555
+ if (finish) metricBits.push(`finish: ${finish}`);
556
+ if (metricBits.length > 0) root.appendChild(h("div", "ai-usage-metrics", metricBits.join(" \xB7 ")));
453
557
  const totalParts = [formatTokens(total)];
454
558
  if (typeof opts.total === "number" && opts.total > 0) {
455
559
  const pct = Math.min(100, Math.round(total / opts.total * 100));
@@ -458,17 +562,18 @@ function renderAiUsage(elm, usage, opts = {}) {
458
562
  if (typeof opts.cost === "number" && Number.isFinite(opts.cost)) {
459
563
  totalParts.push(` \xB7 $${opts.cost.toFixed(4)}`);
460
564
  }
461
- root.appendChild(el("div", "ai-usage-total", totalParts.join("")));
462
- elm.appendChild(root);
565
+ root.appendChild(h("div", "ai-usage-total", totalParts.join("")));
566
+ el.appendChild(root);
567
+ return el;
463
568
  }
464
- function renderAiUsageRing(elm, usage, opts = {}) {
465
- if (typeof document === "undefined") return;
569
+ function renderAiUsageRing(el, usage, opts = {}) {
570
+ if (typeof document === "undefined") return el;
466
571
  const u = normalizeUsage(usage);
467
572
  const used = u.total ?? (u.input ?? 0) + (u.output ?? 0) + (u.cacheRead ?? 0) + (u.cacheWrite ?? 0) + (u.reasoning ?? 0);
468
573
  const hasLimit = typeof opts.total === "number" && opts.total > 0;
469
574
  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");
575
+ el.textContent = "";
576
+ const btn = h("button", "ai-usage-ring");
472
577
  btn.type = "button";
473
578
  btn.classList.add(pct >= 85 ? "is-hot" : pct >= 60 ? "is-warn" : "is-ok");
474
579
  btn.setAttribute("aria-haspopup", "true");
@@ -492,7 +597,7 @@ function renderAiUsageRing(elm, usage, opts = {}) {
492
597
  svg2.appendChild(c);
493
598
  }
494
599
  btn.appendChild(svg2);
495
- btn.appendChild(el("span", "ai-usage-ring-pct", hasLimit ? `${pct}%` : formatTokens(used)));
600
+ btn.appendChild(h("span", "ai-usage-ring-pct", hasLimit ? `${pct}%` : formatTokens(used)));
496
601
  let panel = null;
497
602
  const closePanel = () => {
498
603
  if (!panel) return;
@@ -506,10 +611,10 @@ function renderAiUsageRing(elm, usage, opts = {}) {
506
611
  closePanel();
507
612
  return;
508
613
  }
509
- const p = el("div", "popover ai-usage-popover");
614
+ const p = h("div", "popover ai-usage-popover");
510
615
  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");
616
+ p.appendChild(h("div", "ai-usage-popover-title", opts.title ?? "\u4E0A\u4E0B\u6587\u7A97\u53E3"));
617
+ const body = h("div", "ai-usage-popover-body");
513
618
  renderAiUsage(body, usage, opts);
514
619
  p.appendChild(body);
515
620
  openPopover(p, {
@@ -523,7 +628,8 @@ function renderAiUsageRing(elm, usage, opts = {}) {
523
628
  });
524
629
  panel = p;
525
630
  });
526
- elm.appendChild(btn);
631
+ el.appendChild(btn);
632
+ return el;
527
633
  }
528
634
  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
635
  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,46 +652,108 @@ var ICON_SPARKLES = svgWrap(
546
652
  // src/behaviors/ai-chat.ts
547
653
  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
654
  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");
655
+ function emit(target, name, detail) {
656
+ emitIcen(target, name, detail);
657
+ }
658
+ var REASONING_AUTO_CLOSE_MS = 1e3;
659
+ function setReasoningOpen(reasoning, open) {
660
+ const head = reasoning.querySelector(".ai-reasoning-head");
661
+ const body = reasoning.querySelector(".ai-reasoning-body");
662
+ head?.setAttribute("aria-expanded", String(open));
663
+ reasoning.classList.toggle("is-open", open);
664
+ if (body) body.hidden = !open;
665
+ }
666
+ function clearContinueSlot(scope) {
667
+ scope?.querySelectorAll(".ai-msg-continue").forEach((n) => n.remove());
668
+ }
669
+ function createAiStream(el) {
670
+ const reasoning = el.closest(".ai-reasoning");
671
+ const msgBody = el.closest(".ai-msg-body");
672
+ const msg = el.closest(".ai-msg");
551
673
  const startedAt = Date.now();
552
674
  let state = "streaming";
553
- el3.classList.add("is-streaming");
554
- el3.setAttribute("aria-busy", "true");
555
- reasoning?.classList.add("is-streaming");
675
+ el.classList.add("is-streaming");
676
+ el.setAttribute("aria-busy", "true");
677
+ if (reasoning) {
678
+ reasoning.classList.add("is-streaming");
679
+ setReasoningOpen(reasoning, true);
680
+ const head = reasoning.querySelector(".ai-reasoning-head");
681
+ if (head && head.dataset.reasoningStart == null) head.dataset.reasoningStart = String(startedAt);
682
+ }
683
+ clearContinueSlot(msg ?? msgBody ?? el);
684
+ const fillReasoningTime = () => {
685
+ if (!reasoning) return;
686
+ const time = reasoning.querySelector(".ai-reasoning-time");
687
+ if (!time) return;
688
+ const head = reasoning.querySelector(".ai-reasoning-head");
689
+ const raw = Number(head?.dataset.reasoningStart ?? "");
690
+ const t0 = Number.isFinite(raw) && raw > 0 ? raw : startedAt;
691
+ time.textContent = `\u601D\u8003 ${formatDuration(Math.max(0, Date.now() - t0))}`;
692
+ };
693
+ const scheduleReasoningAutoClose = () => {
694
+ if (!reasoning) return;
695
+ const r = reasoning;
696
+ if (r.__icenReasonUserTouched || r.__icenReasonAutoClosed) return;
697
+ const close = () => {
698
+ if (r.__icenReasonUserTouched || r.__icenReasonAutoClosed) return;
699
+ r.__icenReasonAutoClosed = true;
700
+ setReasoningOpen(r, false);
701
+ };
702
+ if (typeof window === "undefined") {
703
+ close();
704
+ return;
705
+ }
706
+ window.setTimeout(close, REASONING_AUTO_CLOSE_MS);
707
+ };
708
+ const appendContinueSlot = () => {
709
+ if (typeof document === "undefined") return;
710
+ const mount = msgBody ?? msg ?? (el.parentElement instanceof HTMLElement ? el.parentElement : null);
711
+ if (!mount || mount.querySelector(":scope > .ai-msg-continue")) return;
712
+ const btn = document.createElement("button");
713
+ btn.type = "button";
714
+ btn.className = "ai-msg-continue";
715
+ btn.textContent = "\u7EE7\u7EED\u751F\u6210";
716
+ btn.addEventListener("click", () => {
717
+ const target = msg ?? btn;
718
+ emit(target, "icen:ai-retry", { el: target });
719
+ });
720
+ mount.appendChild(btn);
721
+ };
556
722
  const settle = (finished) => {
557
723
  if (state !== "streaming") return;
558
724
  state = finished ? "done" : "cancelled";
559
- el3.classList.remove("is-streaming");
560
- el3.removeAttribute("aria-busy");
725
+ el.classList.remove("is-streaming");
726
+ el.removeAttribute("aria-busy");
561
727
  if (!reasoning) return;
562
728
  reasoning.classList.remove("is-streaming");
563
729
  if (!finished) return;
564
- const head = reasoning.querySelector(".ai-reasoning-head");
565
- const body = reasoning.querySelector(".ai-reasoning-body");
566
- head?.setAttribute("aria-expanded", "false");
567
- if (body) body.hidden = true;
568
- const time = reasoning.querySelector(".ai-reasoning-time");
569
- if (time) time.textContent = formatDuration(Date.now() - startedAt);
730
+ fillReasoningTime();
731
+ scheduleReasoningAutoClose();
570
732
  };
571
733
  return {
572
734
  append(text) {
573
735
  if (state !== "streaming" || !text) return;
574
- el3.textContent += text;
736
+ el.textContent += text;
575
737
  },
576
738
  done() {
577
739
  settle(true);
578
740
  },
579
741
  cancel() {
580
742
  settle(false);
743
+ appendContinueSlot();
581
744
  },
582
745
  fail() {
583
746
  if (state !== "streaming") return;
584
747
  state = "error";
585
- el3.classList.remove("is-streaming");
586
- el3.removeAttribute("aria-busy");
587
- el3.classList.add("is-error");
588
- reasoning?.classList.remove("is-streaming");
748
+ el.classList.remove("is-streaming");
749
+ el.removeAttribute("aria-busy");
750
+ el.classList.add("is-error");
751
+ if (reasoning) {
752
+ reasoning.classList.remove("is-streaming");
753
+ fillReasoningTime();
754
+ scheduleReasoningAutoClose();
755
+ }
756
+ appendContinueSlot();
589
757
  }
590
758
  };
591
759
  }
@@ -600,15 +768,9 @@ function safeHref(uri) {
600
768
  if (s.startsWith("javascript:") || s.startsWith("data:text/html")) return void 0;
601
769
  return uri;
602
770
  }
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
771
  function renderAiContentPart(part) {
610
772
  if (part.type === "text") {
611
- const span = el2("span", "ai-msg-text");
773
+ const span = h("span", "ai-msg-text");
612
774
  span.textContent = part.text;
613
775
  if (part.state === "streaming") span.classList.add("is-streaming");
614
776
  return span;
@@ -643,8 +805,8 @@ function renderAiContentPart(part) {
643
805
  return video;
644
806
  }
645
807
  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"));
808
+ const chip = h("span", "ai-msg-file");
809
+ chip.appendChild(h("span", "ai-msg-file-name", part.filename ?? "\u6587\u4EF6"));
648
810
  const href2 = part.url ? safeHref(part.url) : void 0;
649
811
  if (href2 && /^(https?:|data:)/i.test(href2)) {
650
812
  const a2 = document.createElement("a");
@@ -665,9 +827,18 @@ function renderAiContentPart(part) {
665
827
  return a;
666
828
  }
667
829
  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");
830
+ if (typeof document === "undefined") {
831
+ return {
832
+ el: scrollEl,
833
+ body: scrollEl,
834
+ setMeta: () => void 0,
835
+ setError: () => void 0,
836
+ stream: () => createAiStream(scrollEl)
837
+ };
838
+ }
839
+ const row = h("div", `ai-msg ai-msg--${model.role}`);
840
+ const avatar = h("span", "ai-msg-avatar", ROLE_AVATAR[model.role] ?? "");
841
+ const body = h("div", "ai-msg-body");
671
842
  row.appendChild(avatar);
672
843
  row.appendChild(body);
673
844
  const parts = typeof model.content === "string" ? [{ type: "text", text: model.content }] : model.content;
@@ -675,18 +846,18 @@ function renderAiMessage(scrollEl, model) {
675
846
  const node = renderAiContentPart(part);
676
847
  if (node) body.appendChild(node);
677
848
  }
678
- if (model.model) body.appendChild(el2("span", "ai-msg-model", model.model));
679
- const metaEl = el2("div", "ai-msg-meta");
849
+ if (model.model) body.appendChild(h("span", "ai-msg-model", model.model));
850
+ const metaEl = h("div", "ai-msg-meta");
680
851
  if (model.meta != null) metaEl.textContent = model.meta;
681
852
  row.appendChild(metaEl);
682
- const actions = el2("div", "ai-msg-actions");
683
- const copyBtn = el2("button", "ai-msg-action");
853
+ const actions = h("div", "ai-msg-actions");
854
+ const copyBtn = h("button", "ai-msg-action");
684
855
  copyBtn.type = "button";
685
856
  copyBtn.setAttribute("data-ai-msg-action", "copy");
686
857
  copyBtn.setAttribute("aria-label", "\u590D\u5236");
687
858
  const copyIcon = svgIcon(ICON_COPY);
688
859
  if (copyIcon) copyBtn.appendChild(copyIcon);
689
- const retryBtn = el2("button", "ai-msg-action");
860
+ const retryBtn = h("button", "ai-msg-action");
690
861
  retryBtn.type = "button";
691
862
  retryBtn.setAttribute("data-ai-msg-action", "retry");
692
863
  retryBtn.setAttribute("aria-label", "\u91CD\u8BD5");
@@ -704,13 +875,13 @@ function renderAiMessage(scrollEl, model) {
704
875
  row.classList.add("ai-msg--error");
705
876
  let errEl = row.querySelector(".ai-msg-error-text");
706
877
  if (!errEl) {
707
- errEl = el2("div", "ai-msg-error-text");
878
+ errEl = h("div", "ai-msg-error-text");
708
879
  body.appendChild(errEl);
709
880
  }
710
881
  errEl.textContent = message;
711
882
  },
712
883
  stream() {
713
- const span = el2("span", "ai-msg-stream-text");
884
+ const span = h("span", "ai-msg-stream-text");
714
885
  body.appendChild(span);
715
886
  return createAiStream(span);
716
887
  }
@@ -744,19 +915,35 @@ var ICON_DOC = svgWrap2(
744
915
  var ICON_BOT2 = svgWrap2(
745
916
  '<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
917
  );
747
- var REF_KIND_LABEL = {
748
- file: "\u6587\u4EF6",
749
- folder: "\u6587\u4EF6\u5939",
750
- doc: "\u6587\u6863",
751
- agent: "\u4EE3\u7406"
918
+ var REF_KIND_BUILTIN = {
919
+ file: { label: "\u6587\u4EF6", icon: ICON_FILE3, order: 0 },
920
+ folder: { label: "\u6587\u4EF6\u5939", icon: ICON_FOLDER, order: 1 },
921
+ doc: { label: "\u6587\u6863", icon: ICON_DOC, order: 2 },
922
+ agent: { label: "\u4EE3\u7406", icon: ICON_BOT2, order: 3 }
752
923
  };
753
- var REF_KIND_ICON = {
754
- file: ICON_FILE3,
755
- folder: ICON_FOLDER,
756
- doc: ICON_DOC,
757
- agent: ICON_BOT2
758
- };
759
- var REF_KIND_ORDER = ["file", "folder", "doc", "agent"];
924
+ var REF_KIND_ORDER_LAST = Number.MAX_SAFE_INTEGER;
925
+ var REF_KIND_REGISTRY = /* @__PURE__ */ new Map();
926
+ function registerRefKind(kind, def) {
927
+ const key = kind.trim();
928
+ if (key) REF_KIND_REGISTRY.set(key, def);
929
+ }
930
+ function getRefKindDef(kind) {
931
+ const reg = REF_KIND_REGISTRY.get(kind);
932
+ const builtin = REF_KIND_BUILTIN[kind];
933
+ return {
934
+ label: reg?.label ?? builtin?.label ?? kind,
935
+ icon: reg?.icon ?? builtin?.icon ?? ICON_DOC,
936
+ order: reg && typeof reg.order === "number" && Number.isFinite(reg.order) ? reg.order : builtin?.order ?? REF_KIND_ORDER_LAST
937
+ };
938
+ }
939
+ function knownRefKinds(sources) {
940
+ const kinds = new Set(Object.keys(REF_KIND_BUILTIN));
941
+ for (const key of REF_KIND_REGISTRY.keys()) kinds.add(key);
942
+ for (const s of sources) {
943
+ if (typeof s?.kind === "string" && s.kind) kinds.add(s.kind);
944
+ }
945
+ return Array.from(kinds).sort((a, b) => getRefKindDef(a).order - getRefKindDef(b).order);
946
+ }
760
947
  var RUNNING = /* @__PURE__ */ new WeakMap();
761
948
  var QUEUES = /* @__PURE__ */ new WeakMap();
762
949
  var MODELS = /* @__PURE__ */ new WeakMap();
@@ -765,18 +952,19 @@ var COMMANDS = /* @__PURE__ */ new WeakMap();
765
952
  var REF_SOURCES = /* @__PURE__ */ new WeakMap();
766
953
  var ACTIVE_REFS = /* @__PURE__ */ new WeakMap();
767
954
  var USAGE = /* @__PURE__ */ new WeakMap();
768
- var TODO = /* @__PURE__ */ new WeakMap();
955
+ var TODO_STATES = /* @__PURE__ */ new WeakMap();
769
956
  var HISTORIES = /* @__PURE__ */ new WeakMap();
770
957
  var POPUP = /* @__PURE__ */ new WeakMap();
771
958
  var SUBMITS = /* @__PURE__ */ new WeakMap();
959
+ var POPUP_SIZING = /* @__PURE__ */ new WeakMap();
772
960
  function isBrowser() {
773
961
  return typeof document !== "undefined" && typeof window !== "undefined";
774
962
  }
775
- function emit(target, name, detail) {
776
- target.dispatchEvent(new CustomEvent(name, { detail, bubbles: true }));
963
+ function emit2(target, name, detail) {
964
+ emitIcen(target, name, detail);
777
965
  }
778
- function resolveComposer(el3) {
779
- return el3.closest("[data-ai-composer]");
966
+ function resolveComposer(el) {
967
+ return el.closest("[data-ai-composer]");
780
968
  }
781
969
  function getTextarea(composer) {
782
970
  return composer.querySelector(".ai-composer-input");
@@ -841,8 +1029,8 @@ function renderQueueChips(composer) {
841
1029
  queueEl.replaceChildren(...queue.map((text, i) => makeChip(text, true, `\u79FB\u9664\u6392\u961F\u6D88\u606F ${i + 1}`)));
842
1030
  queueEl.hidden = queue.length === 0;
843
1031
  }
844
- function setComposerRunning(el3, running) {
845
- const composer = resolveComposer(el3);
1032
+ function setComposerRunning(el, running) {
1033
+ const composer = resolveComposer(el);
846
1034
  if (!composer) return;
847
1035
  RUNNING.set(composer, running);
848
1036
  composer.classList.toggle("is-running", running);
@@ -1006,6 +1194,13 @@ function settlePopup(s) {
1006
1194
  s.emptyEl.hidden = visibleOptions(s).length > 0;
1007
1195
  setPopupFocus(s, 0);
1008
1196
  }
1197
+ function filterPopupOptions(s, q) {
1198
+ const needle = q.trim().toLowerCase();
1199
+ for (const opt of s.options) {
1200
+ opt.hidden = needle !== "" && !(opt.dataset.hay ?? "").includes(needle);
1201
+ }
1202
+ settlePopup(s);
1203
+ }
1009
1204
  function popupKeydown(composer, s, e) {
1010
1205
  if (e.isComposing) return;
1011
1206
  switch (e.key) {
@@ -1045,13 +1240,16 @@ function closePopup(composer, restoreFocus = true) {
1045
1240
  }
1046
1241
  if (s.mode === "model") {
1047
1242
  composer.querySelector("[data-ai-model-open]")?.setAttribute("aria-expanded", "false");
1048
- }
1049
- if (s.mode !== "model") {
1243
+ } else {
1050
1244
  getTextarea(composer)?.setAttribute("aria-expanded", "false");
1051
1245
  }
1052
1246
  closePopover(s.panel);
1053
1247
  if (restoreFocus && s.returnFocus && s.returnFocus.isConnected) s.returnFocus.focus();
1054
1248
  }
1249
+ function resolvePopupSizing(composer, defaults) {
1250
+ const explicit = resolvePanelSizing(composer, POPUP_SIZING.get(composer));
1251
+ return { ...defaults, ...explicit };
1252
+ }
1055
1253
  function mountPopup(composer, init) {
1056
1254
  closePopup(composer, false);
1057
1255
  const panel = document.createElement("div");
@@ -1076,9 +1274,7 @@ function mountPopup(composer, init) {
1076
1274
  side: "top",
1077
1275
  align: "start",
1078
1276
  offset: 8,
1079
- minWidth: init.minWidth,
1080
- maxWidth: init.maxWidth,
1081
- maxHeight: init.maxHeight,
1277
+ sizing: resolvePopupSizing(composer, init.sizing),
1082
1278
  onClose: () => {
1083
1279
  if (POPUP.get(composer) === session) POPUP.delete(composer);
1084
1280
  panel.remove();
@@ -1101,6 +1297,16 @@ function mountPopup(composer, init) {
1101
1297
  });
1102
1298
  return session;
1103
1299
  }
1300
+ function appendPopupEmpty(s, text) {
1301
+ s.emptyEl.className = "ai-composer-popup-empty";
1302
+ s.emptyEl.textContent = text;
1303
+ s.emptyEl.hidden = true;
1304
+ s.panel.appendChild(s.emptyEl);
1305
+ }
1306
+ function popupSizingFor(box) {
1307
+ const w = box.getBoundingClientRect().width;
1308
+ return { minWidth: Math.min(Math.max(w, 200), 320), maxWidth: 360, maxHeight: 320 };
1309
+ }
1104
1310
  function openModelPopup(composer) {
1105
1311
  const cfg = MODELS.get(composer);
1106
1312
  if (!cfg) return;
@@ -1110,9 +1316,7 @@ function openModelPopup(composer) {
1110
1316
  const session = mountPopup(composer, {
1111
1317
  mode: "model",
1112
1318
  anchor,
1113
- minWidth: 220,
1114
- maxWidth: 320,
1115
- maxHeight: 340,
1319
+ sizing: { minWidth: 220, maxWidth: 320, maxHeight: 340 },
1116
1320
  returnFocus: btn ?? ta,
1117
1321
  pick: (opt) => {
1118
1322
  const provider = opt.dataset.provider ?? "";
@@ -1126,7 +1330,7 @@ function openModelPopup(composer) {
1126
1330
  MODEL_CTX.delete(composer);
1127
1331
  }
1128
1332
  updateModelButton(composer);
1129
- emit(composer, "icen:ai-model-change", { provider, model, label, context });
1333
+ emit2(composer, "icen:ai-model-change", { provider, model, label, context });
1130
1334
  refreshUsageRing(composer);
1131
1335
  closePopup(composer);
1132
1336
  },
@@ -1164,6 +1368,7 @@ function openModelPopup(composer) {
1164
1368
  opt.dataset.providerLabel = p.label;
1165
1369
  opt.dataset.model = m.id;
1166
1370
  opt.dataset.label = m.label;
1371
+ opt.dataset.hay = `${m.label} ${p.label}`.toLowerCase();
1167
1372
  if (typeof m.context === "number") opt.dataset.context = String(m.context);
1168
1373
  const selected = cfg.current.provider === p.id && cfg.current.model === m.id;
1169
1374
  opt.setAttribute("aria-selected", String(selected));
@@ -1188,10 +1393,7 @@ function openModelPopup(composer) {
1188
1393
  s.groups.push(group);
1189
1394
  }
1190
1395
  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);
1396
+ appendPopupEmpty(s, "\u65E0\u5339\u914D\u6A21\u578B");
1195
1397
  const foot = document.createElement("div");
1196
1398
  foot.className = "ai-composer-popup-foot";
1197
1399
  const usageCfg = USAGE.get(composer);
@@ -1211,12 +1413,7 @@ function openModelPopup(composer) {
1211
1413
  s.keydownHandler = (e) => popupKeydown(composer, s, e);
1212
1414
  input.addEventListener("keydown", s.keydownHandler);
1213
1415
  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);
1416
+ filterPopupOptions(s, input.value);
1220
1417
  });
1221
1418
  }
1222
1419
  });
@@ -1228,13 +1425,10 @@ function openCommandPopup(composer, token) {
1228
1425
  if (!commands || commands.length === 0) return;
1229
1426
  const box = composer.querySelector(".ai-composer-box") ?? composer;
1230
1427
  const ta = getTextarea(composer);
1231
- const boxRect = box.getBoundingClientRect();
1232
1428
  const session = mountPopup(composer, {
1233
1429
  mode: "command",
1234
1430
  anchor: box,
1235
- minWidth: Math.min(Math.max(boxRect.width, 200), 320),
1236
- maxWidth: 360,
1237
- maxHeight: 320,
1431
+ sizing: popupSizingFor(box),
1238
1432
  returnFocus: ta,
1239
1433
  pick: (opt) => {
1240
1434
  const name = opt.dataset.name ?? "";
@@ -1244,7 +1438,7 @@ function openCommandPopup(composer, token) {
1244
1438
  ta.value = "";
1245
1439
  autosizeTa(ta);
1246
1440
  }
1247
- emit(composer, "icen:ai-command", { name, args });
1441
+ emit2(composer, "icen:ai-command", { name, args });
1248
1442
  closePopup(composer);
1249
1443
  },
1250
1444
  build: (s) => {
@@ -1286,25 +1480,15 @@ function openCommandPopup(composer, token) {
1286
1480
  list.appendChild(group);
1287
1481
  s.groups.push(group);
1288
1482
  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);
1483
+ appendPopupEmpty(s, "\u65E0\u5339\u914D\u547D\u4EE4");
1293
1484
  }
1294
1485
  });
1295
1486
  ta?.setAttribute("aria-expanded", "true");
1296
- filterCommandOptions(session, token);
1487
+ filterPopupOptions(session, token);
1297
1488
  }
1298
1489
  function commandToken(value) {
1299
1490
  return value.slice(1).split(/\s/, 1)[0] ?? "";
1300
1491
  }
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
1492
  function updateCommandPopup(composer, s) {
1309
1493
  const ta = getTextarea(composer);
1310
1494
  const value = ta?.value ?? "";
@@ -1313,20 +1497,17 @@ function updateCommandPopup(composer, s) {
1313
1497
  maybeOpenTriggerPopups(composer);
1314
1498
  return;
1315
1499
  }
1316
- filterCommandOptions(s, commandToken(value));
1500
+ filterPopupOptions(s, commandToken(value));
1317
1501
  }
1318
1502
  function openRefPopup(composer, range, filter) {
1319
1503
  const sources = REF_SOURCES.get(composer);
1320
1504
  if (!sources || sources.length === 0) return;
1321
1505
  const box = composer.querySelector(".ai-composer-box") ?? composer;
1322
1506
  const ta = getTextarea(composer);
1323
- const boxRect = box.getBoundingClientRect();
1324
1507
  const session = mountPopup(composer, {
1325
1508
  mode: "ref",
1326
1509
  anchor: box,
1327
- minWidth: Math.min(Math.max(boxRect.width, 200), 320),
1328
- maxWidth: 360,
1329
- maxHeight: 320,
1510
+ sizing: popupSizingFor(box),
1330
1511
  returnFocus: ta,
1331
1512
  refRange: range,
1332
1513
  pick: (opt) => {
@@ -1349,7 +1530,8 @@ function openRefPopup(composer, range, filter) {
1349
1530
  list.className = "ai-composer-popup-list";
1350
1531
  list.setAttribute("role", "listbox");
1351
1532
  list.setAttribute("aria-label", "\u5F15\u7528");
1352
- for (const kind of REF_KIND_ORDER) {
1533
+ for (const kind of knownRefKinds(sources)) {
1534
+ const def = getRefKindDef(kind);
1353
1535
  const items = sources.filter((r) => r.kind === kind);
1354
1536
  if (items.length === 0) continue;
1355
1537
  const group = document.createElement("div");
@@ -1357,7 +1539,7 @@ function openRefPopup(composer, range, filter) {
1357
1539
  group.dataset.kind = kind;
1358
1540
  group.appendChild(Object.assign(document.createElement("div"), {
1359
1541
  className: "ai-composer-popup-group-label",
1360
- textContent: REF_KIND_LABEL[kind]
1542
+ textContent: def.label
1361
1543
  }));
1362
1544
  for (const r of items) {
1363
1545
  const opt = document.createElement("button");
@@ -1368,7 +1550,7 @@ function openRefPopup(composer, range, filter) {
1368
1550
  opt.dataset.hay = `${r.label} ${r.sub ?? ""}`.toLowerCase();
1369
1551
  const icon = document.createElement("span");
1370
1552
  icon.className = "ai-composer-popup-option-icon";
1371
- const svg2 = svgIcon(REF_KIND_ICON[kind]);
1553
+ const svg2 = svgIcon(def.icon);
1372
1554
  if (svg2) icon.appendChild(svg2);
1373
1555
  const label = document.createElement("span");
1374
1556
  label.className = "ai-composer-popup-option-label";
@@ -1387,21 +1569,11 @@ function openRefPopup(composer, range, filter) {
1387
1569
  s.groups.push(group);
1388
1570
  }
1389
1571
  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);
1572
+ appendPopupEmpty(s, "\u65E0\u5339\u914D\u5F15\u7528");
1394
1573
  }
1395
1574
  });
1396
1575
  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);
1576
+ filterPopupOptions(session, filter);
1405
1577
  }
1406
1578
  function updateRefPopup(composer, s) {
1407
1579
  const ta = getTextarea(composer);
@@ -1412,7 +1584,7 @@ function updateRefPopup(composer, s) {
1412
1584
  return;
1413
1585
  }
1414
1586
  s.refRange = { start: t.start, end: t.end };
1415
- filterRefOptions(s, t.filter);
1587
+ filterPopupOptions(s, t.filter);
1416
1588
  }
1417
1589
  function findRefToken(ta) {
1418
1590
  const value = ta.value;
@@ -1469,7 +1641,7 @@ function addActiveRef(composer, source) {
1469
1641
  actives.push(source);
1470
1642
  ACTIVE_REFS.set(composer, actives);
1471
1643
  renderRefs(composer);
1472
- emit(composer, "icen:ai-ref", { action: "add", ref: source });
1644
+ emit2(composer, "icen:ai-ref", { action: "add", ref: source });
1473
1645
  }
1474
1646
  function removeActiveRef(composer, source) {
1475
1647
  const actives = ACTIVE_REFS.get(composer) ?? [];
@@ -1489,24 +1661,24 @@ function removeActiveRef(composer, source) {
1489
1661
  autosizeTa(ta);
1490
1662
  }
1491
1663
  }
1492
- emit(composer, "icen:ai-ref", { action: "remove", ref: source });
1664
+ emit2(composer, "icen:ai-ref", { action: "remove", ref: source });
1493
1665
  }
1494
1666
  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();
1667
+ const h2 = HISTORIES.get(composer) ?? { items: [], index: -1 };
1668
+ if (h2.items[h2.items.length - 1] !== text) {
1669
+ h2.items.push(text);
1670
+ if (h2.items.length > HISTORY_LIMIT) h2.items.shift();
1499
1671
  }
1500
- h.index = -1;
1501
- HISTORIES.set(composer, h);
1672
+ h2.index = -1;
1673
+ HISTORIES.set(composer, h2);
1502
1674
  }
1503
1675
  function setupComposer(composer) {
1504
- const el3 = composer;
1505
- if (el3.__icenAiComposerInit) return void 0;
1506
- el3.__icenAiComposerInit = true;
1676
+ const el = composer;
1677
+ if (el.__icenAiComposerInit) return void 0;
1678
+ el.__icenAiComposerInit = true;
1507
1679
  const ta = getTextarea(composer);
1508
1680
  if (!ta) {
1509
- el3.__icenAiComposerInit = false;
1681
+ el.__icenAiComposerInit = false;
1510
1682
  return void 0;
1511
1683
  }
1512
1684
  const queueEl = composer.querySelector(".ai-composer-queue");
@@ -1530,10 +1702,10 @@ function setupComposer(composer) {
1530
1702
  autosizeTa(ta);
1531
1703
  if (isRunning()) {
1532
1704
  enqueue(text);
1533
- emit(composer, "icen:ai-queue", { text });
1705
+ emit2(composer, "icen:ai-queue", { text });
1534
1706
  } else {
1535
1707
  pushHistory(composer, text);
1536
- emit(composer, "icen:ai-send", { text });
1708
+ emit2(composer, "icen:ai-send", { text });
1537
1709
  }
1538
1710
  };
1539
1711
  SUBMITS.set(composer, submit);
@@ -1541,28 +1713,28 @@ function setupComposer(composer) {
1541
1713
  const queue = QUEUES.get(composer) ?? [];
1542
1714
  if (queue.length === 0) return;
1543
1715
  const index = queue.length - 1;
1544
- const text = queue.pop();
1716
+ const text = queue.pop() ?? "";
1545
1717
  QUEUES.set(composer, queue);
1546
1718
  renderQueue();
1547
1719
  ta.value = text;
1548
1720
  autosizeTa(ta);
1549
- emit(composer, "icen:ai-dequeue", { index });
1721
+ emit2(composer, "icen:ai-dequeue", { index });
1550
1722
  };
1551
1723
  const browseHistory = (delta) => {
1552
- const h = HISTORIES.get(composer);
1553
- if (!h || h.items.length === 0) return;
1724
+ const h2 = HISTORIES.get(composer);
1725
+ if (!h2 || h2.items.length === 0) return;
1554
1726
  if (delta === -1) {
1555
- h.index = h.index === -1 ? h.items.length - 1 : Math.max(0, h.index - 1);
1727
+ h2.index = h2.index === -1 ? h2.items.length - 1 : Math.max(0, h2.index - 1);
1556
1728
  } else {
1557
- h.index += 1;
1558
- if (h.index >= h.items.length) {
1559
- h.index = -1;
1729
+ h2.index += 1;
1730
+ if (h2.index >= h2.items.length) {
1731
+ h2.index = -1;
1560
1732
  ta.value = "";
1561
1733
  autosizeTa(ta);
1562
1734
  return;
1563
1735
  }
1564
1736
  }
1565
- ta.value = h.items[h.index];
1737
+ ta.value = h2.items[h2.index] ?? "";
1566
1738
  autosizeTa(ta);
1567
1739
  };
1568
1740
  const onKeydown = (e) => {
@@ -1578,15 +1750,15 @@ function setupComposer(composer) {
1578
1750
  return;
1579
1751
  }
1580
1752
  if (e.key === "ArrowUp") {
1581
- const h = HISTORIES.get(composer);
1582
- const browsing = (h?.index ?? -1) >= 0;
1753
+ const h2 = HISTORIES.get(composer);
1754
+ const browsing = (h2?.index ?? -1) >= 0;
1583
1755
  if (ta.value === "") {
1584
1756
  if (isRunning() && (QUEUES.get(composer) ?? []).length > 0) {
1585
1757
  e.preventDefault();
1586
1758
  takeBackLastQueued();
1587
1759
  return;
1588
1760
  }
1589
- if (h && h.items.length > 0) {
1761
+ if (h2 && h2.items.length > 0) {
1590
1762
  e.preventDefault();
1591
1763
  browseHistory(-1);
1592
1764
  }
@@ -1597,8 +1769,8 @@ function setupComposer(composer) {
1597
1769
  return;
1598
1770
  }
1599
1771
  if (e.key === "ArrowDown") {
1600
- const h = HISTORIES.get(composer);
1601
- if (h && h.index >= 0) {
1772
+ const h2 = HISTORIES.get(composer);
1773
+ if (h2 && h2.index >= 0) {
1602
1774
  e.preventDefault();
1603
1775
  browseHistory(1);
1604
1776
  }
@@ -1606,8 +1778,8 @@ function setupComposer(composer) {
1606
1778
  };
1607
1779
  const onInput = () => {
1608
1780
  autosizeTa(ta);
1609
- const h = HISTORIES.get(composer);
1610
- if (h && h.index >= 0 && ta.value !== h.items[h.index]) h.index = -1;
1781
+ const h2 = HISTORIES.get(composer);
1782
+ if (h2 && h2.index >= 0 && ta.value !== h2.items[h2.index]) h2.index = -1;
1611
1783
  const session = POPUP.get(composer);
1612
1784
  if (session) {
1613
1785
  if (session.mode === "command") updateCommandPopup(composer, session);
@@ -1625,7 +1797,7 @@ function setupComposer(composer) {
1625
1797
  };
1626
1798
  const onSendClick = () => {
1627
1799
  if (isRunning()) {
1628
- emit(composer, "icen:ai-stop", {});
1800
+ emit2(composer, "icen:ai-stop", {});
1629
1801
  } else {
1630
1802
  submit();
1631
1803
  }
@@ -1639,7 +1811,7 @@ function setupComposer(composer) {
1639
1811
  queue.splice(index, 1);
1640
1812
  QUEUES.set(composer, queue);
1641
1813
  renderQueue();
1642
- emit(composer, "icen:ai-dequeue", { index });
1814
+ emit2(composer, "icen:ai-dequeue", { index });
1643
1815
  }
1644
1816
  };
1645
1817
  const onRefsClick = (e) => {
@@ -1668,7 +1840,7 @@ function setupComposer(composer) {
1668
1840
  }
1669
1841
  attachEl.hidden = false;
1670
1842
  }
1671
- emit(composer, "icen:ai-attach", { files });
1843
+ emit2(composer, "icen:ai-attach", { files });
1672
1844
  };
1673
1845
  const onFileChange = () => {
1674
1846
  const files = Array.from(fileInput.files ?? []);
@@ -1735,7 +1907,7 @@ function setupComposer(composer) {
1735
1907
  composer.querySelector("[data-ai-attach]")?.removeEventListener("click", onAttachClick);
1736
1908
  fileInput.removeEventListener("change", onFileChange);
1737
1909
  fileInput.remove();
1738
- el3.__icenAiComposerInit = false;
1910
+ el.__icenAiComposerInit = false;
1739
1911
  };
1740
1912
  }
1741
1913
  function initAiComposer(root) {
@@ -1757,9 +1929,9 @@ function initAiComposer(root) {
1757
1929
  cleanups.length = 0;
1758
1930
  };
1759
1931
  }
1760
- function setComposerModels(el3, providers, current) {
1932
+ function setComposerModels(el, providers, current) {
1761
1933
  if (!isBrowser()) return;
1762
- const composer = resolveComposer(el3);
1934
+ const composer = resolveComposer(el);
1763
1935
  if (!composer) return;
1764
1936
  const list = (Array.isArray(providers) ? providers : []).filter(
1765
1937
  (p) => p && Array.isArray(p.models) && p.models.length > 0
@@ -1786,9 +1958,9 @@ function setComposerModels(el3, providers, current) {
1786
1958
  updateModelButton(composer);
1787
1959
  refreshUsageRing(composer);
1788
1960
  }
1789
- function setComposerCommands(el3, commands) {
1961
+ function setComposerCommands(el, commands) {
1790
1962
  if (!isBrowser()) return;
1791
- const composer = resolveComposer(el3);
1963
+ const composer = resolveComposer(el);
1792
1964
  if (!composer) return;
1793
1965
  const list = (Array.isArray(commands) ? commands : []).filter((c) => c && typeof c.name === "string" && c.name);
1794
1966
  if (list.length === 0) {
@@ -1799,9 +1971,9 @@ function setComposerCommands(el3, commands) {
1799
1971
  }
1800
1972
  COMMANDS.set(composer, list);
1801
1973
  }
1802
- function setComposerRefs(el3, sources) {
1974
+ function setComposerRefs(el, sources) {
1803
1975
  if (!isBrowser()) return;
1804
- const composer = resolveComposer(el3);
1976
+ const composer = resolveComposer(el);
1805
1977
  if (!composer) return;
1806
1978
  const list = (Array.isArray(sources) ? sources : []).filter((s) => s && typeof s.label === "string" && s.label);
1807
1979
  REF_SOURCES.set(composer, list);
@@ -1812,9 +1984,9 @@ function setComposerRefs(el3, sources) {
1812
1984
  ensureRefsRow(composer);
1813
1985
  renderRefs(composer);
1814
1986
  }
1815
- function setComposerUsage(el3, usage, opts = {}) {
1987
+ function setComposerUsage(el, usage, opts = {}) {
1816
1988
  if (!isBrowser()) return;
1817
- const composer = resolveComposer(el3);
1989
+ const composer = resolveComposer(el);
1818
1990
  if (!composer) return;
1819
1991
  if (!usage) {
1820
1992
  USAGE.delete(composer);
@@ -1827,24 +1999,24 @@ function setComposerUsage(el3, usage, opts = {}) {
1827
1999
  syncToolbar(composer);
1828
2000
  refreshUsageRing(composer);
1829
2001
  }
1830
- function setComposerTodo(el3, items) {
2002
+ function setComposerTodo(el, items) {
1831
2003
  if (!isBrowser()) return;
1832
- const composer = resolveComposer(el3);
2004
+ const composer = resolveComposer(el);
1833
2005
  if (!composer) return;
1834
2006
  let chip = composer.querySelector(".ai-composer-todo");
1835
2007
  if (!items || items.length === 0) {
1836
- TODO.delete(composer);
2008
+ TODO_STATES.delete(composer);
1837
2009
  chip?.remove();
1838
2010
  return;
1839
2011
  }
1840
- TODO.set(composer, items);
2012
+ TODO_STATES.set(composer, items);
1841
2013
  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);
2014
+ const btn = document.createElement("button");
2015
+ btn.type = "button";
2016
+ btn.className = "ai-composer-todo";
2017
+ btn.setAttribute("aria-haspopup", "true");
2018
+ btn.addEventListener("click", () => {
2019
+ const cur = TODO_STATES.get(composer);
1848
2020
  if (!cur) return;
1849
2021
  const p = document.createElement("div");
1850
2022
  p.className = "popover ai-composer-todo-pop";
@@ -1857,12 +2029,13 @@ function setComposerTodo(el3, items) {
1857
2029
  renderAiTodo(body, cur);
1858
2030
  p.append(title, body);
1859
2031
  openPopover(p, {
1860
- anchor: chip,
2032
+ anchor: btn,
1861
2033
  side: "top",
1862
2034
  align: "end",
1863
2035
  onClose: () => p.remove()
1864
2036
  });
1865
2037
  });
2038
+ chip = btn;
1866
2039
  const icon = svgIcon(
1867
2040
  '<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
2041
  );
@@ -1885,21 +2058,14 @@ function setComposerTodo(el3, items) {
1885
2058
  if (label) label.textContent = `${done}/${items.length}`;
1886
2059
  }
1887
2060
  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 = {}) {
2061
+ function bindComposer(el, opts = {}) {
1897
2062
  const noop = () => void 0;
1898
2063
  if (!isBrowser()) return { unbind: noop };
1899
- const composer = resolveComposer(el3);
2064
+ const composer = resolveComposer(el);
1900
2065
  if (!composer) return { unbind: noop };
1901
2066
  if (BOUND.has(composer)) return { unbind: noop };
1902
2067
  BOUND.add(composer);
2068
+ if (opts.popoverSizing) POPUP_SIZING.set(composer, opts.popoverSizing);
1903
2069
  const disposers = [];
1904
2070
  const usageCfg = opts.usage;
1905
2071
  const manageRunning = opts.running ?? true;
@@ -1922,7 +2088,7 @@ function bindComposer(el3, opts = {}) {
1922
2088
  total: contextEstimate(u)
1923
2089
  };
1924
2090
  } else if (usageCfg.from === "billing") {
1925
- accumulateUsage(billing, detail.usage);
2091
+ billing = addUsage(billing, detail.usage);
1926
2092
  usage = billing;
1927
2093
  } else {
1928
2094
  usage = usageCfg.from.summary();
@@ -1935,6 +2101,9 @@ function bindComposer(el3, opts = {}) {
1935
2101
  document.addEventListener("icen:ai-done", onDone);
1936
2102
  disposers.push(() => document.removeEventListener("icen:ai-done", onDone));
1937
2103
  }
2104
+ if (opts.client && !opts.messages) {
2105
+ console.warn("[ai-composer] client \u6A21\u5F0F\u9700\u8981 opts.messages\uFF0C\u5DF2\u964D\u7EA7\u4E3A\u7EAF\u72B6\u6001\u7ED1\u5B9A");
2106
+ }
1938
2107
  if (opts.client && opts.messages) {
1939
2108
  const client = opts.client;
1940
2109
  const messages = opts.messages;
@@ -1975,10 +2144,10 @@ function bindComposer(el3, opts = {}) {
1975
2144
  setComposerRunning(composer, false);
1976
2145
  const queue = QUEUES.get(composer) ?? [];
1977
2146
  if (queue.length > 0) {
1978
- const next = queue.shift();
2147
+ const next = queue.shift() ?? "";
1979
2148
  QUEUES.set(composer, queue);
1980
2149
  renderQueueChips(composer);
1981
- emit(composer, "icen:ai-dequeue", { index: 0 });
2150
+ emit2(composer, "icen:ai-dequeue", { index: 0 });
1982
2151
  send(next);
1983
2152
  }
1984
2153
  })();
@@ -2023,6 +2192,7 @@ function bindComposer(el3, opts = {}) {
2023
2192
  export {
2024
2193
  bindComposer,
2025
2194
  initAiComposer,
2195
+ registerRefKind,
2026
2196
  setComposerCommands,
2027
2197
  setComposerModels,
2028
2198
  setComposerRefs,