@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 AI_STATUSES = [
3
+ "pending",
4
+ "running",
5
+ "streaming",
6
+ "approval",
7
+ "done",
8
+ "error",
9
+ "cancelled"
10
+ ];
2
11
  function aiStatusLabel(s) {
3
12
  const labels = {
4
13
  pending: "\u7B49\u5F85",
@@ -135,10 +144,9 @@ var BUILTIN_KINDS = {
135
144
  note: { label: "\u7B14\u8BB0", icon: ICON_FILE_TEXT, tint: "muted" },
136
145
  chart: { label: "\u56FE\u8868", icon: ICON_CHART, tint: "info", summarize: sum((i) => pickStr(i, ["title", "name"])) }
137
146
  };
138
- var FALLBACK_KIND = { label: "\u7B14\u8BB0", icon: ICON_FILE_TEXT, tint: "muted" };
139
147
  var kindRegistry = new Map(Object.entries(BUILTIN_KINDS));
140
148
  function getAiKind(name) {
141
- return kindRegistry.get(name.trim().toLowerCase()) ?? kindRegistry.get("note") ?? FALLBACK_KIND;
149
+ return kindRegistry.get(name.trim().toLowerCase()) ?? BUILTIN_KINDS.note;
142
150
  }
143
151
  function str(v) {
144
152
  return typeof v === "string" && v ? v : void 0;
@@ -267,6 +275,12 @@ function normalizeContentParts(raw) {
267
275
  }
268
276
  return out;
269
277
  }
278
+ function h(tag, className, text) {
279
+ const node = document.createElement(tag);
280
+ if (className) node.className = className;
281
+ if (text != null) node.textContent = text;
282
+ return node;
283
+ }
270
284
  function svgIcon(svg2) {
271
285
  if (typeof document === "undefined" || typeof DOMParser === "undefined") return null;
272
286
  let parsed;
@@ -279,15 +293,15 @@ function svgIcon(svg2) {
279
293
  if (!root || root.tagName.toLowerCase() !== "svg" || parsed.querySelector("parsererror")) {
280
294
  return null;
281
295
  }
282
- const sanitize = (el2) => {
283
- for (const attr of Array.from(el2.attributes)) {
296
+ const sanitize = (el) => {
297
+ for (const attr of Array.from(el.attributes)) {
284
298
  const name = attr.name.toLowerCase();
285
- if (name.startsWith("on")) el2.removeAttribute(attr.name);
299
+ if (name.startsWith("on")) el.removeAttribute(attr.name);
286
300
  if ((name === "href" || name === "xlink:href") && attr.value.trim().toLowerCase().startsWith("javascript:")) {
287
- el2.removeAttribute(attr.name);
301
+ el.removeAttribute(attr.name);
288
302
  }
289
303
  }
290
- for (const child of Array.from(el2.children)) {
304
+ for (const child of Array.from(el.children)) {
291
305
  const tag = child.tagName.toLowerCase();
292
306
  if (tag === "script" || tag === "foreignobject") {
293
307
  child.remove();
@@ -300,21 +314,22 @@ function svgIcon(svg2) {
300
314
  return document.importNode(root, true);
301
315
  }
302
316
 
317
+ // src/behaviors/events.ts
318
+ function emitIcen(source, type, detail) {
319
+ return source.dispatchEvent(
320
+ new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
321
+ );
322
+ }
323
+
303
324
  // src/behaviors/ai-chat.ts
304
325
  function safeHref(uri) {
305
326
  const s = uri.trim().toLowerCase();
306
327
  if (s.startsWith("javascript:") || s.startsWith("data:text/html")) return void 0;
307
328
  return uri;
308
329
  }
309
- function el(tag, className, text) {
310
- const node = document.createElement(tag);
311
- if (className) node.className = className;
312
- if (text != null) node.textContent = text;
313
- return node;
314
- }
315
330
  function renderAiContentPart(part) {
316
331
  if (part.type === "text") {
317
- const span = el("span", "ai-msg-text");
332
+ const span = h("span", "ai-msg-text");
318
333
  span.textContent = part.text;
319
334
  if (part.state === "streaming") span.classList.add("is-streaming");
320
335
  return span;
@@ -349,8 +364,8 @@ function renderAiContentPart(part) {
349
364
  return video;
350
365
  }
351
366
  if (part.type === "file") {
352
- const chip = el("span", "ai-msg-file");
353
- chip.appendChild(el("span", "ai-msg-file-name", part.filename ?? "\u6587\u4EF6"));
367
+ const chip = h("span", "ai-msg-file");
368
+ chip.appendChild(h("span", "ai-msg-file-name", part.filename ?? "\u6587\u4EF6"));
354
369
  const href2 = part.url ? safeHref(part.url) : void 0;
355
370
  if (href2 && /^(https?:|data:)/i.test(href2)) {
356
371
  const a2 = document.createElement("a");
@@ -373,7 +388,10 @@ function renderAiContentPart(part) {
373
388
 
374
389
  // src/behaviors/charts.ts
375
390
  var SVG_NS = "http://www.w3.org/2000/svg";
391
+ var REGISTERED_TONES = /* @__PURE__ */ new Map();
376
392
  function toneVar(tone = "accent") {
393
+ const registered = REGISTERED_TONES.get(tone);
394
+ if (registered) return registered;
377
395
  switch (tone) {
378
396
  case "success":
379
397
  return "var(--token-success)";
@@ -405,15 +423,15 @@ function sampleLabels(labels, max = 7) {
405
423
  return out;
406
424
  }
407
425
  function svgEl(tag, attrs) {
408
- const el2 = document.createElementNS(SVG_NS, tag);
409
- for (const [k, v] of Object.entries(attrs)) el2.setAttribute(k, v);
410
- return el2;
426
+ const el = document.createElementNS(SVG_NS, tag);
427
+ for (const [k, v] of Object.entries(attrs)) el.setAttribute(k, v);
428
+ return el;
411
429
  }
412
- function renderEmpty(el2, label) {
430
+ function renderEmpty(el, label) {
413
431
  const box = document.createElement("div");
414
432
  box.className = "chart-empty";
415
433
  box.textContent = label ?? "\u6682\u65E0\u6570\u636E";
416
- el2.appendChild(box);
434
+ el.appendChild(box);
417
435
  }
418
436
  function legendItem(label, valueText, tone) {
419
437
  const row = document.createElement("div");
@@ -479,30 +497,30 @@ var CHART_EYE_OFF_SVG = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24
479
497
  function parseChartSvg(svg2) {
480
498
  try {
481
499
  const doc = new DOMParser().parseFromString(svg2, "image/svg+xml");
482
- const el2 = doc.documentElement;
483
- return el2 && el2.tagName.toLowerCase() === "svg" && !doc.querySelector("parsererror") ? document.importNode(el2, true) : null;
500
+ const el = doc.documentElement;
501
+ return el && el.tagName.toLowerCase() === "svg" && !doc.querySelector("parsererror") ? document.importNode(el, true) : null;
484
502
  } catch {
485
503
  return null;
486
504
  }
487
505
  }
488
- function withChrome(el2, opts, hasLegend, paint, state) {
489
- if (el2.dataset.chartBody != null) {
490
- el2.textContent = "";
491
- paint(el2);
506
+ function withChrome(el, opts, hasLegend, paint, state) {
507
+ if (el.dataset.chartBody != null) {
508
+ el.textContent = "";
509
+ paint(el);
492
510
  return;
493
511
  }
494
- el2.textContent = "";
512
+ el.textContent = "";
495
513
  const body = document.createElement("div");
496
514
  body.className = "chart-body";
497
515
  body.dataset.chartBody = "";
498
516
  if (!opts.title && !hasLegend) {
499
- el2.appendChild(body);
517
+ el.appendChild(body);
500
518
  paint(body);
501
519
  return;
502
520
  }
503
521
  let hidden = state && state.hidden != null ? state.hidden : opts.legend === false;
504
522
  if (state) state.hidden = hidden;
505
- el2.setAttribute("data-legend-hidden", String(hidden));
523
+ el.setAttribute("data-legend-hidden", String(hidden));
506
524
  const head = document.createElement("div");
507
525
  head.className = "chart-head";
508
526
  const title = document.createElement("div");
@@ -514,7 +532,7 @@ function withChrome(el2, opts, hasLegend, paint, state) {
514
532
  eye.className = "chart-legend-eye";
515
533
  eye.dataset.chartLegendEye = "";
516
534
  const syncEye = () => {
517
- const nowHidden = el2.getAttribute("data-legend-hidden") === "true";
535
+ const nowHidden = el.getAttribute("data-legend-hidden") === "true";
518
536
  eye.setAttribute("aria-pressed", String(!nowHidden));
519
537
  eye.setAttribute("aria-label", nowHidden ? "\u663E\u793A\u56FE\u4F8B" : "\u9690\u85CF\u56FE\u4F8B");
520
538
  eye.setAttribute("title", nowHidden ? "\u663E\u793A\u56FE\u4F8B" : "\u9690\u85CF\u56FE\u4F8B");
@@ -522,22 +540,19 @@ function withChrome(el2, opts, hasLegend, paint, state) {
522
540
  if (icon) eye.replaceChildren(icon);
523
541
  };
524
542
  eye.addEventListener("click", () => {
525
- const nowHidden = el2.getAttribute("data-legend-hidden") === "true";
543
+ const nowHidden = el.getAttribute("data-legend-hidden") === "true";
526
544
  hidden = !nowHidden;
527
545
  if (state) state.hidden = hidden;
528
- el2.setAttribute("data-legend-hidden", String(hidden));
546
+ el.setAttribute("data-legend-hidden", String(hidden));
529
547
  syncEye();
530
- el2.dispatchEvent(new CustomEvent("icen:chart-legend-visibility", {
531
- bubbles: true,
532
- detail: { visible: hidden ? false : true }
533
- }));
548
+ emitIcen(el, "icen:chart-legend-visibility", { visible: hidden ? false : true });
534
549
  });
535
550
  syncEye();
536
551
  head.append(title, eye);
537
- el2.append(head, body);
552
+ el.append(head, body);
538
553
  paint(body);
539
554
  }
540
- function toggleLegend(items, scope, _opts) {
555
+ function toggleLegend(items, scope) {
541
556
  const legend = document.createElement("div");
542
557
  legend.className = "chart-legend chart-legend--toggle";
543
558
  for (const item of items) {
@@ -556,10 +571,7 @@ function toggleLegend(items, scope, _opts) {
556
571
  scope.querySelectorAll(`[data-chart-key="${CSS.escape(item.key)}"]`).forEach((m) => {
557
572
  m.classList.toggle("is-off", off);
558
573
  });
559
- legend.dispatchEvent(new CustomEvent("icen:chart-legend-toggle", {
560
- bubbles: true,
561
- detail: { key: item.key, seriesIndex: item.si, hidden: off }
562
- }));
574
+ emitIcen(legend, "icen:chart-legend-toggle", { key: item.key, seriesIndex: item.si, hidden: off });
563
575
  };
564
576
  row.addEventListener("click", toggle);
565
577
  row.addEventListener("keydown", (e) => {
@@ -577,12 +589,12 @@ function toggleLegend(items, scope, _opts) {
577
589
  }
578
590
  return legend;
579
591
  }
580
- function renderVBar(el2, opts) {
581
- if (typeof document === "undefined") return;
592
+ function renderVBar(el, opts) {
593
+ if (typeof document === "undefined") return el;
582
594
  const seriesList = normalizeSeriesInput(opts);
583
595
  const labels = opts.labels ?? [];
584
596
  const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
585
- withChrome(el2, opts, seriesList.length > 1, (body) => {
597
+ withChrome(el, opts, seriesList.length > 1, (body) => {
586
598
  if (n === 0 || seriesList.length === 0) {
587
599
  renderEmpty(body, opts.emptyLabel);
588
600
  return;
@@ -629,12 +641,14 @@ function renderVBar(el2, opts) {
629
641
  wrap.appendChild(track);
630
642
  }
631
643
  body.append(wrap, labelsRow(labelsUsed));
632
- if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap, opts));
644
+ if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
633
645
  });
646
+ attachChartInteraction(el, "vbar");
647
+ return el;
634
648
  }
635
- function renderHBar(el2, opts) {
636
- if (typeof document === "undefined") return;
637
- withChrome(el2, opts, false, (body) => {
649
+ function renderHBar(el, opts) {
650
+ if (typeof document === "undefined") return el;
651
+ withChrome(el, opts, false, (body) => {
638
652
  const n = Math.min(opts.labels.length, opts.values.length);
639
653
  if (n === 0) {
640
654
  renderEmpty(body, opts.emptyLabel);
@@ -667,10 +681,12 @@ function renderHBar(el2, opts) {
667
681
  });
668
682
  body.appendChild(wrap);
669
683
  });
684
+ attachChartInteraction(el, "hbar");
685
+ return el;
670
686
  }
671
- function renderStack(el2, opts) {
672
- if (typeof document === "undefined") return;
673
- withChrome(el2, opts, opts.segments.length > 0, (body) => {
687
+ function renderStack(el, opts) {
688
+ if (typeof document === "undefined") return el;
689
+ withChrome(el, opts, opts.segments.length > 0, (body) => {
674
690
  const total = opts.segments.reduce((sum2, s) => sum2 + s.value, 0);
675
691
  if (opts.segments.length === 0 || total <= 0) {
676
692
  renderEmpty(body, opts.emptyLabel);
@@ -678,13 +694,13 @@ function renderStack(el2, opts) {
678
694
  }
679
695
  const bar = document.createElement("div");
680
696
  bar.className = "chart-stack";
681
- for (const seg of opts.segments) {
697
+ for (const [segIndex, seg] of opts.segments.entries()) {
682
698
  const s = document.createElement("div");
683
699
  s.className = "chart-stack-seg";
684
700
  s.style.width = `${seg.value / total * 100}%`;
685
701
  if (seg.tone) s.style.setProperty("--chart-tone", toneVar(seg.tone));
686
702
  s.setAttribute("title", `${seg.label}: ${fmt(opts, seg.value)}`);
687
- setMarkData(s, { index: opts.segments.indexOf(seg), label: seg.label, value: seg.value });
703
+ setMarkData(s, { index: segIndex, label: seg.label, value: seg.value });
688
704
  bar.appendChild(s);
689
705
  }
690
706
  const legend = document.createElement("div");
@@ -694,10 +710,12 @@ function renderStack(el2, opts) {
694
710
  }
695
711
  body.append(bar, legend);
696
712
  });
713
+ attachChartInteraction(el, "stack");
714
+ return el;
697
715
  }
698
- function renderDonut(el2, opts) {
699
- if (typeof document === "undefined") return;
700
- withChrome(el2, opts, opts.segments.length > 0, (body) => {
716
+ function renderDonut(el, opts) {
717
+ if (typeof document === "undefined") return el;
718
+ withChrome(el, opts, opts.segments.length > 0, (body) => {
701
719
  const total = opts.segments.reduce((sum2, s) => sum2 + s.value, 0);
702
720
  if (opts.segments.length === 0 || total <= 0) {
703
721
  renderEmpty(body, opts.emptyLabel);
@@ -743,14 +761,16 @@ function renderDonut(el2, opts) {
743
761
  wrap.append(svg2, legend);
744
762
  body.appendChild(wrap);
745
763
  });
764
+ attachChartInteraction(el, "donut");
765
+ return el;
746
766
  }
747
767
  var lineGradientSeq = 0;
748
- function renderLine(el2, opts) {
749
- if (typeof document === "undefined") return;
768
+ function renderLine(el, opts) {
769
+ if (typeof document === "undefined") return el;
750
770
  const seriesList = normalizeSeriesInput(opts);
751
771
  const labels = opts.labels ?? [];
752
772
  const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
753
- withChrome(el2, opts, seriesList.length > 1, (body) => {
773
+ withChrome(el, opts, seriesList.length > 1, (body) => {
754
774
  if (n === 0 || seriesList.length === 0) {
755
775
  renderEmpty(body, opts.emptyLabel);
756
776
  return;
@@ -851,12 +871,14 @@ function renderLine(el2, opts) {
851
871
  if (defs.firstChild) svg2.insertBefore(defs, svg2.firstChild);
852
872
  wrap.append(svg2, labelsRow(sampleLabels(labelsUsed)));
853
873
  body.appendChild(wrap);
854
- if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap, opts));
874
+ if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
855
875
  });
876
+ attachChartInteraction(el, "line");
877
+ return el;
856
878
  }
857
- function renderRadar(el2, opts) {
858
- if (typeof document === "undefined") return;
859
- withChrome(el2, opts, opts.series.length > 1, (body) => {
879
+ function renderRadar(el, opts) {
880
+ if (typeof document === "undefined") return el;
881
+ withChrome(el, opts, opts.series.length > 1, (body) => {
860
882
  const axes = opts.axes;
861
883
  const n = axes.length;
862
884
  if (n < 3 || opts.series.length === 0) {
@@ -957,13 +979,12 @@ function renderRadar(el2, opts) {
957
979
  }
958
980
  legendItems.push({ key: s.name || `\u7CFB\u5217${si + 1}`, label: s.name || `\u7CFB\u5217${si + 1}`, tone: s.tone, si });
959
981
  });
960
- wrap.append(svg2, opts.series.length > 1 ? toggleLegend(legendItems, wrap, opts) : (() => {
961
- const l = document.createElement("div");
962
- l.className = "chart-legend";
963
- return l;
964
- })());
982
+ const legend = opts.series.length > 1 ? toggleLegend(legendItems, wrap) : null;
983
+ wrap.append(svg2, ...legend ? [legend] : []);
965
984
  body.appendChild(wrap);
966
985
  });
986
+ attachChartInteraction(el, "radar");
987
+ return el;
967
988
  }
968
989
  function niceCeil(v) {
969
990
  if (v <= 0) return 1;
@@ -982,9 +1003,42 @@ function isoDate(d) {
982
1003
  const day = String(d.getDate()).padStart(2, "0");
983
1004
  return `${d.getFullYear()}-${m}-${day}`;
984
1005
  }
985
- function renderHeatmap(el2, opts) {
986
- if (typeof document === "undefined") return;
987
- withChrome(el2, opts, true, (body) => {
1006
+ function weekdayLabelAt(defaults, day) {
1007
+ return defaults[(day + 6) % 7] ?? "";
1008
+ }
1009
+ function levelOf(v, max) {
1010
+ return v <= 0 ? 0 : Math.min(4, Math.ceil(v / max * 4));
1011
+ }
1012
+ function padFirstWeek(grid, firstDate, weekStart) {
1013
+ const first = /* @__PURE__ */ new Date(`${firstDate}T00:00:00`);
1014
+ const pad = (first.getDay() + 7 - weekStart) % 7;
1015
+ for (let i = 0; i < pad; i++) {
1016
+ const blank = document.createElement("span");
1017
+ blank.className = "chart-heatmap-cell is-blank";
1018
+ grid.appendChild(blank);
1019
+ }
1020
+ return pad;
1021
+ }
1022
+ function heatmapLegend(labels) {
1023
+ const legend = document.createElement("div");
1024
+ legend.className = "chart-heatmap-legend";
1025
+ const less = document.createElement("span");
1026
+ less.textContent = labels?.less ?? "\u5C11";
1027
+ const more = document.createElement("span");
1028
+ more.textContent = labels?.more ?? "\u591A";
1029
+ legend.appendChild(less);
1030
+ for (let lv = 0; lv <= 4; lv++) {
1031
+ const cell = document.createElement("span");
1032
+ cell.className = "chart-heatmap-cell";
1033
+ cell.dataset.level = String(lv);
1034
+ legend.appendChild(cell);
1035
+ }
1036
+ legend.appendChild(more);
1037
+ return legend;
1038
+ }
1039
+ function renderHeatmap(el, opts) {
1040
+ if (typeof document === "undefined") return el;
1041
+ withChrome(el, opts, true, (body) => {
988
1042
  let entries;
989
1043
  if (opts.data && opts.data.length > 0) {
990
1044
  entries = opts.data;
@@ -1002,49 +1056,30 @@ function renderHeatmap(el2, opts) {
1002
1056
  const weeks = opts.weeks ?? Math.ceil(entries.length / 7);
1003
1057
  if (entries.length > weeks * 7) entries = entries.slice(entries.length - weeks * 7);
1004
1058
  const max = maxOf(entries.map((e) => e.value));
1005
- const levelOf = (v) => v <= 0 ? 0 : Math.min(4, Math.ceil(v / max * 4));
1006
1059
  const wrap = document.createElement("div");
1007
1060
  wrap.className = "chart-heatmap";
1008
1061
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
1009
1062
  const grid = document.createElement("div");
1010
1063
  grid.className = "chart-heatmap-grid";
1011
1064
  grid.setAttribute("role", "img");
1012
- const first = /* @__PURE__ */ new Date(`${entries[0].date}T00:00:00`);
1013
- const pad = (first.getDay() + 6) % 7;
1014
- for (let i = 0; i < pad; i++) {
1015
- const blank = document.createElement("span");
1016
- blank.className = "chart-heatmap-cell is-blank";
1017
- grid.appendChild(blank);
1018
- }
1019
- for (const entry of entries) {
1065
+ padFirstWeek(grid, entries[0].date, opts.weekStart === 0 ? 0 : 1);
1066
+ for (const [i, entry] of entries.entries()) {
1020
1067
  const cell = document.createElement("span");
1021
1068
  cell.className = "chart-heatmap-cell";
1022
- cell.dataset.level = String(levelOf(entry.value));
1069
+ cell.dataset.level = String(levelOf(entry.value, max));
1023
1070
  cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
1024
- setMarkData(cell, { index: entries.indexOf(entry), label: entry.date, value: entry.value });
1071
+ setMarkData(cell, { index: i, label: entry.date, value: entry.value });
1025
1072
  grid.appendChild(cell);
1026
1073
  }
1027
- const legend = document.createElement("div");
1028
- legend.className = "chart-heatmap-legend";
1029
- const less = document.createElement("span");
1030
- less.textContent = "\u5C11";
1031
- const more = document.createElement("span");
1032
- more.textContent = "\u591A";
1033
- legend.appendChild(less);
1034
- for (let lv = 0; lv <= 4; lv++) {
1035
- const cell = document.createElement("span");
1036
- cell.className = "chart-heatmap-cell";
1037
- cell.dataset.level = String(lv);
1038
- legend.appendChild(cell);
1039
- }
1040
- legend.appendChild(more);
1041
- wrap.append(grid, legend);
1074
+ wrap.append(grid, heatmapLegend(opts.labels));
1042
1075
  body.appendChild(wrap);
1043
1076
  });
1077
+ attachChartInteraction(el, "heatmap");
1078
+ return el;
1044
1079
  }
1045
- function renderSparkline(el2, opts) {
1046
- if (typeof document === "undefined") return;
1047
- withChrome(el2, opts, false, (body) => {
1080
+ function renderSparkline(el, opts) {
1081
+ if (typeof document === "undefined") return el;
1082
+ withChrome(el, opts, false, (body) => {
1048
1083
  const values = opts.values;
1049
1084
  if (values.length === 0) {
1050
1085
  renderEmpty(body, opts.emptyLabel);
@@ -1098,10 +1133,12 @@ function renderSparkline(el2, opts) {
1098
1133
  wrap.appendChild(svg2);
1099
1134
  body.appendChild(wrap);
1100
1135
  });
1136
+ attachChartInteraction(el, "sparkline");
1137
+ return el;
1101
1138
  }
1102
- function renderGauge(el2, opts) {
1103
- if (typeof document === "undefined") return;
1104
- el2.textContent = "";
1139
+ function renderGauge(el, opts) {
1140
+ if (typeof document === "undefined") return el;
1141
+ el.textContent = "";
1105
1142
  const max = opts.max ?? 100;
1106
1143
  const ratio = max > 0 ? Math.min(1, Math.max(0, opts.value / max)) : 0;
1107
1144
  const radius = 42;
@@ -1143,11 +1180,13 @@ function renderGauge(el2, opts) {
1143
1180
  center.appendChild(lab);
1144
1181
  }
1145
1182
  wrap.append(svg2, center);
1146
- el2.appendChild(wrap);
1183
+ el.appendChild(wrap);
1184
+ attachChartInteraction(el, "gauge");
1185
+ return el;
1147
1186
  }
1148
- function renderCalendar(el2, opts) {
1149
- if (typeof document === "undefined") return;
1150
- withChrome(el2, opts, true, (body) => {
1187
+ function renderCalendar(el, opts) {
1188
+ if (typeof document === "undefined") return el;
1189
+ withChrome(el, opts, true, (body) => {
1151
1190
  let entries;
1152
1191
  if (opts.data && opts.data.length > 0) {
1153
1192
  entries = opts.data;
@@ -1160,6 +1199,10 @@ function renderCalendar(el2, opts) {
1160
1199
  const wrap = document.createElement("div");
1161
1200
  wrap.className = "chart-calendar";
1162
1201
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
1202
+ const weekStart = opts.weekStart === 0 ? 0 : 1;
1203
+ const weekdayDefaults = ["\u4E00", "", "\u4E09", "", "\u4E94", "", "\u65E5"];
1204
+ const weekdayLabels = opts.labels?.weekdays ?? weekdayDefaults.map((_, i) => weekdayLabelAt(weekdayDefaults, (weekStart + i) % 7));
1205
+ const monthLabel = (m) => opts.labels?.months?.[m] ?? `${m + 1}\u6708`;
1163
1206
  const grid = document.createElement("div");
1164
1207
  grid.className = "chart-heatmap-grid";
1165
1208
  grid.setAttribute("role", "img");
@@ -1167,20 +1210,13 @@ function renderCalendar(el2, opts) {
1167
1210
  months.className = "chart-calendar-months";
1168
1211
  const weekdayRow = document.createElement("div");
1169
1212
  weekdayRow.className = "chart-calendar-weekdays";
1170
- ["\u4E00", "", "\u4E09", "", "\u4E94", "", "\u65E5"].forEach((w) => {
1213
+ weekdayLabels.forEach((w) => {
1171
1214
  const s = document.createElement("span");
1172
1215
  s.textContent = w;
1173
1216
  weekdayRow.appendChild(s);
1174
1217
  });
1175
1218
  const max = maxOf(entries.map((e) => e.value));
1176
- const levelOf = (v) => v <= 0 ? 0 : Math.min(4, Math.ceil(v / max * 4));
1177
- const first = /* @__PURE__ */ new Date(`${entries[0].date}T00:00:00`);
1178
- const pad = (first.getDay() + 6) % 7;
1179
- for (let i = 0; i < pad; i++) {
1180
- const blank = document.createElement("span");
1181
- blank.className = "chart-heatmap-cell is-blank";
1182
- grid.appendChild(blank);
1183
- }
1219
+ const pad = padFirstWeek(grid, entries[0].date, weekStart);
1184
1220
  let lastMonth = -1;
1185
1221
  entries.forEach((entry, i) => {
1186
1222
  const d = /* @__PURE__ */ new Date(`${entry.date}T00:00:00`);
@@ -1189,40 +1225,29 @@ function renderCalendar(el2, opts) {
1189
1225
  lastMonth = d.getMonth();
1190
1226
  const label = document.createElement("span");
1191
1227
  label.className = "chart-calendar-month";
1192
- label.textContent = `${d.getMonth() + 1}\u6708`;
1228
+ label.textContent = monthLabel(d.getMonth());
1193
1229
  label.style.setProperty("--col", String(col));
1194
1230
  months.appendChild(label);
1195
1231
  }
1196
1232
  const cell = document.createElement("span");
1197
1233
  cell.className = "chart-heatmap-cell";
1198
- cell.dataset.level = String(levelOf(entry.value));
1234
+ cell.dataset.level = String(levelOf(entry.value, max));
1199
1235
  cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
1200
1236
  setMarkData(cell, { index: i, label: entry.date, value: entry.value });
1201
1237
  grid.appendChild(cell);
1202
1238
  });
1203
- const legend = document.createElement("div");
1204
- legend.className = "chart-heatmap-legend";
1205
- const less = document.createElement("span");
1206
- less.textContent = "\u5C11";
1207
- const more = document.createElement("span");
1208
- more.textContent = "\u591A";
1209
- legend.appendChild(less);
1210
- for (let lv = 0; lv <= 4; lv++) {
1211
- const cell = document.createElement("span");
1212
- cell.className = "chart-heatmap-cell";
1213
- cell.dataset.level = String(lv);
1214
- legend.appendChild(cell);
1215
- }
1216
- legend.appendChild(more);
1239
+ const legend = heatmapLegend(opts.labels);
1217
1240
  const columns = Math.max(1, Math.ceil((pad + entries.length) / 7));
1218
1241
  months.style.setProperty("--columns", String(columns));
1219
1242
  wrap.append(months, weekdayRow, grid, legend);
1220
1243
  body.appendChild(wrap);
1221
1244
  });
1245
+ attachChartInteraction(el, "calendar");
1246
+ return el;
1222
1247
  }
1223
- function renderScatter(el2, opts) {
1224
- if (typeof document === "undefined") return;
1225
- withChrome(el2, opts, false, (body) => {
1248
+ function renderScatter(el, opts) {
1249
+ if (typeof document === "undefined") return el;
1250
+ withChrome(el, opts, false, (body) => {
1226
1251
  const pts = opts.points.filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y));
1227
1252
  if (pts.length === 0) {
1228
1253
  renderEmpty(body, opts.emptyLabel);
@@ -1266,7 +1291,7 @@ function renderScatter(el2, opts) {
1266
1291
  "font-size": "8",
1267
1292
  fill: "var(--token-text-faint)"
1268
1293
  });
1269
- txt.textContent = String(Math.round(tv * 10) / 10);
1294
+ txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
1270
1295
  svg2.appendChild(txt);
1271
1296
  }
1272
1297
  for (let i = 0; i <= 4; i++) {
@@ -1288,7 +1313,7 @@ function renderScatter(el2, opts) {
1288
1313
  "font-size": "8",
1289
1314
  fill: "var(--token-text-faint)"
1290
1315
  });
1291
- txt.textContent = String(Math.round(tv * 10) / 10);
1316
+ txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
1292
1317
  svg2.appendChild(txt);
1293
1318
  }
1294
1319
  if (opts.yLabel) {
@@ -1312,7 +1337,7 @@ function renderScatter(el2, opts) {
1312
1337
  "stroke-width": "1"
1313
1338
  });
1314
1339
  const title = svgEl("title", {});
1315
- title.textContent = `${p.label ?? `#${i + 1}`}: (${p.x}, ${p.y}${p.size != null ? `, size ${p.size}` : ""})`;
1340
+ title.textContent = `${p.label ?? `#${i + 1}`}: (${fmt(opts, p.x)}, ${fmt(opts, p.y)}${p.size != null ? `, size ${fmt(opts, p.size)}` : ""})`;
1316
1341
  c.appendChild(title);
1317
1342
  setMarkData(c, { index: i, label: p.label ?? `#${i + 1}`, value: p.y });
1318
1343
  c.dataset.chartX = String(p.x);
@@ -1321,6 +1346,8 @@ function renderScatter(el2, opts) {
1321
1346
  wrap.appendChild(svg2);
1322
1347
  body.appendChild(wrap);
1323
1348
  });
1349
+ attachChartInteraction(el, "scatter");
1350
+ return el;
1324
1351
  }
1325
1352
  function chartFormatValue(format) {
1326
1353
  if (!format) return (v) => String(v);
@@ -1451,7 +1478,7 @@ function normalizeChartSpec(raw) {
1451
1478
  }
1452
1479
  }
1453
1480
  if (!src || typeof src !== "object" || Array.isArray(src)) {
1454
- if (Array.isArray(raw)) src = { data: raw };
1481
+ if (Array.isArray(src)) src = { data: src };
1455
1482
  else return {};
1456
1483
  }
1457
1484
  const s = src;
@@ -1521,6 +1548,7 @@ function normalizeChartSpec(raw) {
1521
1548
  }
1522
1549
  return spec;
1523
1550
  }
1551
+ var TEMPORAL_HIT_RATIO = 0.6;
1524
1552
  function looksTemporal(labels) {
1525
1553
  if (labels.length === 0) return false;
1526
1554
  let hits = 0;
@@ -1534,7 +1562,7 @@ function looksTemporal(labels) {
1534
1562
  else if (/^(v|ver\.?|version[ .]?)?\d+(\.\d+)+$/i.test(l)) hits++;
1535
1563
  else if (/^\d{4}$/.test(l)) hits++;
1536
1564
  }
1537
- return hits / labels.length >= 0.6;
1565
+ return hits / labels.length >= TEMPORAL_HIT_RATIO;
1538
1566
  }
1539
1567
  function inferChartType(spec) {
1540
1568
  if (spec.points && spec.points.length > 0) return "scatter";
@@ -1569,7 +1597,7 @@ function dispatchChartEvent(root, type, mark, chart, phase, pointer) {
1569
1597
  pointerX: pointer?.x,
1570
1598
  pointerY: pointer?.y
1571
1599
  };
1572
- root.dispatchEvent(new CustomEvent(`icen:chart-${type}`, { detail, bubbles: true }));
1600
+ emitIcen(root, `icen:chart-${type}`, detail);
1573
1601
  }
1574
1602
  var tooltipEl = null;
1575
1603
  function chartTooltip() {
@@ -1582,6 +1610,12 @@ function chartTooltip() {
1582
1610
  }
1583
1611
  return tooltipEl;
1584
1612
  }
1613
+ function applyTooltipSizing(host) {
1614
+ const tip = chartTooltip();
1615
+ const raw = host?.closest(".chart, [data-chart-root]")?.getAttribute("data-panel-max") ?? null;
1616
+ const n = raw != null ? Number(raw) : NaN;
1617
+ tip.style.maxWidth = Number.isFinite(n) && n > 0 ? `${n}px` : "";
1618
+ }
1585
1619
  function tooltipText(mark) {
1586
1620
  const label = mark.getAttribute("data-chart-label") ?? "";
1587
1621
  const series = mark.getAttribute("data-chart-series-name");
@@ -1601,6 +1635,101 @@ function moveTooltip(x, y) {
1601
1635
  tip.style.left = `${Math.round(left)}px`;
1602
1636
  tip.style.top = `${Math.round(top)}px`;
1603
1637
  }
1638
+ var chartDelegateMeta = /* @__PURE__ */ new WeakMap();
1639
+ var chartDelegateUnbind = /* @__PURE__ */ new WeakMap();
1640
+ function bindChartEvents(root) {
1641
+ const noop = () => void 0;
1642
+ if (typeof document === "undefined") return noop;
1643
+ const attached = root;
1644
+ if (attached.__icenChartBound) {
1645
+ const prev = chartDelegateUnbind.get(root);
1646
+ if (prev) return prev;
1647
+ }
1648
+ for (let p = root.parentElement; p; p = p.parentElement) {
1649
+ if (p.__icenChartBound) return noop;
1650
+ }
1651
+ const typeNow = () => chartDelegateMeta.get(root)?.type() ?? "vbar";
1652
+ const tipEnabled = () => chartDelegateMeta.get(root)?.tooltip() ?? true;
1653
+ const markOf = (e) => {
1654
+ const t = e.target;
1655
+ return t instanceof Element ? t.closest("[data-chart-mark]") : null;
1656
+ };
1657
+ let hoverMark = null;
1658
+ const enterMark = (mark, x, y) => {
1659
+ hoverMark = mark;
1660
+ mark.classList.add("is-hover");
1661
+ dispatchChartEvent(root, "hover", mark, typeNow(), "enter", { x, y });
1662
+ if (tipEnabled()) {
1663
+ const tip = chartTooltip();
1664
+ tip.textContent = tooltipText(mark);
1665
+ applyTooltipSizing(mark);
1666
+ tip.hidden = false;
1667
+ moveTooltip(x, y);
1668
+ }
1669
+ };
1670
+ const leaveMark = (pointer) => {
1671
+ if (!hoverMark) return;
1672
+ hoverMark.classList.remove("is-hover");
1673
+ dispatchChartEvent(root, "hover", hoverMark, typeNow(), "leave", pointer);
1674
+ hoverMark = null;
1675
+ chartTooltip().hidden = true;
1676
+ };
1677
+ const bindings = [];
1678
+ const on = (type, fn) => {
1679
+ const listener = (evt) => fn(evt);
1680
+ root.addEventListener(type, listener);
1681
+ bindings.push([type, listener]);
1682
+ };
1683
+ on("pointerover", (e) => {
1684
+ const mark = markOf(e);
1685
+ if (!mark || mark === hoverMark) return;
1686
+ leaveMark({ x: e.clientX, y: e.clientY });
1687
+ enterMark(mark, e.clientX, e.clientY);
1688
+ });
1689
+ on("pointermove", (e) => {
1690
+ if (hoverMark && tipEnabled()) {
1691
+ dispatchChartEvent(root, "hover", hoverMark, typeNow(), "move", { x: e.clientX, y: e.clientY });
1692
+ moveTooltip(e.clientX, e.clientY);
1693
+ }
1694
+ });
1695
+ on("pointerout", (e) => {
1696
+ const mark = markOf(e);
1697
+ if (mark && mark === hoverMark) leaveMark({ x: e.clientX, y: e.clientY });
1698
+ });
1699
+ on("pointerleave", () => leaveMark());
1700
+ on("click", (e) => {
1701
+ const mark = markOf(e);
1702
+ if (mark) dispatchChartEvent(root, "click", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
1703
+ });
1704
+ on("dblclick", (e) => {
1705
+ const mark = markOf(e);
1706
+ if (mark) dispatchChartEvent(root, "dblclick", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
1707
+ });
1708
+ on("contextmenu", (e) => {
1709
+ const mark = markOf(e);
1710
+ if (!mark) return;
1711
+ e.preventDefault();
1712
+ dispatchChartEvent(root, "contextmenu", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
1713
+ });
1714
+ const unbind = () => {
1715
+ for (const [type, listener] of bindings) root.removeEventListener(type, listener);
1716
+ bindings.length = 0;
1717
+ attached.__icenChartBound = false;
1718
+ chartDelegateUnbind.delete(root);
1719
+ if (hoverMark) {
1720
+ hoverMark.classList.remove("is-hover");
1721
+ hoverMark = null;
1722
+ }
1723
+ chartTooltip().hidden = true;
1724
+ };
1725
+ attached.__icenChartBound = true;
1726
+ chartDelegateUnbind.set(root, unbind);
1727
+ return unbind;
1728
+ }
1729
+ function attachChartInteraction(el, type) {
1730
+ chartDelegateMeta.set(el, { type: () => type, tooltip: () => true });
1731
+ bindChartEvents(el);
1732
+ }
1604
1733
  function hasLegendOf(spec) {
1605
1734
  switch (spec.type) {
1606
1735
  case "donut":
@@ -1617,15 +1746,12 @@ function hasLegendOf(spec) {
1617
1746
  return false;
1618
1747
  }
1619
1748
  }
1620
- function renderChart(el2, raw) {
1749
+ function renderChart(el, raw) {
1621
1750
  const noop = () => void 0;
1622
1751
  if (typeof document === "undefined") {
1623
- return { el: el2, spec: {}, update: noop, on: () => noop, destroy: noop };
1752
+ return { el, spec: {}, update: noop, on: () => noop, destroy: noop };
1624
1753
  }
1625
- const root = el2;
1626
- const attached = root;
1627
- const wasBound = attached.__icenChartBound === true;
1628
- attached.__icenChartBound = true;
1754
+ const root = el;
1629
1755
  let current = normalizeChartSpec(raw);
1630
1756
  const typeOf = () => current.type ?? "vbar";
1631
1757
  const legendState = { hidden: null };
@@ -1660,10 +1786,10 @@ function renderChart(el2, raw) {
1660
1786
  break;
1661
1787
  }
1662
1788
  case "heatmap":
1663
- renderHeatmap(body, { values: spec.values, data: spec.data, weeks: spec.weeks, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1789
+ renderHeatmap(body, { values: spec.values, data: spec.data, weeks: spec.weeks, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend, weekStart: spec.i18n?.weekStart, labels: spec.i18n });
1664
1790
  break;
1665
1791
  case "calendar":
1666
- renderCalendar(body, { dates: spec.dates, values: spec.values, data: spec.data, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1792
+ renderCalendar(body, { dates: spec.dates, values: spec.values, data: spec.data, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend, weekStart: spec.i18n?.weekStart, labels: spec.i18n });
1667
1793
  break;
1668
1794
  case "sparkline":
1669
1795
  renderSparkline(body, { labels: spec.labels ?? [], values: spec.values ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
@@ -1679,63 +1805,8 @@ function renderChart(el2, raw) {
1679
1805
  }
1680
1806
  });
1681
1807
  };
1682
- if (!wasBound) {
1683
- const markOf = (e) => {
1684
- const t = e.target;
1685
- return t instanceof Element ? t.closest("[data-chart-mark]") : null;
1686
- };
1687
- const tipEnabled = () => current.tooltip !== false;
1688
- let hoverMark = null;
1689
- const enterMark = (mark, x, y) => {
1690
- hoverMark = mark;
1691
- mark.classList.add("is-hover");
1692
- dispatchChartEvent(root, "hover", mark, typeOf(), "enter", { x, y });
1693
- if (tipEnabled()) {
1694
- const tip = chartTooltip();
1695
- tip.textContent = tooltipText(mark);
1696
- tip.hidden = false;
1697
- moveTooltip(x, y);
1698
- }
1699
- };
1700
- const leaveMark = (pointer) => {
1701
- if (!hoverMark) return;
1702
- hoverMark.classList.remove("is-hover");
1703
- dispatchChartEvent(root, "hover", hoverMark, typeOf(), "leave", pointer);
1704
- hoverMark = null;
1705
- chartTooltip().hidden = true;
1706
- };
1707
- root.addEventListener("pointerover", (e) => {
1708
- const mark = markOf(e);
1709
- if (!mark || mark === hoverMark) return;
1710
- leaveMark({ x: e.clientX, y: e.clientY });
1711
- enterMark(mark, e.clientX, e.clientY);
1712
- });
1713
- root.addEventListener("pointermove", (e) => {
1714
- if (hoverMark && tipEnabled()) {
1715
- dispatchChartEvent(root, "hover", hoverMark, typeOf(), "move", { x: e.clientX, y: e.clientY });
1716
- moveTooltip(e.clientX, e.clientY);
1717
- }
1718
- });
1719
- root.addEventListener("pointerout", (e) => {
1720
- const mark = markOf(e);
1721
- if (mark && mark === hoverMark) leaveMark({ x: e.clientX, y: e.clientY });
1722
- });
1723
- root.addEventListener("pointerleave", () => leaveMark());
1724
- root.addEventListener("click", (e) => {
1725
- const mark = markOf(e);
1726
- if (mark) dispatchChartEvent(root, "click", mark, typeOf(), void 0, { x: e.clientX, y: e.clientY });
1727
- });
1728
- root.addEventListener("dblclick", (e) => {
1729
- const mark = markOf(e);
1730
- if (mark) dispatchChartEvent(root, "dblclick", mark, typeOf(), void 0, { x: e.clientX, y: e.clientY });
1731
- });
1732
- root.addEventListener("contextmenu", (e) => {
1733
- const mark = markOf(e);
1734
- if (!mark) return;
1735
- e.preventDefault();
1736
- dispatchChartEvent(root, "contextmenu", mark, typeOf(), void 0, { x: e.clientX, y: e.clientY });
1737
- });
1738
- }
1808
+ chartDelegateMeta.set(root, { type: typeOf, tooltip: () => current.tooltip !== false });
1809
+ const unbindEvents = bindChartEvents(root);
1739
1810
  render();
1740
1811
  return {
1741
1812
  el: root,
@@ -1753,28 +1824,18 @@ function renderChart(el2, raw) {
1753
1824
  },
1754
1825
  destroy() {
1755
1826
  root.textContent = "";
1756
- attached.__icenChartBound = false;
1827
+ unbindEvents();
1757
1828
  chartTooltip().hidden = true;
1758
1829
  }
1759
1830
  };
1760
1831
  }
1832
+ var GRAPH_W = 360;
1833
+ var GRAPH_H = 260;
1834
+ var GRAPH_PAD = 28;
1835
+ var GRAPH_LW = GRAPH_W - GRAPH_PAD * 2;
1836
+ var GRAPH_LH = GRAPH_H - GRAPH_PAD * 2;
1761
1837
 
1762
1838
  // src/behaviors/ai-tool.ts
1763
- var AI_STATUSES = [
1764
- "pending",
1765
- "running",
1766
- "streaming",
1767
- "approval",
1768
- "done",
1769
- "error",
1770
- "cancelled"
1771
- ];
1772
- function h(tag, className, text) {
1773
- const node = document.createElement(tag);
1774
- if (className) node.className = className;
1775
- if (text != null) node.textContent = text;
1776
- return node;
1777
- }
1778
1839
  function setStatusClass(item, status) {
1779
1840
  for (const s of AI_STATUSES) item.classList.toggle(`is-${s}`, s === status);
1780
1841
  }
@@ -1921,11 +1982,11 @@ function applyStatusMeta(parts, status, meta) {
1921
1982
  parts.metaEl.textContent = meta;
1922
1983
  parts.metaEl.hidden = !meta;
1923
1984
  }
1924
- function renderAiToolCall(el2, model) {
1985
+ function renderAiToolCall(el, model) {
1925
1986
  if (typeof document === "undefined") {
1926
- return { el: el2, update: () => void 0 };
1987
+ return { el, update: () => void 0 };
1927
1988
  }
1928
- const item = el2;
1989
+ const item = el;
1929
1990
  let current = model;
1930
1991
  item.className = `ai-tool ai-tool--${model.kind}`;
1931
1992
  item.dataset.aiId = model.id;
@@ -1942,12 +2003,9 @@ function renderAiToolCall(el2, model) {
1942
2003
  parts.head.setAttribute("aria-expanded", String(open));
1943
2004
  body.hidden = !open;
1944
2005
  if (fire) {
1945
- item.dispatchEvent(
1946
- new CustomEvent("icen:ai-toggle", { detail: { el: item, open }, bubbles: true })
1947
- );
2006
+ emitIcen(item, "icen:ai-toggle", { el: item, open });
1948
2007
  }
1949
2008
  };
1950
- let prevStatus = null;
1951
2009
  const apply = (m) => {
1952
2010
  setStatusClass(item, m.status);
1953
2011
  applyTint(item, m);
@@ -1960,7 +2018,6 @@ function renderAiToolCall(el2, model) {
1960
2018
  approval.reason.textContent = reason;
1961
2019
  approval.reason.hidden = !reason;
1962
2020
  if (!item.dataset.aiUserToggled && defaultExpanded(m)) setOpen(true);
1963
- prevStatus = m.status;
1964
2021
  };
1965
2022
  apply(current);
1966
2023
  return {
@@ -1971,11 +2028,11 @@ function renderAiToolCall(el2, model) {
1971
2028
  }
1972
2029
  };
1973
2030
  }
1974
- function renderAiSubagent(el2, model) {
2031
+ function renderAiSubagent(el, model) {
1975
2032
  if (typeof document === "undefined") {
1976
- return { el: el2, update: () => void 0 };
2033
+ return { el, update: () => void 0 };
1977
2034
  }
1978
- const item = el2;
2035
+ const item = el;
1979
2036
  let current = model;
1980
2037
  item.className = "ai-subagent";
1981
2038
  item.dataset.aiId = model.id;
@@ -1992,9 +2049,7 @@ function renderAiSubagent(el2, model) {
1992
2049
  parts.head.setAttribute("aria-expanded", String(open));
1993
2050
  body.hidden = !open;
1994
2051
  if (fire) {
1995
- item.dispatchEvent(
1996
- new CustomEvent("icen:ai-toggle", { detail: { el: item, open }, bubbles: true })
1997
- );
2052
+ emitIcen(item, "icen:ai-toggle", { el: item, open });
1998
2053
  }
1999
2054
  };
2000
2055
  const activityHandles = /* @__PURE__ */ new Map();
@@ -2021,7 +2076,6 @@ function renderAiSubagent(el2, model) {
2021
2076
  }
2022
2077
  }
2023
2078
  };
2024
- let prevStatus = null;
2025
2079
  const apply = (m) => {
2026
2080
  setStatusClass(item, m.status);
2027
2081
  applyTint(item, m);
@@ -2034,7 +2088,6 @@ function renderAiSubagent(el2, model) {
2034
2088
  result.textContent = receipt;
2035
2089
  result.hidden = !receipt;
2036
2090
  if (!item.dataset.aiUserToggled && defaultExpanded(m)) setOpen(true);
2037
- prevStatus = m.status;
2038
2091
  };
2039
2092
  apply(current);
2040
2093
  return {
@@ -2048,7 +2101,7 @@ function renderAiSubagent(el2, model) {
2048
2101
  function setupItem(item, kind) {
2049
2102
  const marked = item;
2050
2103
  const flag = kind === "tool" ? "__icenAiToolInit" : "__icenAiSubagentInit";
2051
- if (marked[flag]) return;
2104
+ if (marked[flag]) return () => void 0;
2052
2105
  marked[flag] = true;
2053
2106
  const headClass = kind === "tool" ? ".ai-tool-head" : ".ai-subagent-head";
2054
2107
  const bodyClass = kind === "tool" ? ".ai-tool-body" : ".ai-subagent-body";
@@ -2058,64 +2111,64 @@ function setupItem(item, kind) {
2058
2111
  head?.setAttribute("aria-expanded", String(open));
2059
2112
  if (body) body.hidden = !open;
2060
2113
  if (fire) {
2061
- item.dispatchEvent(
2062
- new CustomEvent("icen:ai-toggle", { detail: { el: item, open }, bubbles: true })
2063
- );
2114
+ emitIcen(item, "icen:ai-toggle", { el: item, open });
2064
2115
  }
2065
2116
  };
2066
- item.addEventListener("click", (ev) => {
2117
+ const onCardClick = (ev) => {
2067
2118
  const target = ev.target;
2068
2119
  if (!(target instanceof Element)) return;
2069
2120
  const ownerOf = (node) => node.closest(".ai-tool, .ai-subagent");
2070
2121
  const approveBtn = target.closest("[data-ai-approve]");
2071
2122
  if (approveBtn && ownerOf(approveBtn) === item) {
2072
- item.dispatchEvent(
2073
- new CustomEvent("icen:ai-approve", {
2074
- detail: { id: item.dataset.aiId ?? "", kind: item.dataset.aiKind ?? "" },
2075
- bubbles: true
2076
- })
2077
- );
2123
+ emitIcen(item, "icen:ai-approve", { id: item.dataset.aiId ?? "", kind: item.dataset.aiKind ?? "" });
2078
2124
  return;
2079
2125
  }
2080
2126
  const rejectBtn = target.closest("[data-ai-reject]");
2081
2127
  if (rejectBtn && ownerOf(rejectBtn) === item) {
2082
- item.dispatchEvent(
2083
- new CustomEvent("icen:ai-reject", {
2084
- detail: { id: item.dataset.aiId ?? "", kind: item.dataset.aiKind ?? "" },
2085
- bubbles: true
2086
- })
2087
- );
2128
+ emitIcen(item, "icen:ai-reject", { id: item.dataset.aiId ?? "", kind: item.dataset.aiKind ?? "" });
2088
2129
  return;
2089
2130
  }
2090
2131
  const headEl = target.closest(".ai-tool-head, .ai-subagent-head");
2091
2132
  if (!headEl || ownerOf(headEl) !== item || !head) return;
2092
2133
  item.dataset.aiUserToggled = "1";
2093
2134
  setOpen(head.getAttribute("aria-expanded") !== "true");
2094
- });
2135
+ };
2136
+ item.addEventListener("click", onCardClick);
2095
2137
  if (body?.hidden) {
2096
2138
  const kindName = item.dataset.aiKind ?? "";
2097
2139
  const isErr = item.classList.contains("is-error");
2098
2140
  const isApproval = item.classList.contains("is-approval");
2099
2141
  if (isErr || isApproval || kindName === "chart") {
2100
- item.dataset.aiUserToggled = "";
2101
2142
  setOpen(true, false);
2102
2143
  delete item.dataset.aiUserToggled;
2103
2144
  }
2104
2145
  }
2146
+ return () => {
2147
+ item.removeEventListener("click", onCardClick);
2148
+ marked[flag] = false;
2149
+ };
2105
2150
  }
2106
2151
  function initItems(root, selector, kind) {
2107
- if (typeof document === "undefined") return;
2152
+ if (typeof document === "undefined") return [];
2108
2153
  const scope = root ?? document;
2109
2154
  const items = [];
2110
2155
  if (scope instanceof Element && scope.matches(selector)) items.push(scope);
2111
2156
  items.push(...Array.from(scope.querySelectorAll(selector)));
2112
- for (const item of items) setupItem(item, kind);
2157
+ return items.map((item) => setupItem(item, kind));
2113
2158
  }
2114
2159
  function initAiTool(root) {
2115
- initItems(root, ".ai-tool", "tool");
2160
+ const cleanups = initItems(root, ".ai-tool", "tool");
2161
+ return () => {
2162
+ for (const fn of cleanups) fn();
2163
+ cleanups.length = 0;
2164
+ };
2116
2165
  }
2117
2166
  function initAiSubagent(root) {
2118
- initItems(root, ".ai-subagent", "subagent");
2167
+ const cleanups = initItems(root, ".ai-subagent", "subagent");
2168
+ return () => {
2169
+ for (const fn of cleanups) fn();
2170
+ cleanups.length = 0;
2171
+ };
2119
2172
  }
2120
2173
  export {
2121
2174
  initAiSubagent,