@icen.ai/ui 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +15 -9
- package/README.md +8 -3
- package/dist/base.css +4 -3
- package/dist/behaviors/accordion.d.mts +5 -2
- package/dist/behaviors/accordion.mjs +36 -11
- package/dist/behaviors/ai-chat.d.mts +3 -1
- package/dist/behaviors/ai-chat.mjs +66 -43
- package/dist/behaviors/ai-composer.d.mts +28 -3
- package/dist/behaviors/ai-composer.mjs +291 -209
- package/dist/behaviors/ai-core.d.mts +9 -4
- package/dist/behaviors/ai-core.mjs +21 -8
- package/dist/behaviors/ai-diff.d.mts +2 -2
- package/dist/behaviors/ai-diff.mjs +172 -41
- package/dist/behaviors/ai-panel.d.mts +11 -8
- package/dist/behaviors/ai-panel.mjs +185 -110
- package/dist/behaviors/ai-provider.mjs +54 -48
- package/dist/behaviors/ai-tool.d.mts +12 -4
- package/dist/behaviors/ai-tool.mjs +311 -263
- package/dist/behaviors/ai-tools.d.mts +9 -6
- package/dist/behaviors/ai-tools.mjs +259 -155
- package/dist/behaviors/back-top.mjs +16 -1
- package/dist/behaviors/carousel.d.mts +5 -2
- package/dist/behaviors/carousel.mjs +53 -26
- package/dist/behaviors/charts.d.mts +68 -22
- package/dist/behaviors/charts.mjs +221 -150
- package/dist/behaviors/command-palette.d.mts +40 -6
- package/dist/behaviors/command-palette.mjs +143 -15
- package/dist/behaviors/context-menu.d.mts +21 -4
- package/dist/behaviors/context-menu.mjs +122 -38
- package/dist/behaviors/controls.d.mts +59 -0
- package/dist/behaviors/controls.mjs +601 -0
- package/dist/behaviors/copy.d.mts +5 -2
- package/dist/behaviors/copy.mjs +37 -12
- package/dist/behaviors/datatable.d.mts +12 -0
- package/dist/behaviors/datatable.mjs +226 -43
- package/dist/behaviors/date-picker.d.mts +2 -2
- package/dist/behaviors/date-picker.mjs +171 -32
- package/dist/behaviors/dropdown.d.mts +6 -2
- package/dist/behaviors/dropdown.mjs +164 -25
- package/dist/behaviors/events.d.mts +330 -0
- package/dist/behaviors/events.mjs +208 -0
- package/dist/behaviors/input.mjs +25 -12
- package/dist/behaviors/modal.d.mts +24 -5
- package/dist/behaviors/modal.mjs +76 -10
- package/dist/behaviors/nav.d.mts +1 -1
- package/dist/behaviors/nav.mjs +33 -7
- package/dist/behaviors/notification.d.mts +20 -4
- package/dist/behaviors/notification.mjs +53 -73
- package/dist/behaviors/popover.d.mts +39 -3
- package/dist/behaviors/popover.mjs +90 -4
- package/dist/behaviors/select.d.mts +4 -2
- package/dist/behaviors/select.mjs +161 -24
- package/dist/behaviors/sidebar.d.mts +5 -2
- package/dist/behaviors/sidebar.mjs +43 -13
- package/dist/behaviors/slider.d.mts +5 -2
- package/dist/behaviors/slider.mjs +54 -43
- package/dist/behaviors/split-pane.d.mts +6 -2
- package/dist/behaviors/split-pane.mjs +62 -21
- package/dist/behaviors/tabs.d.mts +3 -2
- package/dist/behaviors/tabs.mjs +65 -14
- package/dist/behaviors/tag-input.d.mts +5 -2
- package/dist/behaviors/tag-input.mjs +61 -21
- package/dist/behaviors/theme.d.mts +13 -2
- package/dist/behaviors/theme.mjs +48 -4
- package/dist/behaviors/toast.d.mts +30 -8
- package/dist/behaviors/toast.mjs +74 -7
- package/dist/behaviors/tree.d.mts +5 -2
- package/dist/behaviors/tree.mjs +38 -15
- package/dist/behaviors/upload.mjs +21 -23
- package/dist/cli.mjs +1 -1
- package/dist/components/ai-panel.css +1 -1
- package/dist/components/ai-tool-call.mjs +1 -1
- package/dist/components/badge.css +1 -1
- package/dist/components/breadcrumb.css +0 -32
- package/dist/components/chart-calendar.mjs +1 -2
- package/dist/components/chart-pie.css +0 -6
- package/dist/components/charts.css +1 -1
- package/dist/components/content.css +3 -16
- package/dist/components/datatable.css +1 -1
- package/dist/components/date-picker.css +1 -1
- package/dist/components/notification.css +2 -3
- package/dist/components/pagination.css +3 -1
- package/dist/components/pagination.mjs +1 -0
- package/dist/components/rating.mjs +1 -0
- package/dist/components/segmented.css +9 -1
- package/dist/components/segmented.mjs +1 -1
- package/dist/components/select.css +67 -4
- package/dist/components/steps.css +13 -3
- package/dist/components/steps.mjs +1 -0
- package/dist/components/switch.css +3 -1
- package/dist/components/switch.mjs +1 -0
- package/dist/components/table.mjs +1 -0
- package/dist/components/tabs.css +23 -0
- package/dist/components/tag-input.css +1 -6
- package/dist/components/upload.css +0 -15
- package/dist/index.d.mts +12 -10
- package/dist/index.mjs +2700 -1460
- package/dist/registry.json +42 -13
- package/dist/retro-effects.css +3 -16
- package/dist/tokens/retro-effects.css +3 -16
- package/dist/tokens/style-profiles.css +3 -2
- package/dist/tokens/typography.css +2 -0
- package/dist/tokens.css +5 -2
- package/dist/ui.css +138 -98
- package/docs/spec/ai-native.md +7 -4
- package/package.json +1 -1
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
// src/behaviors/ai-core.ts
|
|
2
|
+
var AI_STATUSES = [
|
|
3
|
+
"pending",
|
|
4
|
+
"running",
|
|
5
|
+
"streaming",
|
|
6
|
+
"approval",
|
|
7
|
+
"done",
|
|
8
|
+
"error",
|
|
9
|
+
"cancelled"
|
|
10
|
+
];
|
|
2
11
|
function aiStatusLabel(s) {
|
|
3
12
|
const labels = {
|
|
4
13
|
pending: "\u7B49\u5F85",
|
|
@@ -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()) ??
|
|
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 = (
|
|
283
|
-
for (const attr of Array.from(
|
|
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"))
|
|
299
|
+
if (name.startsWith("on")) el.removeAttribute(attr.name);
|
|
286
300
|
if ((name === "href" || name === "xlink:href") && attr.value.trim().toLowerCase().startsWith("javascript:")) {
|
|
287
|
-
|
|
301
|
+
el.removeAttribute(attr.name);
|
|
288
302
|
}
|
|
289
303
|
}
|
|
290
|
-
for (const child of Array.from(
|
|
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 =
|
|
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 =
|
|
353
|
-
chip.appendChild(
|
|
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
|
|
409
|
-
for (const [k, v] of Object.entries(attrs))
|
|
410
|
-
return
|
|
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(
|
|
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
|
-
|
|
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
|
|
483
|
-
return
|
|
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(
|
|
489
|
-
if (
|
|
490
|
-
|
|
491
|
-
paint(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
543
|
+
const nowHidden = el.getAttribute("data-legend-hidden") === "true";
|
|
526
544
|
hidden = !nowHidden;
|
|
527
545
|
if (state) state.hidden = hidden;
|
|
528
|
-
|
|
546
|
+
el.setAttribute("data-legend-hidden", String(hidden));
|
|
529
547
|
syncEye();
|
|
530
|
-
|
|
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
|
-
|
|
552
|
+
el.append(head, body);
|
|
538
553
|
paint(body);
|
|
539
554
|
}
|
|
540
|
-
function toggleLegend(items, scope
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
636
|
-
if (typeof document === "undefined") return;
|
|
637
|
-
withChrome(
|
|
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(
|
|
672
|
-
if (typeof document === "undefined") return;
|
|
673
|
-
withChrome(
|
|
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:
|
|
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(
|
|
699
|
-
if (typeof document === "undefined") return;
|
|
700
|
-
withChrome(
|
|
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(
|
|
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(
|
|
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
|
|
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(
|
|
858
|
-
if (typeof document === "undefined") return;
|
|
859
|
-
withChrome(
|
|
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
|
-
|
|
961
|
-
|
|
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
|
|
986
|
-
|
|
987
|
-
|
|
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
|
-
|
|
1013
|
-
const
|
|
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:
|
|
1071
|
+
setMarkData(cell, { index: i, label: entry.date, value: entry.value });
|
|
1025
1072
|
grid.appendChild(cell);
|
|
1026
1073
|
}
|
|
1027
|
-
|
|
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(
|
|
1046
|
-
if (typeof document === "undefined") return;
|
|
1047
|
-
withChrome(
|
|
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(
|
|
1103
|
-
if (typeof document === "undefined") return;
|
|
1104
|
-
|
|
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
|
-
|
|
1183
|
+
el.appendChild(wrap);
|
|
1184
|
+
attachChartInteraction(el, "gauge");
|
|
1185
|
+
return el;
|
|
1147
1186
|
}
|
|
1148
|
-
function renderCalendar(
|
|
1149
|
-
if (typeof document === "undefined") return;
|
|
1150
|
-
withChrome(
|
|
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
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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(
|
|
1224
|
-
if (typeof document === "undefined") return;
|
|
1225
|
-
withChrome(
|
|
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 =
|
|
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 =
|
|
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(
|
|
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 >=
|
|
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
|
|
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(
|
|
1749
|
+
function renderChart(el, raw) {
|
|
1621
1750
|
const noop = () => void 0;
|
|
1622
1751
|
if (typeof document === "undefined") {
|
|
1623
|
-
return { el
|
|
1752
|
+
return { el, spec: {}, update: noop, on: () => noop, destroy: noop };
|
|
1624
1753
|
}
|
|
1625
|
-
const root =
|
|
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
|
-
|
|
1683
|
-
|
|
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,13 @@ function renderChart(el2, raw) {
|
|
|
1753
1824
|
},
|
|
1754
1825
|
destroy() {
|
|
1755
1826
|
root.textContent = "";
|
|
1756
|
-
|
|
1827
|
+
unbindEvents();
|
|
1757
1828
|
chartTooltip().hidden = true;
|
|
1758
1829
|
}
|
|
1759
1830
|
};
|
|
1760
1831
|
}
|
|
1761
1832
|
|
|
1762
1833
|
// 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
1834
|
function setStatusClass(item, status) {
|
|
1779
1835
|
for (const s of AI_STATUSES) item.classList.toggle(`is-${s}`, s === status);
|
|
1780
1836
|
}
|
|
@@ -1921,11 +1977,11 @@ function applyStatusMeta(parts, status, meta) {
|
|
|
1921
1977
|
parts.metaEl.textContent = meta;
|
|
1922
1978
|
parts.metaEl.hidden = !meta;
|
|
1923
1979
|
}
|
|
1924
|
-
function renderAiToolCall(
|
|
1980
|
+
function renderAiToolCall(el, model) {
|
|
1925
1981
|
if (typeof document === "undefined") {
|
|
1926
|
-
return { el
|
|
1982
|
+
return { el, update: () => void 0 };
|
|
1927
1983
|
}
|
|
1928
|
-
const item =
|
|
1984
|
+
const item = el;
|
|
1929
1985
|
let current = model;
|
|
1930
1986
|
item.className = `ai-tool ai-tool--${model.kind}`;
|
|
1931
1987
|
item.dataset.aiId = model.id;
|
|
@@ -1942,12 +1998,9 @@ function renderAiToolCall(el2, model) {
|
|
|
1942
1998
|
parts.head.setAttribute("aria-expanded", String(open));
|
|
1943
1999
|
body.hidden = !open;
|
|
1944
2000
|
if (fire) {
|
|
1945
|
-
item
|
|
1946
|
-
new CustomEvent("icen:ai-toggle", { detail: { el: item, open }, bubbles: true })
|
|
1947
|
-
);
|
|
2001
|
+
emitIcen(item, "icen:ai-toggle", { el: item, open });
|
|
1948
2002
|
}
|
|
1949
2003
|
};
|
|
1950
|
-
let prevStatus = null;
|
|
1951
2004
|
const apply = (m) => {
|
|
1952
2005
|
setStatusClass(item, m.status);
|
|
1953
2006
|
applyTint(item, m);
|
|
@@ -1960,7 +2013,6 @@ function renderAiToolCall(el2, model) {
|
|
|
1960
2013
|
approval.reason.textContent = reason;
|
|
1961
2014
|
approval.reason.hidden = !reason;
|
|
1962
2015
|
if (!item.dataset.aiUserToggled && defaultExpanded(m)) setOpen(true);
|
|
1963
|
-
prevStatus = m.status;
|
|
1964
2016
|
};
|
|
1965
2017
|
apply(current);
|
|
1966
2018
|
return {
|
|
@@ -1971,11 +2023,11 @@ function renderAiToolCall(el2, model) {
|
|
|
1971
2023
|
}
|
|
1972
2024
|
};
|
|
1973
2025
|
}
|
|
1974
|
-
function renderAiSubagent(
|
|
2026
|
+
function renderAiSubagent(el, model) {
|
|
1975
2027
|
if (typeof document === "undefined") {
|
|
1976
|
-
return { el
|
|
2028
|
+
return { el, update: () => void 0 };
|
|
1977
2029
|
}
|
|
1978
|
-
const item =
|
|
2030
|
+
const item = el;
|
|
1979
2031
|
let current = model;
|
|
1980
2032
|
item.className = "ai-subagent";
|
|
1981
2033
|
item.dataset.aiId = model.id;
|
|
@@ -1992,9 +2044,7 @@ function renderAiSubagent(el2, model) {
|
|
|
1992
2044
|
parts.head.setAttribute("aria-expanded", String(open));
|
|
1993
2045
|
body.hidden = !open;
|
|
1994
2046
|
if (fire) {
|
|
1995
|
-
item
|
|
1996
|
-
new CustomEvent("icen:ai-toggle", { detail: { el: item, open }, bubbles: true })
|
|
1997
|
-
);
|
|
2047
|
+
emitIcen(item, "icen:ai-toggle", { el: item, open });
|
|
1998
2048
|
}
|
|
1999
2049
|
};
|
|
2000
2050
|
const activityHandles = /* @__PURE__ */ new Map();
|
|
@@ -2021,7 +2071,6 @@ function renderAiSubagent(el2, model) {
|
|
|
2021
2071
|
}
|
|
2022
2072
|
}
|
|
2023
2073
|
};
|
|
2024
|
-
let prevStatus = null;
|
|
2025
2074
|
const apply = (m) => {
|
|
2026
2075
|
setStatusClass(item, m.status);
|
|
2027
2076
|
applyTint(item, m);
|
|
@@ -2034,7 +2083,6 @@ function renderAiSubagent(el2, model) {
|
|
|
2034
2083
|
result.textContent = receipt;
|
|
2035
2084
|
result.hidden = !receipt;
|
|
2036
2085
|
if (!item.dataset.aiUserToggled && defaultExpanded(m)) setOpen(true);
|
|
2037
|
-
prevStatus = m.status;
|
|
2038
2086
|
};
|
|
2039
2087
|
apply(current);
|
|
2040
2088
|
return {
|
|
@@ -2048,7 +2096,7 @@ function renderAiSubagent(el2, model) {
|
|
|
2048
2096
|
function setupItem(item, kind) {
|
|
2049
2097
|
const marked = item;
|
|
2050
2098
|
const flag = kind === "tool" ? "__icenAiToolInit" : "__icenAiSubagentInit";
|
|
2051
|
-
if (marked[flag]) return;
|
|
2099
|
+
if (marked[flag]) return () => void 0;
|
|
2052
2100
|
marked[flag] = true;
|
|
2053
2101
|
const headClass = kind === "tool" ? ".ai-tool-head" : ".ai-subagent-head";
|
|
2054
2102
|
const bodyClass = kind === "tool" ? ".ai-tool-body" : ".ai-subagent-body";
|
|
@@ -2058,64 +2106,64 @@ function setupItem(item, kind) {
|
|
|
2058
2106
|
head?.setAttribute("aria-expanded", String(open));
|
|
2059
2107
|
if (body) body.hidden = !open;
|
|
2060
2108
|
if (fire) {
|
|
2061
|
-
item
|
|
2062
|
-
new CustomEvent("icen:ai-toggle", { detail: { el: item, open }, bubbles: true })
|
|
2063
|
-
);
|
|
2109
|
+
emitIcen(item, "icen:ai-toggle", { el: item, open });
|
|
2064
2110
|
}
|
|
2065
2111
|
};
|
|
2066
|
-
|
|
2112
|
+
const onCardClick = (ev) => {
|
|
2067
2113
|
const target = ev.target;
|
|
2068
2114
|
if (!(target instanceof Element)) return;
|
|
2069
2115
|
const ownerOf = (node) => node.closest(".ai-tool, .ai-subagent");
|
|
2070
2116
|
const approveBtn = target.closest("[data-ai-approve]");
|
|
2071
2117
|
if (approveBtn && ownerOf(approveBtn) === item) {
|
|
2072
|
-
item.
|
|
2073
|
-
new CustomEvent("icen:ai-approve", {
|
|
2074
|
-
detail: { id: item.dataset.aiId ?? "", kind: item.dataset.aiKind ?? "" },
|
|
2075
|
-
bubbles: true
|
|
2076
|
-
})
|
|
2077
|
-
);
|
|
2118
|
+
emitIcen(item, "icen:ai-approve", { id: item.dataset.aiId ?? "", kind: item.dataset.aiKind ?? "" });
|
|
2078
2119
|
return;
|
|
2079
2120
|
}
|
|
2080
2121
|
const rejectBtn = target.closest("[data-ai-reject]");
|
|
2081
2122
|
if (rejectBtn && ownerOf(rejectBtn) === item) {
|
|
2082
|
-
item.
|
|
2083
|
-
new CustomEvent("icen:ai-reject", {
|
|
2084
|
-
detail: { id: item.dataset.aiId ?? "", kind: item.dataset.aiKind ?? "" },
|
|
2085
|
-
bubbles: true
|
|
2086
|
-
})
|
|
2087
|
-
);
|
|
2123
|
+
emitIcen(item, "icen:ai-reject", { id: item.dataset.aiId ?? "", kind: item.dataset.aiKind ?? "" });
|
|
2088
2124
|
return;
|
|
2089
2125
|
}
|
|
2090
2126
|
const headEl = target.closest(".ai-tool-head, .ai-subagent-head");
|
|
2091
2127
|
if (!headEl || ownerOf(headEl) !== item || !head) return;
|
|
2092
2128
|
item.dataset.aiUserToggled = "1";
|
|
2093
2129
|
setOpen(head.getAttribute("aria-expanded") !== "true");
|
|
2094
|
-
}
|
|
2130
|
+
};
|
|
2131
|
+
item.addEventListener("click", onCardClick);
|
|
2095
2132
|
if (body?.hidden) {
|
|
2096
2133
|
const kindName = item.dataset.aiKind ?? "";
|
|
2097
2134
|
const isErr = item.classList.contains("is-error");
|
|
2098
2135
|
const isApproval = item.classList.contains("is-approval");
|
|
2099
2136
|
if (isErr || isApproval || kindName === "chart") {
|
|
2100
|
-
item.dataset.aiUserToggled = "";
|
|
2101
2137
|
setOpen(true, false);
|
|
2102
2138
|
delete item.dataset.aiUserToggled;
|
|
2103
2139
|
}
|
|
2104
2140
|
}
|
|
2141
|
+
return () => {
|
|
2142
|
+
item.removeEventListener("click", onCardClick);
|
|
2143
|
+
marked[flag] = false;
|
|
2144
|
+
};
|
|
2105
2145
|
}
|
|
2106
2146
|
function initItems(root, selector, kind) {
|
|
2107
|
-
if (typeof document === "undefined") return;
|
|
2147
|
+
if (typeof document === "undefined") return [];
|
|
2108
2148
|
const scope = root ?? document;
|
|
2109
2149
|
const items = [];
|
|
2110
2150
|
if (scope instanceof Element && scope.matches(selector)) items.push(scope);
|
|
2111
2151
|
items.push(...Array.from(scope.querySelectorAll(selector)));
|
|
2112
|
-
|
|
2152
|
+
return items.map((item) => setupItem(item, kind));
|
|
2113
2153
|
}
|
|
2114
2154
|
function initAiTool(root) {
|
|
2115
|
-
initItems(root, ".ai-tool", "tool");
|
|
2155
|
+
const cleanups = initItems(root, ".ai-tool", "tool");
|
|
2156
|
+
return () => {
|
|
2157
|
+
for (const fn of cleanups) fn();
|
|
2158
|
+
cleanups.length = 0;
|
|
2159
|
+
};
|
|
2116
2160
|
}
|
|
2117
2161
|
function initAiSubagent(root) {
|
|
2118
|
-
initItems(root, ".ai-subagent", "subagent");
|
|
2162
|
+
const cleanups = initItems(root, ".ai-subagent", "subagent");
|
|
2163
|
+
return () => {
|
|
2164
|
+
for (const fn of cleanups) fn();
|
|
2165
|
+
cleanups.length = 0;
|
|
2166
|
+
};
|
|
2119
2167
|
}
|
|
2120
2168
|
export {
|
|
2121
2169
|
initAiSubagent,
|