@icen.ai/ui 0.8.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/AGENTS.md +15 -9
  2. package/README.md +8 -3
  3. package/dist/base.css +4 -3
  4. package/dist/behaviors/accordion.d.mts +5 -2
  5. package/dist/behaviors/accordion.mjs +36 -11
  6. package/dist/behaviors/ai-chat.d.mts +3 -1
  7. package/dist/behaviors/ai-chat.mjs +66 -43
  8. package/dist/behaviors/ai-composer.d.mts +28 -3
  9. package/dist/behaviors/ai-composer.mjs +291 -209
  10. package/dist/behaviors/ai-core.d.mts +9 -4
  11. package/dist/behaviors/ai-core.mjs +21 -8
  12. package/dist/behaviors/ai-diff.d.mts +2 -2
  13. package/dist/behaviors/ai-diff.mjs +172 -41
  14. package/dist/behaviors/ai-panel.d.mts +11 -8
  15. package/dist/behaviors/ai-panel.mjs +185 -110
  16. package/dist/behaviors/ai-provider.mjs +54 -48
  17. package/dist/behaviors/ai-tool.d.mts +12 -4
  18. package/dist/behaviors/ai-tool.mjs +311 -263
  19. package/dist/behaviors/ai-tools.d.mts +9 -6
  20. package/dist/behaviors/ai-tools.mjs +259 -155
  21. package/dist/behaviors/back-top.mjs +16 -1
  22. package/dist/behaviors/carousel.d.mts +5 -2
  23. package/dist/behaviors/carousel.mjs +53 -26
  24. package/dist/behaviors/charts.d.mts +68 -22
  25. package/dist/behaviors/charts.mjs +221 -150
  26. package/dist/behaviors/command-palette.d.mts +40 -6
  27. package/dist/behaviors/command-palette.mjs +143 -15
  28. package/dist/behaviors/context-menu.d.mts +21 -4
  29. package/dist/behaviors/context-menu.mjs +122 -38
  30. package/dist/behaviors/controls.d.mts +59 -0
  31. package/dist/behaviors/controls.mjs +601 -0
  32. package/dist/behaviors/copy.d.mts +5 -2
  33. package/dist/behaviors/copy.mjs +37 -12
  34. package/dist/behaviors/datatable.d.mts +12 -0
  35. package/dist/behaviors/datatable.mjs +226 -43
  36. package/dist/behaviors/date-picker.d.mts +2 -2
  37. package/dist/behaviors/date-picker.mjs +171 -32
  38. package/dist/behaviors/dropdown.d.mts +6 -2
  39. package/dist/behaviors/dropdown.mjs +164 -25
  40. package/dist/behaviors/events.d.mts +330 -0
  41. package/dist/behaviors/events.mjs +208 -0
  42. package/dist/behaviors/input.mjs +25 -12
  43. package/dist/behaviors/modal.d.mts +24 -5
  44. package/dist/behaviors/modal.mjs +76 -10
  45. package/dist/behaviors/nav.d.mts +1 -1
  46. package/dist/behaviors/nav.mjs +33 -7
  47. package/dist/behaviors/notification.d.mts +20 -4
  48. package/dist/behaviors/notification.mjs +53 -73
  49. package/dist/behaviors/popover.d.mts +39 -3
  50. package/dist/behaviors/popover.mjs +90 -4
  51. package/dist/behaviors/select.d.mts +4 -2
  52. package/dist/behaviors/select.mjs +161 -24
  53. package/dist/behaviors/sidebar.d.mts +5 -2
  54. package/dist/behaviors/sidebar.mjs +43 -13
  55. package/dist/behaviors/slider.d.mts +5 -2
  56. package/dist/behaviors/slider.mjs +54 -43
  57. package/dist/behaviors/split-pane.d.mts +6 -2
  58. package/dist/behaviors/split-pane.mjs +62 -21
  59. package/dist/behaviors/tabs.d.mts +3 -2
  60. package/dist/behaviors/tabs.mjs +65 -14
  61. package/dist/behaviors/tag-input.d.mts +5 -2
  62. package/dist/behaviors/tag-input.mjs +61 -21
  63. package/dist/behaviors/theme.d.mts +13 -2
  64. package/dist/behaviors/theme.mjs +48 -4
  65. package/dist/behaviors/toast.d.mts +30 -8
  66. package/dist/behaviors/toast.mjs +74 -7
  67. package/dist/behaviors/tree.d.mts +5 -2
  68. package/dist/behaviors/tree.mjs +38 -15
  69. package/dist/behaviors/upload.mjs +21 -23
  70. package/dist/cli.mjs +1 -1
  71. package/dist/components/ai-panel.css +1 -1
  72. package/dist/components/ai-tool-call.mjs +1 -1
  73. package/dist/components/badge.css +1 -1
  74. package/dist/components/breadcrumb.css +0 -32
  75. package/dist/components/chart-calendar.mjs +1 -2
  76. package/dist/components/chart-pie.css +0 -6
  77. package/dist/components/charts.css +1 -1
  78. package/dist/components/content.css +3 -16
  79. package/dist/components/datatable.css +1 -1
  80. package/dist/components/date-picker.css +1 -1
  81. package/dist/components/notification.css +2 -3
  82. package/dist/components/pagination.css +3 -1
  83. package/dist/components/pagination.mjs +1 -0
  84. package/dist/components/rating.mjs +1 -0
  85. package/dist/components/segmented.css +9 -1
  86. package/dist/components/segmented.mjs +1 -1
  87. package/dist/components/select.css +67 -4
  88. package/dist/components/steps.css +13 -3
  89. package/dist/components/steps.mjs +1 -0
  90. package/dist/components/switch.css +3 -1
  91. package/dist/components/switch.mjs +1 -0
  92. package/dist/components/table.mjs +1 -0
  93. package/dist/components/tabs.css +23 -0
  94. package/dist/components/tag-input.css +1 -6
  95. package/dist/components/upload.css +0 -15
  96. package/dist/index.d.mts +12 -10
  97. package/dist/index.mjs +2700 -1460
  98. package/dist/registry.json +42 -13
  99. package/dist/retro-effects.css +3 -16
  100. package/dist/tokens/retro-effects.css +3 -16
  101. package/dist/tokens/style-profiles.css +3 -2
  102. package/dist/tokens/typography.css +2 -0
  103. package/dist/tokens.css +5 -2
  104. package/dist/ui.css +138 -98
  105. package/docs/spec/ai-native.md +7 -4
  106. package/package.json +1 -1
@@ -1,6 +1,20 @@
1
+ // src/behaviors/events.ts
2
+ function emitIcen(source, type, detail) {
3
+ return source.dispatchEvent(
4
+ new CustomEvent(type, { detail, bubbles: true, composed: true, cancelable: true })
5
+ );
6
+ }
7
+
1
8
  // src/behaviors/charts.ts
2
9
  var SVG_NS = "http://www.w3.org/2000/svg";
10
+ var REGISTERED_TONES = /* @__PURE__ */ new Map();
11
+ function registerChartTone(name, cssVar) {
12
+ if (!name || !cssVar) return;
13
+ REGISTERED_TONES.set(name, cssVar.startsWith("var(") ? cssVar : `var(${cssVar})`);
14
+ }
3
15
  function toneVar(tone = "accent") {
16
+ const registered = REGISTERED_TONES.get(tone);
17
+ if (registered) return registered;
4
18
  switch (tone) {
5
19
  case "success":
6
20
  return "var(--token-success)";
@@ -154,17 +168,14 @@ function withChrome(el, opts, hasLegend, paint, state) {
154
168
  if (state) state.hidden = hidden;
155
169
  el.setAttribute("data-legend-hidden", String(hidden));
156
170
  syncEye();
157
- el.dispatchEvent(new CustomEvent("icen:chart-legend-visibility", {
158
- bubbles: true,
159
- detail: { visible: hidden ? false : true }
160
- }));
171
+ emitIcen(el, "icen:chart-legend-visibility", { visible: hidden ? false : true });
161
172
  });
162
173
  syncEye();
163
174
  head.append(title, eye);
164
175
  el.append(head, body);
165
176
  paint(body);
166
177
  }
167
- function toggleLegend(items, scope, _opts) {
178
+ function toggleLegend(items, scope) {
168
179
  const legend = document.createElement("div");
169
180
  legend.className = "chart-legend chart-legend--toggle";
170
181
  for (const item of items) {
@@ -183,10 +194,7 @@ function toggleLegend(items, scope, _opts) {
183
194
  scope.querySelectorAll(`[data-chart-key="${CSS.escape(item.key)}"]`).forEach((m) => {
184
195
  m.classList.toggle("is-off", off);
185
196
  });
186
- legend.dispatchEvent(new CustomEvent("icen:chart-legend-toggle", {
187
- bubbles: true,
188
- detail: { key: item.key, seriesIndex: item.si, hidden: off }
189
- }));
197
+ emitIcen(legend, "icen:chart-legend-toggle", { key: item.key, seriesIndex: item.si, hidden: off });
190
198
  };
191
199
  row.addEventListener("click", toggle);
192
200
  row.addEventListener("keydown", (e) => {
@@ -205,7 +213,7 @@ function toggleLegend(items, scope, _opts) {
205
213
  return legend;
206
214
  }
207
215
  function renderVBar(el, opts) {
208
- if (typeof document === "undefined") return;
216
+ if (typeof document === "undefined") return el;
209
217
  const seriesList = normalizeSeriesInput(opts);
210
218
  const labels = opts.labels ?? [];
211
219
  const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
@@ -256,11 +264,13 @@ function renderVBar(el, opts) {
256
264
  wrap.appendChild(track);
257
265
  }
258
266
  body.append(wrap, labelsRow(labelsUsed));
259
- if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap, opts));
267
+ if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
260
268
  });
269
+ attachChartInteraction(el, "vbar");
270
+ return el;
261
271
  }
262
272
  function renderHBar(el, opts) {
263
- if (typeof document === "undefined") return;
273
+ if (typeof document === "undefined") return el;
264
274
  withChrome(el, opts, false, (body) => {
265
275
  const n = Math.min(opts.labels.length, opts.values.length);
266
276
  if (n === 0) {
@@ -294,9 +304,11 @@ function renderHBar(el, opts) {
294
304
  });
295
305
  body.appendChild(wrap);
296
306
  });
307
+ attachChartInteraction(el, "hbar");
308
+ return el;
297
309
  }
298
310
  function renderStack(el, opts) {
299
- if (typeof document === "undefined") return;
311
+ if (typeof document === "undefined") return el;
300
312
  withChrome(el, opts, opts.segments.length > 0, (body) => {
301
313
  const total = opts.segments.reduce((sum, s) => sum + s.value, 0);
302
314
  if (opts.segments.length === 0 || total <= 0) {
@@ -305,13 +317,13 @@ function renderStack(el, opts) {
305
317
  }
306
318
  const bar = document.createElement("div");
307
319
  bar.className = "chart-stack";
308
- for (const seg of opts.segments) {
320
+ for (const [segIndex, seg] of opts.segments.entries()) {
309
321
  const s = document.createElement("div");
310
322
  s.className = "chart-stack-seg";
311
323
  s.style.width = `${seg.value / total * 100}%`;
312
324
  if (seg.tone) s.style.setProperty("--chart-tone", toneVar(seg.tone));
313
325
  s.setAttribute("title", `${seg.label}: ${fmt(opts, seg.value)}`);
314
- setMarkData(s, { index: opts.segments.indexOf(seg), label: seg.label, value: seg.value });
326
+ setMarkData(s, { index: segIndex, label: seg.label, value: seg.value });
315
327
  bar.appendChild(s);
316
328
  }
317
329
  const legend = document.createElement("div");
@@ -321,9 +333,11 @@ function renderStack(el, opts) {
321
333
  }
322
334
  body.append(bar, legend);
323
335
  });
336
+ attachChartInteraction(el, "stack");
337
+ return el;
324
338
  }
325
339
  function renderDonut(el, opts) {
326
- if (typeof document === "undefined") return;
340
+ if (typeof document === "undefined") return el;
327
341
  withChrome(el, opts, opts.segments.length > 0, (body) => {
328
342
  const total = opts.segments.reduce((sum, s) => sum + s.value, 0);
329
343
  if (opts.segments.length === 0 || total <= 0) {
@@ -370,10 +384,12 @@ function renderDonut(el, opts) {
370
384
  wrap.append(svg, legend);
371
385
  body.appendChild(wrap);
372
386
  });
387
+ attachChartInteraction(el, "donut");
388
+ return el;
373
389
  }
374
390
  var lineGradientSeq = 0;
375
391
  function renderLine(el, opts) {
376
- if (typeof document === "undefined") return;
392
+ if (typeof document === "undefined") return el;
377
393
  const seriesList = normalizeSeriesInput(opts);
378
394
  const labels = opts.labels ?? [];
379
395
  const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
@@ -478,11 +494,13 @@ function renderLine(el, opts) {
478
494
  if (defs.firstChild) svg.insertBefore(defs, svg.firstChild);
479
495
  wrap.append(svg, labelsRow(sampleLabels(labelsUsed)));
480
496
  body.appendChild(wrap);
481
- if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap, opts));
497
+ if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
482
498
  });
499
+ attachChartInteraction(el, "line");
500
+ return el;
483
501
  }
484
502
  function renderArea(el, opts) {
485
- if (typeof document === "undefined") return;
503
+ if (typeof document === "undefined") return el;
486
504
  withChrome(el, opts, false, (body) => {
487
505
  const n = Math.min(opts.labels.length, opts.values.length);
488
506
  if (n === 0) {
@@ -551,9 +569,11 @@ function renderArea(el, opts) {
551
569
  wrap.append(svg, labelsRow(sampleLabels(labels)));
552
570
  body.appendChild(wrap);
553
571
  });
572
+ attachChartInteraction(el, "area");
573
+ return el;
554
574
  }
555
575
  function renderRadar(el, opts) {
556
- if (typeof document === "undefined") return;
576
+ if (typeof document === "undefined") return el;
557
577
  withChrome(el, opts, opts.series.length > 1, (body) => {
558
578
  const axes = opts.axes;
559
579
  const n = axes.length;
@@ -655,13 +675,12 @@ function renderRadar(el, opts) {
655
675
  }
656
676
  legendItems.push({ key: s.name || `\u7CFB\u5217${si + 1}`, label: s.name || `\u7CFB\u5217${si + 1}`, tone: s.tone, si });
657
677
  });
658
- wrap.append(svg, opts.series.length > 1 ? toggleLegend(legendItems, wrap, opts) : (() => {
659
- const l = document.createElement("div");
660
- l.className = "chart-legend";
661
- return l;
662
- })());
678
+ const legend = opts.series.length > 1 ? toggleLegend(legendItems, wrap) : null;
679
+ wrap.append(svg, ...legend ? [legend] : []);
663
680
  body.appendChild(wrap);
664
681
  });
682
+ attachChartInteraction(el, "radar");
683
+ return el;
665
684
  }
666
685
  function niceCeil(v) {
667
686
  if (v <= 0) return 1;
@@ -680,8 +699,41 @@ function isoDate(d) {
680
699
  const day = String(d.getDate()).padStart(2, "0");
681
700
  return `${d.getFullYear()}-${m}-${day}`;
682
701
  }
702
+ function weekdayLabelAt(defaults, day) {
703
+ return defaults[(day + 6) % 7] ?? "";
704
+ }
705
+ function levelOf(v, max) {
706
+ return v <= 0 ? 0 : Math.min(4, Math.ceil(v / max * 4));
707
+ }
708
+ function padFirstWeek(grid, firstDate, weekStart) {
709
+ const first = /* @__PURE__ */ new Date(`${firstDate}T00:00:00`);
710
+ const pad = (first.getDay() + 7 - weekStart) % 7;
711
+ for (let i = 0; i < pad; i++) {
712
+ const blank = document.createElement("span");
713
+ blank.className = "chart-heatmap-cell is-blank";
714
+ grid.appendChild(blank);
715
+ }
716
+ return pad;
717
+ }
718
+ function heatmapLegend(labels) {
719
+ const legend = document.createElement("div");
720
+ legend.className = "chart-heatmap-legend";
721
+ const less = document.createElement("span");
722
+ less.textContent = labels?.less ?? "\u5C11";
723
+ const more = document.createElement("span");
724
+ more.textContent = labels?.more ?? "\u591A";
725
+ legend.appendChild(less);
726
+ for (let lv = 0; lv <= 4; lv++) {
727
+ const cell = document.createElement("span");
728
+ cell.className = "chart-heatmap-cell";
729
+ cell.dataset.level = String(lv);
730
+ legend.appendChild(cell);
731
+ }
732
+ legend.appendChild(more);
733
+ return legend;
734
+ }
683
735
  function renderHeatmap(el, opts) {
684
- if (typeof document === "undefined") return;
736
+ if (typeof document === "undefined") return el;
685
737
  withChrome(el, opts, true, (body) => {
686
738
  let entries;
687
739
  if (opts.data && opts.data.length > 0) {
@@ -700,48 +752,29 @@ function renderHeatmap(el, opts) {
700
752
  const weeks = opts.weeks ?? Math.ceil(entries.length / 7);
701
753
  if (entries.length > weeks * 7) entries = entries.slice(entries.length - weeks * 7);
702
754
  const max = maxOf(entries.map((e) => e.value));
703
- const levelOf = (v) => v <= 0 ? 0 : Math.min(4, Math.ceil(v / max * 4));
704
755
  const wrap = document.createElement("div");
705
756
  wrap.className = "chart-heatmap";
706
757
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
707
758
  const grid = document.createElement("div");
708
759
  grid.className = "chart-heatmap-grid";
709
760
  grid.setAttribute("role", "img");
710
- const first = /* @__PURE__ */ new Date(`${entries[0].date}T00:00:00`);
711
- const pad = (first.getDay() + 6) % 7;
712
- for (let i = 0; i < pad; i++) {
713
- const blank = document.createElement("span");
714
- blank.className = "chart-heatmap-cell is-blank";
715
- grid.appendChild(blank);
716
- }
717
- for (const entry of entries) {
761
+ padFirstWeek(grid, entries[0].date, opts.weekStart === 0 ? 0 : 1);
762
+ for (const [i, entry] of entries.entries()) {
718
763
  const cell = document.createElement("span");
719
764
  cell.className = "chart-heatmap-cell";
720
- cell.dataset.level = String(levelOf(entry.value));
765
+ cell.dataset.level = String(levelOf(entry.value, max));
721
766
  cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
722
- setMarkData(cell, { index: entries.indexOf(entry), label: entry.date, value: entry.value });
767
+ setMarkData(cell, { index: i, label: entry.date, value: entry.value });
723
768
  grid.appendChild(cell);
724
769
  }
725
- const legend = document.createElement("div");
726
- legend.className = "chart-heatmap-legend";
727
- const less = document.createElement("span");
728
- less.textContent = "\u5C11";
729
- const more = document.createElement("span");
730
- more.textContent = "\u591A";
731
- legend.appendChild(less);
732
- for (let lv = 0; lv <= 4; lv++) {
733
- const cell = document.createElement("span");
734
- cell.className = "chart-heatmap-cell";
735
- cell.dataset.level = String(lv);
736
- legend.appendChild(cell);
737
- }
738
- legend.appendChild(more);
739
- wrap.append(grid, legend);
770
+ wrap.append(grid, heatmapLegend(opts.labels));
740
771
  body.appendChild(wrap);
741
772
  });
773
+ attachChartInteraction(el, "heatmap");
774
+ return el;
742
775
  }
743
776
  function renderSparkline(el, opts) {
744
- if (typeof document === "undefined") return;
777
+ if (typeof document === "undefined") return el;
745
778
  withChrome(el, opts, false, (body) => {
746
779
  const values = opts.values;
747
780
  if (values.length === 0) {
@@ -796,9 +829,11 @@ function renderSparkline(el, opts) {
796
829
  wrap.appendChild(svg);
797
830
  body.appendChild(wrap);
798
831
  });
832
+ attachChartInteraction(el, "sparkline");
833
+ return el;
799
834
  }
800
835
  function renderGauge(el, opts) {
801
- if (typeof document === "undefined") return;
836
+ if (typeof document === "undefined") return el;
802
837
  el.textContent = "";
803
838
  const max = opts.max ?? 100;
804
839
  const ratio = max > 0 ? Math.min(1, Math.max(0, opts.value / max)) : 0;
@@ -842,9 +877,11 @@ function renderGauge(el, opts) {
842
877
  }
843
878
  wrap.append(svg, center);
844
879
  el.appendChild(wrap);
880
+ attachChartInteraction(el, "gauge");
881
+ return el;
845
882
  }
846
883
  function renderCalendar(el, opts) {
847
- if (typeof document === "undefined") return;
884
+ if (typeof document === "undefined") return el;
848
885
  withChrome(el, opts, true, (body) => {
849
886
  let entries;
850
887
  if (opts.data && opts.data.length > 0) {
@@ -858,6 +895,10 @@ function renderCalendar(el, opts) {
858
895
  const wrap = document.createElement("div");
859
896
  wrap.className = "chart-calendar";
860
897
  if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
898
+ const weekStart = opts.weekStart === 0 ? 0 : 1;
899
+ const weekdayDefaults = ["\u4E00", "", "\u4E09", "", "\u4E94", "", "\u65E5"];
900
+ const weekdayLabels = opts.labels?.weekdays ?? weekdayDefaults.map((_, i) => weekdayLabelAt(weekdayDefaults, (weekStart + i) % 7));
901
+ const monthLabel = (m) => opts.labels?.months?.[m] ?? `${m + 1}\u6708`;
861
902
  const grid = document.createElement("div");
862
903
  grid.className = "chart-heatmap-grid";
863
904
  grid.setAttribute("role", "img");
@@ -865,20 +906,13 @@ function renderCalendar(el, opts) {
865
906
  months.className = "chart-calendar-months";
866
907
  const weekdayRow = document.createElement("div");
867
908
  weekdayRow.className = "chart-calendar-weekdays";
868
- ["\u4E00", "", "\u4E09", "", "\u4E94", "", "\u65E5"].forEach((w) => {
909
+ weekdayLabels.forEach((w) => {
869
910
  const s = document.createElement("span");
870
911
  s.textContent = w;
871
912
  weekdayRow.appendChild(s);
872
913
  });
873
914
  const max = maxOf(entries.map((e) => e.value));
874
- const levelOf = (v) => v <= 0 ? 0 : Math.min(4, Math.ceil(v / max * 4));
875
- const first = /* @__PURE__ */ new Date(`${entries[0].date}T00:00:00`);
876
- const pad = (first.getDay() + 6) % 7;
877
- for (let i = 0; i < pad; i++) {
878
- const blank = document.createElement("span");
879
- blank.className = "chart-heatmap-cell is-blank";
880
- grid.appendChild(blank);
881
- }
915
+ const pad = padFirstWeek(grid, entries[0].date, weekStart);
882
916
  let lastMonth = -1;
883
917
  entries.forEach((entry, i) => {
884
918
  const d = /* @__PURE__ */ new Date(`${entry.date}T00:00:00`);
@@ -887,39 +921,28 @@ function renderCalendar(el, opts) {
887
921
  lastMonth = d.getMonth();
888
922
  const label = document.createElement("span");
889
923
  label.className = "chart-calendar-month";
890
- label.textContent = `${d.getMonth() + 1}\u6708`;
924
+ label.textContent = monthLabel(d.getMonth());
891
925
  label.style.setProperty("--col", String(col));
892
926
  months.appendChild(label);
893
927
  }
894
928
  const cell = document.createElement("span");
895
929
  cell.className = "chart-heatmap-cell";
896
- cell.dataset.level = String(levelOf(entry.value));
930
+ cell.dataset.level = String(levelOf(entry.value, max));
897
931
  cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
898
932
  setMarkData(cell, { index: i, label: entry.date, value: entry.value });
899
933
  grid.appendChild(cell);
900
934
  });
901
- const legend = document.createElement("div");
902
- legend.className = "chart-heatmap-legend";
903
- const less = document.createElement("span");
904
- less.textContent = "\u5C11";
905
- const more = document.createElement("span");
906
- more.textContent = "\u591A";
907
- legend.appendChild(less);
908
- for (let lv = 0; lv <= 4; lv++) {
909
- const cell = document.createElement("span");
910
- cell.className = "chart-heatmap-cell";
911
- cell.dataset.level = String(lv);
912
- legend.appendChild(cell);
913
- }
914
- legend.appendChild(more);
935
+ const legend = heatmapLegend(opts.labels);
915
936
  const columns = Math.max(1, Math.ceil((pad + entries.length) / 7));
916
937
  months.style.setProperty("--columns", String(columns));
917
938
  wrap.append(months, weekdayRow, grid, legend);
918
939
  body.appendChild(wrap);
919
940
  });
941
+ attachChartInteraction(el, "calendar");
942
+ return el;
920
943
  }
921
944
  function renderScatter(el, opts) {
922
- if (typeof document === "undefined") return;
945
+ if (typeof document === "undefined") return el;
923
946
  withChrome(el, opts, false, (body) => {
924
947
  const pts = opts.points.filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y));
925
948
  if (pts.length === 0) {
@@ -964,7 +987,7 @@ function renderScatter(el, opts) {
964
987
  "font-size": "8",
965
988
  fill: "var(--token-text-faint)"
966
989
  });
967
- txt.textContent = String(Math.round(tv * 10) / 10);
990
+ txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
968
991
  svg.appendChild(txt);
969
992
  }
970
993
  for (let i = 0; i <= 4; i++) {
@@ -986,7 +1009,7 @@ function renderScatter(el, opts) {
986
1009
  "font-size": "8",
987
1010
  fill: "var(--token-text-faint)"
988
1011
  });
989
- txt.textContent = String(Math.round(tv * 10) / 10);
1012
+ txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
990
1013
  svg.appendChild(txt);
991
1014
  }
992
1015
  if (opts.yLabel) {
@@ -1010,7 +1033,7 @@ function renderScatter(el, opts) {
1010
1033
  "stroke-width": "1"
1011
1034
  });
1012
1035
  const title = svgEl("title", {});
1013
- title.textContent = `${p.label ?? `#${i + 1}`}: (${p.x}, ${p.y}${p.size != null ? `, size ${p.size}` : ""})`;
1036
+ title.textContent = `${p.label ?? `#${i + 1}`}: (${fmt(opts, p.x)}, ${fmt(opts, p.y)}${p.size != null ? `, size ${fmt(opts, p.size)}` : ""})`;
1014
1037
  c.appendChild(title);
1015
1038
  setMarkData(c, { index: i, label: p.label ?? `#${i + 1}`, value: p.y });
1016
1039
  c.dataset.chartX = String(p.x);
@@ -1019,6 +1042,8 @@ function renderScatter(el, opts) {
1019
1042
  wrap.appendChild(svg);
1020
1043
  body.appendChild(wrap);
1021
1044
  });
1045
+ attachChartInteraction(el, "scatter");
1046
+ return el;
1022
1047
  }
1023
1048
  function chartFormatValue(format) {
1024
1049
  if (!format) return (v) => String(v);
@@ -1149,7 +1174,7 @@ function normalizeChartSpec(raw) {
1149
1174
  }
1150
1175
  }
1151
1176
  if (!src || typeof src !== "object" || Array.isArray(src)) {
1152
- if (Array.isArray(raw)) src = { data: raw };
1177
+ if (Array.isArray(src)) src = { data: src };
1153
1178
  else return {};
1154
1179
  }
1155
1180
  const s = src;
@@ -1219,6 +1244,7 @@ function normalizeChartSpec(raw) {
1219
1244
  }
1220
1245
  return spec;
1221
1246
  }
1247
+ var TEMPORAL_HIT_RATIO = 0.6;
1222
1248
  function looksTemporal(labels) {
1223
1249
  if (labels.length === 0) return false;
1224
1250
  let hits = 0;
@@ -1232,7 +1258,7 @@ function looksTemporal(labels) {
1232
1258
  else if (/^(v|ver\.?|version[ .]?)?\d+(\.\d+)+$/i.test(l)) hits++;
1233
1259
  else if (/^\d{4}$/.test(l)) hits++;
1234
1260
  }
1235
- return hits / labels.length >= 0.6;
1261
+ return hits / labels.length >= TEMPORAL_HIT_RATIO;
1236
1262
  }
1237
1263
  function inferChartType(spec) {
1238
1264
  if (spec.points && spec.points.length > 0) return "scatter";
@@ -1267,7 +1293,7 @@ function dispatchChartEvent(root, type, mark, chart, phase, pointer) {
1267
1293
  pointerX: pointer?.x,
1268
1294
  pointerY: pointer?.y
1269
1295
  };
1270
- root.dispatchEvent(new CustomEvent(`icen:chart-${type}`, { detail, bubbles: true }));
1296
+ emitIcen(root, `icen:chart-${type}`, detail);
1271
1297
  }
1272
1298
  var tooltipEl = null;
1273
1299
  function chartTooltip() {
@@ -1280,6 +1306,12 @@ function chartTooltip() {
1280
1306
  }
1281
1307
  return tooltipEl;
1282
1308
  }
1309
+ function applyTooltipSizing(host) {
1310
+ const tip = chartTooltip();
1311
+ const raw = host?.closest(".chart, [data-chart-root]")?.getAttribute("data-panel-max") ?? null;
1312
+ const n = raw != null ? Number(raw) : NaN;
1313
+ tip.style.maxWidth = Number.isFinite(n) && n > 0 ? `${n}px` : "";
1314
+ }
1283
1315
  function tooltipText(mark) {
1284
1316
  const label = mark.getAttribute("data-chart-label") ?? "";
1285
1317
  const series = mark.getAttribute("data-chart-series-name");
@@ -1299,6 +1331,101 @@ function moveTooltip(x, y) {
1299
1331
  tip.style.left = `${Math.round(left)}px`;
1300
1332
  tip.style.top = `${Math.round(top)}px`;
1301
1333
  }
1334
+ var chartDelegateMeta = /* @__PURE__ */ new WeakMap();
1335
+ var chartDelegateUnbind = /* @__PURE__ */ new WeakMap();
1336
+ function bindChartEvents(root) {
1337
+ const noop = () => void 0;
1338
+ if (typeof document === "undefined") return noop;
1339
+ const attached = root;
1340
+ if (attached.__icenChartBound) {
1341
+ const prev = chartDelegateUnbind.get(root);
1342
+ if (prev) return prev;
1343
+ }
1344
+ for (let p = root.parentElement; p; p = p.parentElement) {
1345
+ if (p.__icenChartBound) return noop;
1346
+ }
1347
+ const typeNow = () => chartDelegateMeta.get(root)?.type() ?? "vbar";
1348
+ const tipEnabled = () => chartDelegateMeta.get(root)?.tooltip() ?? true;
1349
+ const markOf = (e) => {
1350
+ const t = e.target;
1351
+ return t instanceof Element ? t.closest("[data-chart-mark]") : null;
1352
+ };
1353
+ let hoverMark = null;
1354
+ const enterMark = (mark, x, y) => {
1355
+ hoverMark = mark;
1356
+ mark.classList.add("is-hover");
1357
+ dispatchChartEvent(root, "hover", mark, typeNow(), "enter", { x, y });
1358
+ if (tipEnabled()) {
1359
+ const tip = chartTooltip();
1360
+ tip.textContent = tooltipText(mark);
1361
+ applyTooltipSizing(mark);
1362
+ tip.hidden = false;
1363
+ moveTooltip(x, y);
1364
+ }
1365
+ };
1366
+ const leaveMark = (pointer) => {
1367
+ if (!hoverMark) return;
1368
+ hoverMark.classList.remove("is-hover");
1369
+ dispatchChartEvent(root, "hover", hoverMark, typeNow(), "leave", pointer);
1370
+ hoverMark = null;
1371
+ chartTooltip().hidden = true;
1372
+ };
1373
+ const bindings = [];
1374
+ const on = (type, fn) => {
1375
+ const listener = (evt) => fn(evt);
1376
+ root.addEventListener(type, listener);
1377
+ bindings.push([type, listener]);
1378
+ };
1379
+ on("pointerover", (e) => {
1380
+ const mark = markOf(e);
1381
+ if (!mark || mark === hoverMark) return;
1382
+ leaveMark({ x: e.clientX, y: e.clientY });
1383
+ enterMark(mark, e.clientX, e.clientY);
1384
+ });
1385
+ on("pointermove", (e) => {
1386
+ if (hoverMark && tipEnabled()) {
1387
+ dispatchChartEvent(root, "hover", hoverMark, typeNow(), "move", { x: e.clientX, y: e.clientY });
1388
+ moveTooltip(e.clientX, e.clientY);
1389
+ }
1390
+ });
1391
+ on("pointerout", (e) => {
1392
+ const mark = markOf(e);
1393
+ if (mark && mark === hoverMark) leaveMark({ x: e.clientX, y: e.clientY });
1394
+ });
1395
+ on("pointerleave", () => leaveMark());
1396
+ on("click", (e) => {
1397
+ const mark = markOf(e);
1398
+ if (mark) dispatchChartEvent(root, "click", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
1399
+ });
1400
+ on("dblclick", (e) => {
1401
+ const mark = markOf(e);
1402
+ if (mark) dispatchChartEvent(root, "dblclick", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
1403
+ });
1404
+ on("contextmenu", (e) => {
1405
+ const mark = markOf(e);
1406
+ if (!mark) return;
1407
+ e.preventDefault();
1408
+ dispatchChartEvent(root, "contextmenu", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
1409
+ });
1410
+ const unbind = () => {
1411
+ for (const [type, listener] of bindings) root.removeEventListener(type, listener);
1412
+ bindings.length = 0;
1413
+ attached.__icenChartBound = false;
1414
+ chartDelegateUnbind.delete(root);
1415
+ if (hoverMark) {
1416
+ hoverMark.classList.remove("is-hover");
1417
+ hoverMark = null;
1418
+ }
1419
+ chartTooltip().hidden = true;
1420
+ };
1421
+ attached.__icenChartBound = true;
1422
+ chartDelegateUnbind.set(root, unbind);
1423
+ return unbind;
1424
+ }
1425
+ function attachChartInteraction(el, type) {
1426
+ chartDelegateMeta.set(el, { type: () => type, tooltip: () => true });
1427
+ bindChartEvents(el);
1428
+ }
1302
1429
  function hasLegendOf(spec) {
1303
1430
  switch (spec.type) {
1304
1431
  case "donut":
@@ -1321,9 +1448,6 @@ function renderChart(el, raw) {
1321
1448
  return { el, spec: {}, update: noop, on: () => noop, destroy: noop };
1322
1449
  }
1323
1450
  const root = el;
1324
- const attached = root;
1325
- const wasBound = attached.__icenChartBound === true;
1326
- attached.__icenChartBound = true;
1327
1451
  let current = normalizeChartSpec(raw);
1328
1452
  const typeOf = () => current.type ?? "vbar";
1329
1453
  const legendState = { hidden: null };
@@ -1358,10 +1482,10 @@ function renderChart(el, raw) {
1358
1482
  break;
1359
1483
  }
1360
1484
  case "heatmap":
1361
- renderHeatmap(body, { values: spec.values, data: spec.data, weeks: spec.weeks, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1485
+ 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 });
1362
1486
  break;
1363
1487
  case "calendar":
1364
- renderCalendar(body, { dates: spec.dates, values: spec.values, data: spec.data, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
1488
+ 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 });
1365
1489
  break;
1366
1490
  case "sparkline":
1367
1491
  renderSparkline(body, { labels: spec.labels ?? [], values: spec.values ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
@@ -1377,63 +1501,8 @@ function renderChart(el, raw) {
1377
1501
  }
1378
1502
  });
1379
1503
  };
1380
- if (!wasBound) {
1381
- const markOf = (e) => {
1382
- const t = e.target;
1383
- return t instanceof Element ? t.closest("[data-chart-mark]") : null;
1384
- };
1385
- const tipEnabled = () => current.tooltip !== false;
1386
- let hoverMark = null;
1387
- const enterMark = (mark, x, y) => {
1388
- hoverMark = mark;
1389
- mark.classList.add("is-hover");
1390
- dispatchChartEvent(root, "hover", mark, typeOf(), "enter", { x, y });
1391
- if (tipEnabled()) {
1392
- const tip = chartTooltip();
1393
- tip.textContent = tooltipText(mark);
1394
- tip.hidden = false;
1395
- moveTooltip(x, y);
1396
- }
1397
- };
1398
- const leaveMark = (pointer) => {
1399
- if (!hoverMark) return;
1400
- hoverMark.classList.remove("is-hover");
1401
- dispatchChartEvent(root, "hover", hoverMark, typeOf(), "leave", pointer);
1402
- hoverMark = null;
1403
- chartTooltip().hidden = true;
1404
- };
1405
- root.addEventListener("pointerover", (e) => {
1406
- const mark = markOf(e);
1407
- if (!mark || mark === hoverMark) return;
1408
- leaveMark({ x: e.clientX, y: e.clientY });
1409
- enterMark(mark, e.clientX, e.clientY);
1410
- });
1411
- root.addEventListener("pointermove", (e) => {
1412
- if (hoverMark && tipEnabled()) {
1413
- dispatchChartEvent(root, "hover", hoverMark, typeOf(), "move", { x: e.clientX, y: e.clientY });
1414
- moveTooltip(e.clientX, e.clientY);
1415
- }
1416
- });
1417
- root.addEventListener("pointerout", (e) => {
1418
- const mark = markOf(e);
1419
- if (mark && mark === hoverMark) leaveMark({ x: e.clientX, y: e.clientY });
1420
- });
1421
- root.addEventListener("pointerleave", () => leaveMark());
1422
- root.addEventListener("click", (e) => {
1423
- const mark = markOf(e);
1424
- if (mark) dispatchChartEvent(root, "click", mark, typeOf(), void 0, { x: e.clientX, y: e.clientY });
1425
- });
1426
- root.addEventListener("dblclick", (e) => {
1427
- const mark = markOf(e);
1428
- if (mark) dispatchChartEvent(root, "dblclick", mark, typeOf(), void 0, { x: e.clientX, y: e.clientY });
1429
- });
1430
- root.addEventListener("contextmenu", (e) => {
1431
- const mark = markOf(e);
1432
- if (!mark) return;
1433
- e.preventDefault();
1434
- dispatchChartEvent(root, "contextmenu", mark, typeOf(), void 0, { x: e.clientX, y: e.clientY });
1435
- });
1436
- }
1504
+ chartDelegateMeta.set(root, { type: typeOf, tooltip: () => current.tooltip !== false });
1505
+ const unbindEvents = bindChartEvents(root);
1437
1506
  render();
1438
1507
  return {
1439
1508
  el: root,
@@ -1451,15 +1520,17 @@ function renderChart(el, raw) {
1451
1520
  },
1452
1521
  destroy() {
1453
1522
  root.textContent = "";
1454
- attached.__icenChartBound = false;
1523
+ unbindEvents();
1455
1524
  chartTooltip().hidden = true;
1456
1525
  }
1457
1526
  };
1458
1527
  }
1459
1528
  export {
1529
+ bindChartEvents,
1460
1530
  chartFormatValue,
1461
1531
  inferChartType,
1462
1532
  normalizeChartSpec,
1533
+ registerChartTone,
1463
1534
  renderArea,
1464
1535
  renderCalendar,
1465
1536
  renderChart,