@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.
- package/AGENTS.md +63 -14
- package/README.md +17 -9
- 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-branch.d.mts +19 -0
- package/dist/behaviors/ai-branch.mjs +217 -0
- package/dist/behaviors/ai-chat.d.mts +11 -7
- package/dist/behaviors/ai-chat.mjs +134 -51
- package/dist/behaviors/ai-composer.d.mts +28 -3
- package/dist/behaviors/ai-composer.mjs +399 -229
- 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-feedback.d.mts +26 -0
- package/dist/behaviors/ai-feedback.mjs +303 -0
- package/dist/behaviors/ai-panel.d.mts +26 -9
- package/dist/behaviors/ai-panel.mjs +211 -110
- package/dist/behaviors/ai-provider.mjs +54 -48
- package/dist/behaviors/ai-threads.d.mts +38 -0
- package/dist/behaviors/ai-threads.mjs +319 -0
- package/dist/behaviors/ai-tool.d.mts +12 -4
- package/dist/behaviors/ai-tool.mjs +316 -263
- package/dist/behaviors/ai-tools.d.mts +9 -6
- package/dist/behaviors/ai-tools.mjs +264 -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 +170 -22
- package/dist/behaviors/charts.mjs +1016 -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 +523 -0
- package/dist/behaviors/events.mjs +208 -0
- package/dist/behaviors/input.mjs +25 -12
- package/dist/behaviors/kb-access.d.mts +34 -0
- package/dist/behaviors/kb-access.mjs +569 -0
- package/dist/behaviors/kb-acl.d.mts +50 -0
- package/dist/behaviors/kb-acl.mjs +405 -0
- package/dist/behaviors/kb-answer.d.mts +38 -0
- package/dist/behaviors/kb-answer.mjs +2055 -0
- package/dist/behaviors/kb-audit.d.mts +23 -0
- package/dist/behaviors/kb-audit.mjs +375 -0
- package/dist/behaviors/kb-canvas.d.mts +44 -0
- package/dist/behaviors/kb-canvas.mjs +589 -0
- package/dist/behaviors/kb-chain.d.mts +15 -0
- package/dist/behaviors/kb-chain.mjs +270 -0
- package/dist/behaviors/kb-checkpoint.d.mts +16 -0
- package/dist/behaviors/kb-checkpoint.mjs +342 -0
- package/dist/behaviors/kb-chunks.d.mts +31 -0
- package/dist/behaviors/kb-chunks.mjs +475 -0
- package/dist/behaviors/kb-citation.d.mts +38 -0
- package/dist/behaviors/kb-citation.mjs +677 -0
- package/dist/behaviors/kb-clarify.d.mts +20 -0
- package/dist/behaviors/kb-clarify.mjs +248 -0
- package/dist/behaviors/kb-conflict.d.mts +22 -0
- package/dist/behaviors/kb-conflict.mjs +284 -0
- package/dist/behaviors/kb-connector.d.mts +17 -0
- package/dist/behaviors/kb-connector.mjs +375 -0
- package/dist/behaviors/kb-core.d.mts +529 -0
- package/dist/behaviors/kb-core.mjs +890 -0
- package/dist/behaviors/kb-eval.d.mts +22 -0
- package/dist/behaviors/kb-eval.mjs +408 -0
- package/dist/behaviors/kb-explain.d.mts +11 -0
- package/dist/behaviors/kb-explain.mjs +243 -0
- package/dist/behaviors/kb-filter.d.mts +31 -0
- package/dist/behaviors/kb-filter.mjs +587 -0
- package/dist/behaviors/kb-gap.d.mts +27 -0
- package/dist/behaviors/kb-gap.mjs +273 -0
- package/dist/behaviors/kb-hittest.d.mts +34 -0
- package/dist/behaviors/kb-hittest.mjs +409 -0
- package/dist/behaviors/kb-metadata.d.mts +29 -0
- package/dist/behaviors/kb-metadata.mjs +326 -0
- package/dist/behaviors/kb-passage.d.mts +41 -0
- package/dist/behaviors/kb-passage.mjs +529 -0
- package/dist/behaviors/kb-pipeline.d.mts +18 -0
- package/dist/behaviors/kb-pipeline.mjs +381 -0
- package/dist/behaviors/kb-qa.d.mts +26 -0
- package/dist/behaviors/kb-qa.mjs +332 -0
- package/dist/behaviors/kb-rerank.d.mts +20 -0
- package/dist/behaviors/kb-rerank.mjs +315 -0
- package/dist/behaviors/kb-retrieval.d.mts +49 -0
- package/dist/behaviors/kb-retrieval.mjs +746 -0
- package/dist/behaviors/kb-review.d.mts +44 -0
- package/dist/behaviors/kb-review.mjs +451 -0
- package/dist/behaviors/kb-sandbox.d.mts +33 -0
- package/dist/behaviors/kb-sandbox.mjs +325 -0
- package/dist/behaviors/kb-segment.d.mts +32 -0
- package/dist/behaviors/kb-segment.mjs +499 -0
- package/dist/behaviors/kb-sources.d.mts +27 -0
- package/dist/behaviors/kb-sources.mjs +373 -0
- package/dist/behaviors/kb-sql.d.mts +43 -0
- package/dist/behaviors/kb-sql.mjs +948 -0
- package/dist/behaviors/kb-trace.d.mts +16 -0
- package/dist/behaviors/kb-trace.mjs +414 -0
- package/dist/behaviors/kb-visibility.d.mts +44 -0
- package/dist/behaviors/kb-visibility.mjs +403 -0
- package/dist/behaviors/kb-who-can.d.mts +32 -0
- package/dist/behaviors/kb-who-can.mjs +379 -0
- 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-branch.mjs +3 -0
- package/dist/components/ai-chat.css +295 -0
- package/dist/components/ai-feedback.mjs +3 -0
- package/dist/components/ai-panel.css +218 -1
- package/dist/components/ai-threads.mjs +3 -0
- 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-graph.css +57 -0
- package/dist/components/chart-graph.mjs +4 -0
- package/dist/components/chart-map.mjs +3 -0
- package/dist/components/chart-pie.css +0 -6
- package/dist/components/chart-scatter.mjs +1 -1
- package/dist/components/charts.css +33 -1
- package/dist/components/charts.mjs +1 -0
- 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/kb-access.mjs +4 -0
- package/dist/components/kb-acl.mjs +4 -0
- package/dist/components/kb-agent.css +481 -0
- package/dist/components/kb-answer.mjs +4 -0
- package/dist/components/kb-audit.mjs +4 -0
- package/dist/components/kb-canvas.mjs +4 -0
- package/dist/components/kb-chain.mjs +4 -0
- package/dist/components/kb-checkpoint.mjs +4 -0
- package/dist/components/kb-chunks.mjs +4 -0
- package/dist/components/kb-citation.mjs +4 -0
- package/dist/components/kb-clarify.mjs +4 -0
- package/dist/components/kb-conflict.mjs +4 -0
- package/dist/components/kb-connector.mjs +4 -0
- package/dist/components/kb-data.css +649 -0
- package/dist/components/kb-eval.mjs +4 -0
- package/dist/components/kb-explain.mjs +4 -0
- package/dist/components/kb-filter.mjs +4 -0
- package/dist/components/kb-gap.mjs +4 -0
- package/dist/components/kb-ground.css +566 -0
- package/dist/components/kb-hittest.mjs +4 -0
- package/dist/components/kb-ingest.css +898 -0
- package/dist/components/kb-metadata.mjs +4 -0
- package/dist/components/kb-ops.css +548 -0
- package/dist/components/kb-passage.mjs +4 -0
- package/dist/components/kb-perm.css +444 -0
- package/dist/components/kb-pipeline.mjs +4 -0
- package/dist/components/kb-qa.mjs +4 -0
- package/dist/components/kb-rerank.mjs +4 -0
- package/dist/components/kb-retrieval.mjs +4 -0
- package/dist/components/kb-review.mjs +4 -0
- package/dist/components/kb-sandbox.mjs +4 -0
- package/dist/components/kb-search.css +940 -0
- package/dist/components/kb-segment.mjs +4 -0
- package/dist/components/kb-sources.mjs +4 -0
- package/dist/components/kb-sql.mjs +4 -0
- package/dist/components/kb-trace.mjs +4 -0
- package/dist/components/kb-visibility.mjs +4 -0
- package/dist/components/kb-who-can.mjs +4 -0
- package/dist/components/kb.css +252 -0
- package/dist/components/kb.mjs +9 -0
- 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 +48 -11
- package/dist/index.mjs +13205 -3615
- package/dist/registry.json +366 -15
- 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 +5713 -267
- package/docs/research/2026-10-03-ai-kb-components.md +392 -0
- package/docs/research/2026-10-03-kb-permissions.md +992 -0
- package/docs/spec/ai-native.md +7 -4
- package/docs/spec/kb-family.md +443 -0
- package/package.json +2 -2
|
@@ -1,6 +1,16 @@
|
|
|
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();
|
|
3
11
|
function toneVar(tone = "accent") {
|
|
12
|
+
const registered = REGISTERED_TONES.get(tone);
|
|
13
|
+
if (registered) return registered;
|
|
4
14
|
switch (tone) {
|
|
5
15
|
case "success":
|
|
6
16
|
return "var(--token-success)";
|
|
@@ -154,17 +164,14 @@ function withChrome(el, opts, hasLegend, paint, state) {
|
|
|
154
164
|
if (state) state.hidden = hidden;
|
|
155
165
|
el.setAttribute("data-legend-hidden", String(hidden));
|
|
156
166
|
syncEye();
|
|
157
|
-
el
|
|
158
|
-
bubbles: true,
|
|
159
|
-
detail: { visible: hidden ? false : true }
|
|
160
|
-
}));
|
|
167
|
+
emitIcen(el, "icen:chart-legend-visibility", { visible: hidden ? false : true });
|
|
161
168
|
});
|
|
162
169
|
syncEye();
|
|
163
170
|
head.append(title, eye);
|
|
164
171
|
el.append(head, body);
|
|
165
172
|
paint(body);
|
|
166
173
|
}
|
|
167
|
-
function toggleLegend(items, scope
|
|
174
|
+
function toggleLegend(items, scope) {
|
|
168
175
|
const legend = document.createElement("div");
|
|
169
176
|
legend.className = "chart-legend chart-legend--toggle";
|
|
170
177
|
for (const item of items) {
|
|
@@ -183,10 +190,7 @@ function toggleLegend(items, scope, _opts) {
|
|
|
183
190
|
scope.querySelectorAll(`[data-chart-key="${CSS.escape(item.key)}"]`).forEach((m) => {
|
|
184
191
|
m.classList.toggle("is-off", off);
|
|
185
192
|
});
|
|
186
|
-
legend
|
|
187
|
-
bubbles: true,
|
|
188
|
-
detail: { key: item.key, seriesIndex: item.si, hidden: off }
|
|
189
|
-
}));
|
|
193
|
+
emitIcen(legend, "icen:chart-legend-toggle", { key: item.key, seriesIndex: item.si, hidden: off });
|
|
190
194
|
};
|
|
191
195
|
row.addEventListener("click", toggle);
|
|
192
196
|
row.addEventListener("keydown", (e) => {
|
|
@@ -205,7 +209,7 @@ function toggleLegend(items, scope, _opts) {
|
|
|
205
209
|
return legend;
|
|
206
210
|
}
|
|
207
211
|
function renderVBar(el, opts) {
|
|
208
|
-
if (typeof document === "undefined") return;
|
|
212
|
+
if (typeof document === "undefined") return el;
|
|
209
213
|
const seriesList = normalizeSeriesInput(opts);
|
|
210
214
|
const labels = opts.labels ?? [];
|
|
211
215
|
const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
|
|
@@ -256,11 +260,13 @@ function renderVBar(el, opts) {
|
|
|
256
260
|
wrap.appendChild(track);
|
|
257
261
|
}
|
|
258
262
|
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
|
|
263
|
+
if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
|
|
260
264
|
});
|
|
265
|
+
attachChartInteraction(el, "vbar");
|
|
266
|
+
return el;
|
|
261
267
|
}
|
|
262
268
|
function renderHBar(el, opts) {
|
|
263
|
-
if (typeof document === "undefined") return;
|
|
269
|
+
if (typeof document === "undefined") return el;
|
|
264
270
|
withChrome(el, opts, false, (body) => {
|
|
265
271
|
const n = Math.min(opts.labels.length, opts.values.length);
|
|
266
272
|
if (n === 0) {
|
|
@@ -294,9 +300,11 @@ function renderHBar(el, opts) {
|
|
|
294
300
|
});
|
|
295
301
|
body.appendChild(wrap);
|
|
296
302
|
});
|
|
303
|
+
attachChartInteraction(el, "hbar");
|
|
304
|
+
return el;
|
|
297
305
|
}
|
|
298
306
|
function renderStack(el, opts) {
|
|
299
|
-
if (typeof document === "undefined") return;
|
|
307
|
+
if (typeof document === "undefined") return el;
|
|
300
308
|
withChrome(el, opts, opts.segments.length > 0, (body) => {
|
|
301
309
|
const total = opts.segments.reduce((sum, s) => sum + s.value, 0);
|
|
302
310
|
if (opts.segments.length === 0 || total <= 0) {
|
|
@@ -305,13 +313,13 @@ function renderStack(el, opts) {
|
|
|
305
313
|
}
|
|
306
314
|
const bar = document.createElement("div");
|
|
307
315
|
bar.className = "chart-stack";
|
|
308
|
-
for (const seg of opts.segments) {
|
|
316
|
+
for (const [segIndex, seg] of opts.segments.entries()) {
|
|
309
317
|
const s = document.createElement("div");
|
|
310
318
|
s.className = "chart-stack-seg";
|
|
311
319
|
s.style.width = `${seg.value / total * 100}%`;
|
|
312
320
|
if (seg.tone) s.style.setProperty("--chart-tone", toneVar(seg.tone));
|
|
313
321
|
s.setAttribute("title", `${seg.label}: ${fmt(opts, seg.value)}`);
|
|
314
|
-
setMarkData(s, { index:
|
|
322
|
+
setMarkData(s, { index: segIndex, label: seg.label, value: seg.value });
|
|
315
323
|
bar.appendChild(s);
|
|
316
324
|
}
|
|
317
325
|
const legend = document.createElement("div");
|
|
@@ -321,9 +329,11 @@ function renderStack(el, opts) {
|
|
|
321
329
|
}
|
|
322
330
|
body.append(bar, legend);
|
|
323
331
|
});
|
|
332
|
+
attachChartInteraction(el, "stack");
|
|
333
|
+
return el;
|
|
324
334
|
}
|
|
325
335
|
function renderDonut(el, opts) {
|
|
326
|
-
if (typeof document === "undefined") return;
|
|
336
|
+
if (typeof document === "undefined") return el;
|
|
327
337
|
withChrome(el, opts, opts.segments.length > 0, (body) => {
|
|
328
338
|
const total = opts.segments.reduce((sum, s) => sum + s.value, 0);
|
|
329
339
|
if (opts.segments.length === 0 || total <= 0) {
|
|
@@ -370,10 +380,12 @@ function renderDonut(el, opts) {
|
|
|
370
380
|
wrap.append(svg, legend);
|
|
371
381
|
body.appendChild(wrap);
|
|
372
382
|
});
|
|
383
|
+
attachChartInteraction(el, "donut");
|
|
384
|
+
return el;
|
|
373
385
|
}
|
|
374
386
|
var lineGradientSeq = 0;
|
|
375
387
|
function renderLine(el, opts) {
|
|
376
|
-
if (typeof document === "undefined") return;
|
|
388
|
+
if (typeof document === "undefined") return el;
|
|
377
389
|
const seriesList = normalizeSeriesInput(opts);
|
|
378
390
|
const labels = opts.labels ?? [];
|
|
379
391
|
const n = Math.min(labels.length, ...seriesList.map((s) => s.values.length));
|
|
@@ -478,11 +490,13 @@ function renderLine(el, opts) {
|
|
|
478
490
|
if (defs.firstChild) svg.insertBefore(defs, svg.firstChild);
|
|
479
491
|
wrap.append(svg, labelsRow(sampleLabels(labelsUsed)));
|
|
480
492
|
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
|
|
493
|
+
if (seriesList.length > 1) body.appendChild(toggleLegend(seriesList.map((s, si) => ({ key: s.name, label: s.name, tone: s.tone, si })), wrap));
|
|
482
494
|
});
|
|
495
|
+
attachChartInteraction(el, "line");
|
|
496
|
+
return el;
|
|
483
497
|
}
|
|
484
498
|
function renderRadar(el, opts) {
|
|
485
|
-
if (typeof document === "undefined") return;
|
|
499
|
+
if (typeof document === "undefined") return el;
|
|
486
500
|
withChrome(el, opts, opts.series.length > 1, (body) => {
|
|
487
501
|
const axes = opts.axes;
|
|
488
502
|
const n = axes.length;
|
|
@@ -584,13 +598,12 @@ function renderRadar(el, opts) {
|
|
|
584
598
|
}
|
|
585
599
|
legendItems.push({ key: s.name || `\u7CFB\u5217${si + 1}`, label: s.name || `\u7CFB\u5217${si + 1}`, tone: s.tone, si });
|
|
586
600
|
});
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
l.className = "chart-legend";
|
|
590
|
-
return l;
|
|
591
|
-
})());
|
|
601
|
+
const legend = opts.series.length > 1 ? toggleLegend(legendItems, wrap) : null;
|
|
602
|
+
wrap.append(svg, ...legend ? [legend] : []);
|
|
592
603
|
body.appendChild(wrap);
|
|
593
604
|
});
|
|
605
|
+
attachChartInteraction(el, "radar");
|
|
606
|
+
return el;
|
|
594
607
|
}
|
|
595
608
|
function niceCeil(v) {
|
|
596
609
|
if (v <= 0) return 1;
|
|
@@ -609,8 +622,41 @@ function isoDate(d) {
|
|
|
609
622
|
const day = String(d.getDate()).padStart(2, "0");
|
|
610
623
|
return `${d.getFullYear()}-${m}-${day}`;
|
|
611
624
|
}
|
|
625
|
+
function weekdayLabelAt(defaults, day) {
|
|
626
|
+
return defaults[(day + 6) % 7] ?? "";
|
|
627
|
+
}
|
|
628
|
+
function levelOf(v, max) {
|
|
629
|
+
return v <= 0 ? 0 : Math.min(4, Math.ceil(v / max * 4));
|
|
630
|
+
}
|
|
631
|
+
function padFirstWeek(grid, firstDate, weekStart) {
|
|
632
|
+
const first = /* @__PURE__ */ new Date(`${firstDate}T00:00:00`);
|
|
633
|
+
const pad = (first.getDay() + 7 - weekStart) % 7;
|
|
634
|
+
for (let i = 0; i < pad; i++) {
|
|
635
|
+
const blank = document.createElement("span");
|
|
636
|
+
blank.className = "chart-heatmap-cell is-blank";
|
|
637
|
+
grid.appendChild(blank);
|
|
638
|
+
}
|
|
639
|
+
return pad;
|
|
640
|
+
}
|
|
641
|
+
function heatmapLegend(labels) {
|
|
642
|
+
const legend = document.createElement("div");
|
|
643
|
+
legend.className = "chart-heatmap-legend";
|
|
644
|
+
const less = document.createElement("span");
|
|
645
|
+
less.textContent = labels?.less ?? "\u5C11";
|
|
646
|
+
const more = document.createElement("span");
|
|
647
|
+
more.textContent = labels?.more ?? "\u591A";
|
|
648
|
+
legend.appendChild(less);
|
|
649
|
+
for (let lv = 0; lv <= 4; lv++) {
|
|
650
|
+
const cell = document.createElement("span");
|
|
651
|
+
cell.className = "chart-heatmap-cell";
|
|
652
|
+
cell.dataset.level = String(lv);
|
|
653
|
+
legend.appendChild(cell);
|
|
654
|
+
}
|
|
655
|
+
legend.appendChild(more);
|
|
656
|
+
return legend;
|
|
657
|
+
}
|
|
612
658
|
function renderHeatmap(el, opts) {
|
|
613
|
-
if (typeof document === "undefined") return;
|
|
659
|
+
if (typeof document === "undefined") return el;
|
|
614
660
|
withChrome(el, opts, true, (body) => {
|
|
615
661
|
let entries;
|
|
616
662
|
if (opts.data && opts.data.length > 0) {
|
|
@@ -629,48 +675,29 @@ function renderHeatmap(el, opts) {
|
|
|
629
675
|
const weeks = opts.weeks ?? Math.ceil(entries.length / 7);
|
|
630
676
|
if (entries.length > weeks * 7) entries = entries.slice(entries.length - weeks * 7);
|
|
631
677
|
const max = maxOf(entries.map((e) => e.value));
|
|
632
|
-
const levelOf = (v) => v <= 0 ? 0 : Math.min(4, Math.ceil(v / max * 4));
|
|
633
678
|
const wrap = document.createElement("div");
|
|
634
679
|
wrap.className = "chart-heatmap";
|
|
635
680
|
if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
|
|
636
681
|
const grid = document.createElement("div");
|
|
637
682
|
grid.className = "chart-heatmap-grid";
|
|
638
683
|
grid.setAttribute("role", "img");
|
|
639
|
-
|
|
640
|
-
const
|
|
641
|
-
for (let i = 0; i < pad; i++) {
|
|
642
|
-
const blank = document.createElement("span");
|
|
643
|
-
blank.className = "chart-heatmap-cell is-blank";
|
|
644
|
-
grid.appendChild(blank);
|
|
645
|
-
}
|
|
646
|
-
for (const entry of entries) {
|
|
684
|
+
padFirstWeek(grid, entries[0].date, opts.weekStart === 0 ? 0 : 1);
|
|
685
|
+
for (const [i, entry] of entries.entries()) {
|
|
647
686
|
const cell = document.createElement("span");
|
|
648
687
|
cell.className = "chart-heatmap-cell";
|
|
649
|
-
cell.dataset.level = String(levelOf(entry.value));
|
|
688
|
+
cell.dataset.level = String(levelOf(entry.value, max));
|
|
650
689
|
cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
|
|
651
|
-
setMarkData(cell, { index:
|
|
690
|
+
setMarkData(cell, { index: i, label: entry.date, value: entry.value });
|
|
652
691
|
grid.appendChild(cell);
|
|
653
692
|
}
|
|
654
|
-
|
|
655
|
-
legend.className = "chart-heatmap-legend";
|
|
656
|
-
const less = document.createElement("span");
|
|
657
|
-
less.textContent = "\u5C11";
|
|
658
|
-
const more = document.createElement("span");
|
|
659
|
-
more.textContent = "\u591A";
|
|
660
|
-
legend.appendChild(less);
|
|
661
|
-
for (let lv = 0; lv <= 4; lv++) {
|
|
662
|
-
const cell = document.createElement("span");
|
|
663
|
-
cell.className = "chart-heatmap-cell";
|
|
664
|
-
cell.dataset.level = String(lv);
|
|
665
|
-
legend.appendChild(cell);
|
|
666
|
-
}
|
|
667
|
-
legend.appendChild(more);
|
|
668
|
-
wrap.append(grid, legend);
|
|
693
|
+
wrap.append(grid, heatmapLegend(opts.labels));
|
|
669
694
|
body.appendChild(wrap);
|
|
670
695
|
});
|
|
696
|
+
attachChartInteraction(el, "heatmap");
|
|
697
|
+
return el;
|
|
671
698
|
}
|
|
672
699
|
function renderSparkline(el, opts) {
|
|
673
|
-
if (typeof document === "undefined") return;
|
|
700
|
+
if (typeof document === "undefined") return el;
|
|
674
701
|
withChrome(el, opts, false, (body) => {
|
|
675
702
|
const values = opts.values;
|
|
676
703
|
if (values.length === 0) {
|
|
@@ -725,9 +752,11 @@ function renderSparkline(el, opts) {
|
|
|
725
752
|
wrap.appendChild(svg);
|
|
726
753
|
body.appendChild(wrap);
|
|
727
754
|
});
|
|
755
|
+
attachChartInteraction(el, "sparkline");
|
|
756
|
+
return el;
|
|
728
757
|
}
|
|
729
758
|
function renderGauge(el, opts) {
|
|
730
|
-
if (typeof document === "undefined") return;
|
|
759
|
+
if (typeof document === "undefined") return el;
|
|
731
760
|
el.textContent = "";
|
|
732
761
|
const max = opts.max ?? 100;
|
|
733
762
|
const ratio = max > 0 ? Math.min(1, Math.max(0, opts.value / max)) : 0;
|
|
@@ -771,9 +800,11 @@ function renderGauge(el, opts) {
|
|
|
771
800
|
}
|
|
772
801
|
wrap.append(svg, center);
|
|
773
802
|
el.appendChild(wrap);
|
|
803
|
+
attachChartInteraction(el, "gauge");
|
|
804
|
+
return el;
|
|
774
805
|
}
|
|
775
806
|
function renderCalendar(el, opts) {
|
|
776
|
-
if (typeof document === "undefined") return;
|
|
807
|
+
if (typeof document === "undefined") return el;
|
|
777
808
|
withChrome(el, opts, true, (body) => {
|
|
778
809
|
let entries;
|
|
779
810
|
if (opts.data && opts.data.length > 0) {
|
|
@@ -787,6 +818,10 @@ function renderCalendar(el, opts) {
|
|
|
787
818
|
const wrap = document.createElement("div");
|
|
788
819
|
wrap.className = "chart-calendar";
|
|
789
820
|
if (opts.tone) wrap.style.setProperty("--chart-tone", toneVar(opts.tone));
|
|
821
|
+
const weekStart = opts.weekStart === 0 ? 0 : 1;
|
|
822
|
+
const weekdayDefaults = ["\u4E00", "", "\u4E09", "", "\u4E94", "", "\u65E5"];
|
|
823
|
+
const weekdayLabels = opts.labels?.weekdays ?? weekdayDefaults.map((_, i) => weekdayLabelAt(weekdayDefaults, (weekStart + i) % 7));
|
|
824
|
+
const monthLabel = (m) => opts.labels?.months?.[m] ?? `${m + 1}\u6708`;
|
|
790
825
|
const grid = document.createElement("div");
|
|
791
826
|
grid.className = "chart-heatmap-grid";
|
|
792
827
|
grid.setAttribute("role", "img");
|
|
@@ -794,20 +829,13 @@ function renderCalendar(el, opts) {
|
|
|
794
829
|
months.className = "chart-calendar-months";
|
|
795
830
|
const weekdayRow = document.createElement("div");
|
|
796
831
|
weekdayRow.className = "chart-calendar-weekdays";
|
|
797
|
-
|
|
832
|
+
weekdayLabels.forEach((w) => {
|
|
798
833
|
const s = document.createElement("span");
|
|
799
834
|
s.textContent = w;
|
|
800
835
|
weekdayRow.appendChild(s);
|
|
801
836
|
});
|
|
802
837
|
const max = maxOf(entries.map((e) => e.value));
|
|
803
|
-
const
|
|
804
|
-
const first = /* @__PURE__ */ new Date(`${entries[0].date}T00:00:00`);
|
|
805
|
-
const pad = (first.getDay() + 6) % 7;
|
|
806
|
-
for (let i = 0; i < pad; i++) {
|
|
807
|
-
const blank = document.createElement("span");
|
|
808
|
-
blank.className = "chart-heatmap-cell is-blank";
|
|
809
|
-
grid.appendChild(blank);
|
|
810
|
-
}
|
|
838
|
+
const pad = padFirstWeek(grid, entries[0].date, weekStart);
|
|
811
839
|
let lastMonth = -1;
|
|
812
840
|
entries.forEach((entry, i) => {
|
|
813
841
|
const d = /* @__PURE__ */ new Date(`${entry.date}T00:00:00`);
|
|
@@ -816,39 +844,28 @@ function renderCalendar(el, opts) {
|
|
|
816
844
|
lastMonth = d.getMonth();
|
|
817
845
|
const label = document.createElement("span");
|
|
818
846
|
label.className = "chart-calendar-month";
|
|
819
|
-
label.textContent =
|
|
847
|
+
label.textContent = monthLabel(d.getMonth());
|
|
820
848
|
label.style.setProperty("--col", String(col));
|
|
821
849
|
months.appendChild(label);
|
|
822
850
|
}
|
|
823
851
|
const cell = document.createElement("span");
|
|
824
852
|
cell.className = "chart-heatmap-cell";
|
|
825
|
-
cell.dataset.level = String(levelOf(entry.value));
|
|
853
|
+
cell.dataset.level = String(levelOf(entry.value, max));
|
|
826
854
|
cell.setAttribute("title", `${entry.date}\uFF1A${fmt(opts, entry.value)}`);
|
|
827
855
|
setMarkData(cell, { index: i, label: entry.date, value: entry.value });
|
|
828
856
|
grid.appendChild(cell);
|
|
829
857
|
});
|
|
830
|
-
const legend =
|
|
831
|
-
legend.className = "chart-heatmap-legend";
|
|
832
|
-
const less = document.createElement("span");
|
|
833
|
-
less.textContent = "\u5C11";
|
|
834
|
-
const more = document.createElement("span");
|
|
835
|
-
more.textContent = "\u591A";
|
|
836
|
-
legend.appendChild(less);
|
|
837
|
-
for (let lv = 0; lv <= 4; lv++) {
|
|
838
|
-
const cell = document.createElement("span");
|
|
839
|
-
cell.className = "chart-heatmap-cell";
|
|
840
|
-
cell.dataset.level = String(lv);
|
|
841
|
-
legend.appendChild(cell);
|
|
842
|
-
}
|
|
843
|
-
legend.appendChild(more);
|
|
858
|
+
const legend = heatmapLegend(opts.labels);
|
|
844
859
|
const columns = Math.max(1, Math.ceil((pad + entries.length) / 7));
|
|
845
860
|
months.style.setProperty("--columns", String(columns));
|
|
846
861
|
wrap.append(months, weekdayRow, grid, legend);
|
|
847
862
|
body.appendChild(wrap);
|
|
848
863
|
});
|
|
864
|
+
attachChartInteraction(el, "calendar");
|
|
865
|
+
return el;
|
|
849
866
|
}
|
|
850
867
|
function renderScatter(el, opts) {
|
|
851
|
-
if (typeof document === "undefined") return;
|
|
868
|
+
if (typeof document === "undefined") return el;
|
|
852
869
|
withChrome(el, opts, false, (body) => {
|
|
853
870
|
const pts = opts.points.filter((p) => Number.isFinite(p.x) && Number.isFinite(p.y));
|
|
854
871
|
if (pts.length === 0) {
|
|
@@ -893,7 +910,7 @@ function renderScatter(el, opts) {
|
|
|
893
910
|
"font-size": "8",
|
|
894
911
|
fill: "var(--token-text-faint)"
|
|
895
912
|
});
|
|
896
|
-
txt.textContent =
|
|
913
|
+
txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
|
|
897
914
|
svg.appendChild(txt);
|
|
898
915
|
}
|
|
899
916
|
for (let i = 0; i <= 4; i++) {
|
|
@@ -915,7 +932,7 @@ function renderScatter(el, opts) {
|
|
|
915
932
|
"font-size": "8",
|
|
916
933
|
fill: "var(--token-text-faint)"
|
|
917
934
|
});
|
|
918
|
-
txt.textContent =
|
|
935
|
+
txt.textContent = fmt(opts, Math.round(tv * 10) / 10);
|
|
919
936
|
svg.appendChild(txt);
|
|
920
937
|
}
|
|
921
938
|
if (opts.yLabel) {
|
|
@@ -939,7 +956,7 @@ function renderScatter(el, opts) {
|
|
|
939
956
|
"stroke-width": "1"
|
|
940
957
|
});
|
|
941
958
|
const title = svgEl("title", {});
|
|
942
|
-
title.textContent = `${p.label ?? `#${i + 1}`}: (${p.x}, ${p.y}${p.size != null ? `, size ${p.size}` : ""})`;
|
|
959
|
+
title.textContent = `${p.label ?? `#${i + 1}`}: (${fmt(opts, p.x)}, ${fmt(opts, p.y)}${p.size != null ? `, size ${fmt(opts, p.size)}` : ""})`;
|
|
943
960
|
c.appendChild(title);
|
|
944
961
|
setMarkData(c, { index: i, label: p.label ?? `#${i + 1}`, value: p.y });
|
|
945
962
|
c.dataset.chartX = String(p.x);
|
|
@@ -948,6 +965,8 @@ function renderScatter(el, opts) {
|
|
|
948
965
|
wrap.appendChild(svg);
|
|
949
966
|
body.appendChild(wrap);
|
|
950
967
|
});
|
|
968
|
+
attachChartInteraction(el, "scatter");
|
|
969
|
+
return el;
|
|
951
970
|
}
|
|
952
971
|
function chartFormatValue(format) {
|
|
953
972
|
if (!format) return (v) => String(v);
|
|
@@ -1078,7 +1097,7 @@ function normalizeChartSpec(raw) {
|
|
|
1078
1097
|
}
|
|
1079
1098
|
}
|
|
1080
1099
|
if (!src || typeof src !== "object" || Array.isArray(src)) {
|
|
1081
|
-
if (Array.isArray(
|
|
1100
|
+
if (Array.isArray(src)) src = { data: src };
|
|
1082
1101
|
else return {};
|
|
1083
1102
|
}
|
|
1084
1103
|
const s = src;
|
|
@@ -1148,6 +1167,7 @@ function normalizeChartSpec(raw) {
|
|
|
1148
1167
|
}
|
|
1149
1168
|
return spec;
|
|
1150
1169
|
}
|
|
1170
|
+
var TEMPORAL_HIT_RATIO = 0.6;
|
|
1151
1171
|
function looksTemporal(labels) {
|
|
1152
1172
|
if (labels.length === 0) return false;
|
|
1153
1173
|
let hits = 0;
|
|
@@ -1161,7 +1181,7 @@ function looksTemporal(labels) {
|
|
|
1161
1181
|
else if (/^(v|ver\.?|version[ .]?)?\d+(\.\d+)+$/i.test(l)) hits++;
|
|
1162
1182
|
else if (/^\d{4}$/.test(l)) hits++;
|
|
1163
1183
|
}
|
|
1164
|
-
return hits / labels.length >=
|
|
1184
|
+
return hits / labels.length >= TEMPORAL_HIT_RATIO;
|
|
1165
1185
|
}
|
|
1166
1186
|
function inferChartType(spec) {
|
|
1167
1187
|
if (spec.points && spec.points.length > 0) return "scatter";
|
|
@@ -1196,7 +1216,7 @@ function dispatchChartEvent(root, type, mark, chart, phase, pointer) {
|
|
|
1196
1216
|
pointerX: pointer?.x,
|
|
1197
1217
|
pointerY: pointer?.y
|
|
1198
1218
|
};
|
|
1199
|
-
root
|
|
1219
|
+
emitIcen(root, `icen:chart-${type}`, detail);
|
|
1200
1220
|
}
|
|
1201
1221
|
var tooltipEl = null;
|
|
1202
1222
|
function chartTooltip() {
|
|
@@ -1209,6 +1229,12 @@ function chartTooltip() {
|
|
|
1209
1229
|
}
|
|
1210
1230
|
return tooltipEl;
|
|
1211
1231
|
}
|
|
1232
|
+
function applyTooltipSizing(host) {
|
|
1233
|
+
const tip = chartTooltip();
|
|
1234
|
+
const raw = host?.closest(".chart, [data-chart-root]")?.getAttribute("data-panel-max") ?? null;
|
|
1235
|
+
const n = raw != null ? Number(raw) : NaN;
|
|
1236
|
+
tip.style.maxWidth = Number.isFinite(n) && n > 0 ? `${n}px` : "";
|
|
1237
|
+
}
|
|
1212
1238
|
function tooltipText(mark) {
|
|
1213
1239
|
const label = mark.getAttribute("data-chart-label") ?? "";
|
|
1214
1240
|
const series = mark.getAttribute("data-chart-series-name");
|
|
@@ -1228,6 +1254,101 @@ function moveTooltip(x, y) {
|
|
|
1228
1254
|
tip.style.left = `${Math.round(left)}px`;
|
|
1229
1255
|
tip.style.top = `${Math.round(top)}px`;
|
|
1230
1256
|
}
|
|
1257
|
+
var chartDelegateMeta = /* @__PURE__ */ new WeakMap();
|
|
1258
|
+
var chartDelegateUnbind = /* @__PURE__ */ new WeakMap();
|
|
1259
|
+
function bindChartEvents(root) {
|
|
1260
|
+
const noop = () => void 0;
|
|
1261
|
+
if (typeof document === "undefined") return noop;
|
|
1262
|
+
const attached = root;
|
|
1263
|
+
if (attached.__icenChartBound) {
|
|
1264
|
+
const prev = chartDelegateUnbind.get(root);
|
|
1265
|
+
if (prev) return prev;
|
|
1266
|
+
}
|
|
1267
|
+
for (let p = root.parentElement; p; p = p.parentElement) {
|
|
1268
|
+
if (p.__icenChartBound) return noop;
|
|
1269
|
+
}
|
|
1270
|
+
const typeNow = () => chartDelegateMeta.get(root)?.type() ?? "vbar";
|
|
1271
|
+
const tipEnabled = () => chartDelegateMeta.get(root)?.tooltip() ?? true;
|
|
1272
|
+
const markOf = (e) => {
|
|
1273
|
+
const t = e.target;
|
|
1274
|
+
return t instanceof Element ? t.closest("[data-chart-mark]") : null;
|
|
1275
|
+
};
|
|
1276
|
+
let hoverMark = null;
|
|
1277
|
+
const enterMark = (mark, x, y) => {
|
|
1278
|
+
hoverMark = mark;
|
|
1279
|
+
mark.classList.add("is-hover");
|
|
1280
|
+
dispatchChartEvent(root, "hover", mark, typeNow(), "enter", { x, y });
|
|
1281
|
+
if (tipEnabled()) {
|
|
1282
|
+
const tip = chartTooltip();
|
|
1283
|
+
tip.textContent = tooltipText(mark);
|
|
1284
|
+
applyTooltipSizing(mark);
|
|
1285
|
+
tip.hidden = false;
|
|
1286
|
+
moveTooltip(x, y);
|
|
1287
|
+
}
|
|
1288
|
+
};
|
|
1289
|
+
const leaveMark = (pointer) => {
|
|
1290
|
+
if (!hoverMark) return;
|
|
1291
|
+
hoverMark.classList.remove("is-hover");
|
|
1292
|
+
dispatchChartEvent(root, "hover", hoverMark, typeNow(), "leave", pointer);
|
|
1293
|
+
hoverMark = null;
|
|
1294
|
+
chartTooltip().hidden = true;
|
|
1295
|
+
};
|
|
1296
|
+
const bindings = [];
|
|
1297
|
+
const on = (type, fn) => {
|
|
1298
|
+
const listener = (evt) => fn(evt);
|
|
1299
|
+
root.addEventListener(type, listener);
|
|
1300
|
+
bindings.push([type, listener]);
|
|
1301
|
+
};
|
|
1302
|
+
on("pointerover", (e) => {
|
|
1303
|
+
const mark = markOf(e);
|
|
1304
|
+
if (!mark || mark === hoverMark) return;
|
|
1305
|
+
leaveMark({ x: e.clientX, y: e.clientY });
|
|
1306
|
+
enterMark(mark, e.clientX, e.clientY);
|
|
1307
|
+
});
|
|
1308
|
+
on("pointermove", (e) => {
|
|
1309
|
+
if (hoverMark && tipEnabled()) {
|
|
1310
|
+
dispatchChartEvent(root, "hover", hoverMark, typeNow(), "move", { x: e.clientX, y: e.clientY });
|
|
1311
|
+
moveTooltip(e.clientX, e.clientY);
|
|
1312
|
+
}
|
|
1313
|
+
});
|
|
1314
|
+
on("pointerout", (e) => {
|
|
1315
|
+
const mark = markOf(e);
|
|
1316
|
+
if (mark && mark === hoverMark) leaveMark({ x: e.clientX, y: e.clientY });
|
|
1317
|
+
});
|
|
1318
|
+
on("pointerleave", () => leaveMark());
|
|
1319
|
+
on("click", (e) => {
|
|
1320
|
+
const mark = markOf(e);
|
|
1321
|
+
if (mark) dispatchChartEvent(root, "click", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
|
|
1322
|
+
});
|
|
1323
|
+
on("dblclick", (e) => {
|
|
1324
|
+
const mark = markOf(e);
|
|
1325
|
+
if (mark) dispatchChartEvent(root, "dblclick", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
|
|
1326
|
+
});
|
|
1327
|
+
on("contextmenu", (e) => {
|
|
1328
|
+
const mark = markOf(e);
|
|
1329
|
+
if (!mark) return;
|
|
1330
|
+
e.preventDefault();
|
|
1331
|
+
dispatchChartEvent(root, "contextmenu", mark, typeNow(), void 0, { x: e.clientX, y: e.clientY });
|
|
1332
|
+
});
|
|
1333
|
+
const unbind = () => {
|
|
1334
|
+
for (const [type, listener] of bindings) root.removeEventListener(type, listener);
|
|
1335
|
+
bindings.length = 0;
|
|
1336
|
+
attached.__icenChartBound = false;
|
|
1337
|
+
chartDelegateUnbind.delete(root);
|
|
1338
|
+
if (hoverMark) {
|
|
1339
|
+
hoverMark.classList.remove("is-hover");
|
|
1340
|
+
hoverMark = null;
|
|
1341
|
+
}
|
|
1342
|
+
chartTooltip().hidden = true;
|
|
1343
|
+
};
|
|
1344
|
+
attached.__icenChartBound = true;
|
|
1345
|
+
chartDelegateUnbind.set(root, unbind);
|
|
1346
|
+
return unbind;
|
|
1347
|
+
}
|
|
1348
|
+
function attachChartInteraction(el, type) {
|
|
1349
|
+
chartDelegateMeta.set(el, { type: () => type, tooltip: () => true });
|
|
1350
|
+
bindChartEvents(el);
|
|
1351
|
+
}
|
|
1231
1352
|
function hasLegendOf(spec) {
|
|
1232
1353
|
switch (spec.type) {
|
|
1233
1354
|
case "donut":
|
|
@@ -1250,9 +1371,6 @@ function renderChart(el, raw) {
|
|
|
1250
1371
|
return { el, spec: {}, update: noop, on: () => noop, destroy: noop };
|
|
1251
1372
|
}
|
|
1252
1373
|
const root = el;
|
|
1253
|
-
const attached = root;
|
|
1254
|
-
const wasBound = attached.__icenChartBound === true;
|
|
1255
|
-
attached.__icenChartBound = true;
|
|
1256
1374
|
let current = normalizeChartSpec(raw);
|
|
1257
1375
|
const typeOf = () => current.type ?? "vbar";
|
|
1258
1376
|
const legendState = { hidden: null };
|
|
@@ -1287,10 +1405,10 @@ function renderChart(el, raw) {
|
|
|
1287
1405
|
break;
|
|
1288
1406
|
}
|
|
1289
1407
|
case "heatmap":
|
|
1290
|
-
renderHeatmap(body, { values: spec.values, data: spec.data, weeks: spec.weeks, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
|
|
1408
|
+
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 });
|
|
1291
1409
|
break;
|
|
1292
1410
|
case "calendar":
|
|
1293
|
-
renderCalendar(body, { dates: spec.dates, values: spec.values, data: spec.data, tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
|
|
1411
|
+
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 });
|
|
1294
1412
|
break;
|
|
1295
1413
|
case "sparkline":
|
|
1296
1414
|
renderSparkline(body, { labels: spec.labels ?? [], values: spec.values ?? [], tone: spec.tone, formatValue, emptyLabel: spec.emptyLabel, title: spec.title, legend: spec.legend });
|
|
@@ -1306,63 +1424,8 @@ function renderChart(el, raw) {
|
|
|
1306
1424
|
}
|
|
1307
1425
|
});
|
|
1308
1426
|
};
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
const t = e.target;
|
|
1312
|
-
return t instanceof Element ? t.closest("[data-chart-mark]") : null;
|
|
1313
|
-
};
|
|
1314
|
-
const tipEnabled = () => current.tooltip !== false;
|
|
1315
|
-
let hoverMark = null;
|
|
1316
|
-
const enterMark = (mark, x, y) => {
|
|
1317
|
-
hoverMark = mark;
|
|
1318
|
-
mark.classList.add("is-hover");
|
|
1319
|
-
dispatchChartEvent(root, "hover", mark, typeOf(), "enter", { x, y });
|
|
1320
|
-
if (tipEnabled()) {
|
|
1321
|
-
const tip = chartTooltip();
|
|
1322
|
-
tip.textContent = tooltipText(mark);
|
|
1323
|
-
tip.hidden = false;
|
|
1324
|
-
moveTooltip(x, y);
|
|
1325
|
-
}
|
|
1326
|
-
};
|
|
1327
|
-
const leaveMark = (pointer) => {
|
|
1328
|
-
if (!hoverMark) return;
|
|
1329
|
-
hoverMark.classList.remove("is-hover");
|
|
1330
|
-
dispatchChartEvent(root, "hover", hoverMark, typeOf(), "leave", pointer);
|
|
1331
|
-
hoverMark = null;
|
|
1332
|
-
chartTooltip().hidden = true;
|
|
1333
|
-
};
|
|
1334
|
-
root.addEventListener("pointerover", (e) => {
|
|
1335
|
-
const mark = markOf(e);
|
|
1336
|
-
if (!mark || mark === hoverMark) return;
|
|
1337
|
-
leaveMark({ x: e.clientX, y: e.clientY });
|
|
1338
|
-
enterMark(mark, e.clientX, e.clientY);
|
|
1339
|
-
});
|
|
1340
|
-
root.addEventListener("pointermove", (e) => {
|
|
1341
|
-
if (hoverMark && tipEnabled()) {
|
|
1342
|
-
dispatchChartEvent(root, "hover", hoverMark, typeOf(), "move", { x: e.clientX, y: e.clientY });
|
|
1343
|
-
moveTooltip(e.clientX, e.clientY);
|
|
1344
|
-
}
|
|
1345
|
-
});
|
|
1346
|
-
root.addEventListener("pointerout", (e) => {
|
|
1347
|
-
const mark = markOf(e);
|
|
1348
|
-
if (mark && mark === hoverMark) leaveMark({ x: e.clientX, y: e.clientY });
|
|
1349
|
-
});
|
|
1350
|
-
root.addEventListener("pointerleave", () => leaveMark());
|
|
1351
|
-
root.addEventListener("click", (e) => {
|
|
1352
|
-
const mark = markOf(e);
|
|
1353
|
-
if (mark) dispatchChartEvent(root, "click", mark, typeOf(), void 0, { x: e.clientX, y: e.clientY });
|
|
1354
|
-
});
|
|
1355
|
-
root.addEventListener("dblclick", (e) => {
|
|
1356
|
-
const mark = markOf(e);
|
|
1357
|
-
if (mark) dispatchChartEvent(root, "dblclick", mark, typeOf(), void 0, { x: e.clientX, y: e.clientY });
|
|
1358
|
-
});
|
|
1359
|
-
root.addEventListener("contextmenu", (e) => {
|
|
1360
|
-
const mark = markOf(e);
|
|
1361
|
-
if (!mark) return;
|
|
1362
|
-
e.preventDefault();
|
|
1363
|
-
dispatchChartEvent(root, "contextmenu", mark, typeOf(), void 0, { x: e.clientX, y: e.clientY });
|
|
1364
|
-
});
|
|
1365
|
-
}
|
|
1427
|
+
chartDelegateMeta.set(root, { type: typeOf, tooltip: () => current.tooltip !== false });
|
|
1428
|
+
const unbindEvents = bindChartEvents(root);
|
|
1366
1429
|
render();
|
|
1367
1430
|
return {
|
|
1368
1431
|
el: root,
|
|
@@ -1380,14 +1443,20 @@ function renderChart(el, raw) {
|
|
|
1380
1443
|
},
|
|
1381
1444
|
destroy() {
|
|
1382
1445
|
root.textContent = "";
|
|
1383
|
-
|
|
1446
|
+
unbindEvents();
|
|
1384
1447
|
chartTooltip().hidden = true;
|
|
1385
1448
|
}
|
|
1386
1449
|
};
|
|
1387
1450
|
}
|
|
1451
|
+
var GRAPH_W = 360;
|
|
1452
|
+
var GRAPH_H = 260;
|
|
1453
|
+
var GRAPH_PAD = 28;
|
|
1454
|
+
var GRAPH_LW = GRAPH_W - GRAPH_PAD * 2;
|
|
1455
|
+
var GRAPH_LH = GRAPH_H - GRAPH_PAD * 2;
|
|
1388
1456
|
|
|
1389
1457
|
// src/behaviors/ai-tools.ts
|
|
1390
1458
|
var toolRegistry = /* @__PURE__ */ new Map();
|
|
1459
|
+
var LIVE_AREAS = /* @__PURE__ */ new Set();
|
|
1391
1460
|
function registerAiTool(def) {
|
|
1392
1461
|
if (def && typeof def.name === "string" && def.name && typeof def.run === "function") {
|
|
1393
1462
|
toolRegistry.set(def.name, def);
|
|
@@ -1395,6 +1464,13 @@ function registerAiTool(def) {
|
|
|
1395
1464
|
}
|
|
1396
1465
|
function unregisterAiTool(name) {
|
|
1397
1466
|
toolRegistry.delete(name);
|
|
1467
|
+
for (const entry of LIVE_AREAS) {
|
|
1468
|
+
if (!entry.el.isConnected) {
|
|
1469
|
+
LIVE_AREAS.delete(entry);
|
|
1470
|
+
continue;
|
|
1471
|
+
}
|
|
1472
|
+
entry.purge(name);
|
|
1473
|
+
}
|
|
1398
1474
|
}
|
|
1399
1475
|
function getAiTool(name) {
|
|
1400
1476
|
return toolRegistry.get(name);
|
|
@@ -1493,7 +1569,7 @@ function isBrowserTools() {
|
|
|
1493
1569
|
}
|
|
1494
1570
|
function emitArea(area, name, detail) {
|
|
1495
1571
|
try {
|
|
1496
|
-
area
|
|
1572
|
+
emitIcen(area, name, detail);
|
|
1497
1573
|
} catch {
|
|
1498
1574
|
}
|
|
1499
1575
|
}
|
|
@@ -1508,7 +1584,6 @@ function createAiToolArea(el, opts = {}) {
|
|
|
1508
1584
|
area.classList.add("ai-tools-mount");
|
|
1509
1585
|
let allow = opts.tools;
|
|
1510
1586
|
let max = opts.max;
|
|
1511
|
-
let seq = 0;
|
|
1512
1587
|
const records = [];
|
|
1513
1588
|
const listeners = [];
|
|
1514
1589
|
const notify = () => {
|
|
@@ -1524,13 +1599,25 @@ function createAiToolArea(el, opts = {}) {
|
|
|
1524
1599
|
emitArea(area, "icen:ai-tool-evict", { name: oldest.def.name, el: oldest.el });
|
|
1525
1600
|
}
|
|
1526
1601
|
};
|
|
1602
|
+
const purgeTool = (toolName) => {
|
|
1603
|
+
let removed = false;
|
|
1604
|
+
for (let i = records.length - 1; i >= 0; i--) {
|
|
1605
|
+
const r = records[i];
|
|
1606
|
+
if (r.def.name !== toolName) continue;
|
|
1607
|
+
r.el?.remove();
|
|
1608
|
+
records.splice(i, 1);
|
|
1609
|
+
removed = true;
|
|
1610
|
+
}
|
|
1611
|
+
if (removed) notify();
|
|
1612
|
+
};
|
|
1613
|
+
LIVE_AREAS.add({ el: area, purge: purgeTool });
|
|
1527
1614
|
const areaApi = {
|
|
1528
1615
|
el: area,
|
|
1529
1616
|
get count() {
|
|
1530
1617
|
return records.filter((r) => r.el).length;
|
|
1531
1618
|
},
|
|
1532
1619
|
can(name) {
|
|
1533
|
-
return toolAllowed(name, allow) &&
|
|
1620
|
+
return toolAllowed(name, allow) && getAiTool(name) != null;
|
|
1534
1621
|
},
|
|
1535
1622
|
setTools(tools) {
|
|
1536
1623
|
allow = tools;
|
|
@@ -1552,9 +1639,24 @@ function createAiToolArea(el, opts = {}) {
|
|
|
1552
1639
|
};
|
|
1553
1640
|
},
|
|
1554
1641
|
call(name, input) {
|
|
1555
|
-
const def =
|
|
1642
|
+
const def = getAiTool(name);
|
|
1556
1643
|
if (!def || !toolAllowed(name, allow)) return null;
|
|
1557
|
-
|
|
1644
|
+
if (def.present === "data") {
|
|
1645
|
+
const record2 = { def, ts: Date.now(), input, result: void 0 };
|
|
1646
|
+
emitArea(area, "icen:ai-tool-call", { name, input, el: area });
|
|
1647
|
+
let ok2 = true;
|
|
1648
|
+
let error2;
|
|
1649
|
+
try {
|
|
1650
|
+
record2.result = def.run(input, document.createElement("div"));
|
|
1651
|
+
} catch (err) {
|
|
1652
|
+
ok2 = false;
|
|
1653
|
+
error2 = err instanceof Error ? err.message : String(err);
|
|
1654
|
+
}
|
|
1655
|
+
records.push(record2);
|
|
1656
|
+
emitArea(area, "icen:ai-tool-result", { name, ok: ok2, el: area, error: error2, count: records.length });
|
|
1657
|
+
notify();
|
|
1658
|
+
return record2;
|
|
1659
|
+
}
|
|
1558
1660
|
const item = document.createElement("div");
|
|
1559
1661
|
item.className = "ai-tools-item";
|
|
1560
1662
|
const head = document.createElement("button");
|
|
@@ -1574,6 +1676,7 @@ function createAiToolArea(el, opts = {}) {
|
|
|
1574
1676
|
const remove = document.createElement("span");
|
|
1575
1677
|
remove.className = "ai-tools-item-remove";
|
|
1576
1678
|
remove.setAttribute("role", "button");
|
|
1679
|
+
remove.tabIndex = 0;
|
|
1577
1680
|
remove.setAttribute("aria-label", `\u79FB\u9664 ${def.name} \u6302\u8F7D`);
|
|
1578
1681
|
remove.textContent = "\xD7";
|
|
1579
1682
|
const mount = document.createElement("div");
|
|
@@ -1589,6 +1692,13 @@ function createAiToolArea(el, opts = {}) {
|
|
|
1589
1692
|
mount.hidden = open;
|
|
1590
1693
|
item.classList.toggle("is-collapsed", open);
|
|
1591
1694
|
});
|
|
1695
|
+
head.addEventListener("keydown", (e) => {
|
|
1696
|
+
if (e.key !== "Enter" && e.key !== " ") return;
|
|
1697
|
+
if (!(e.target instanceof Element) || !e.target.closest(".ai-tools-item-remove")) return;
|
|
1698
|
+
e.preventDefault();
|
|
1699
|
+
e.stopPropagation();
|
|
1700
|
+
remove.click();
|
|
1701
|
+
});
|
|
1592
1702
|
const record = { def, el: item, ts: Date.now(), input, result: void 0 };
|
|
1593
1703
|
remove.addEventListener("click", (e) => {
|
|
1594
1704
|
e.stopPropagation();
|
|
@@ -1602,7 +1712,6 @@ function createAiToolArea(el, opts = {}) {
|
|
|
1602
1712
|
let error;
|
|
1603
1713
|
try {
|
|
1604
1714
|
record.result = def.run(input, mount);
|
|
1605
|
-
record.handle = record.result;
|
|
1606
1715
|
} catch (err) {
|
|
1607
1716
|
ok = false;
|
|
1608
1717
|
error = err instanceof Error ? err.message : String(err);
|