@pixerate/editor 0.170.0 → 0.172.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/README.md +4 -0
  2. package/dist/agent/index.cjs +28 -28
  3. package/dist/agent/index.js +3 -3
  4. package/dist/canvas/index.cjs +3 -3
  5. package/dist/canvas/index.js +1 -1
  6. package/dist/chart/index.cjs +156 -68
  7. package/dist/chart/index.d.cts +1 -0
  8. package/dist/chart/index.d.ts +1 -0
  9. package/dist/chart/index.js +91 -3
  10. package/dist/chart/layout-shared.d.cts +2 -1
  11. package/dist/chart/layout-shared.d.ts +2 -1
  12. package/dist/chart/layout.d.cts +5 -0
  13. package/dist/chart/layout.d.ts +5 -0
  14. package/dist/chart/tooltip.d.cts +53 -0
  15. package/dist/chart/tooltip.d.ts +53 -0
  16. package/dist/chart/types.d.cts +14 -0
  17. package/dist/chart/types.d.ts +14 -0
  18. package/dist/{chunk-SBAPXFPB.cjs → chunk-FHNGNL3V.cjs} +44 -44
  19. package/dist/{chunk-2F4GAZL5.cjs → chunk-GXEC4FS6.cjs} +16 -16
  20. package/dist/{chunk-T62BRXMS.js → chunk-IMHRN4ZZ.js} +1 -1
  21. package/dist/{chunk-N3ACT6VJ.js → chunk-KPWG3PSW.js} +1 -1
  22. package/dist/{chunk-M52UCHVZ.js → chunk-OJQVKJYD.js} +12 -1
  23. package/dist/{chunk-ZUPP4NKM.js → chunk-R2VHVQVY.js} +2 -2
  24. package/dist/{chunk-GD34ICEX.cjs → chunk-RLOIEDA3.cjs} +10 -10
  25. package/dist/{chunk-WOIXNDHV.cjs → chunk-TUXKLM5W.cjs} +13 -2
  26. package/dist/extensions/index.cjs +5 -5
  27. package/dist/extensions/index.js +4 -4
  28. package/dist/image-editor/index.cjs +18 -5
  29. package/dist/image-editor/index.js +17 -4
  30. package/dist/image-editor/layers/panel.d.cts +19 -3
  31. package/dist/image-editor/layers/panel.d.ts +19 -3
  32. package/dist/index.cjs +10 -10
  33. package/dist/index.js +4 -4
  34. package/package.json +112 -12
  35. package/skills/pixerate-chart/SKILL.md +1 -0
@@ -22,6 +22,7 @@ export * from './recommend.cjs';
22
22
  export * from './controller.cjs';
23
23
  export * from './commands.cjs';
24
24
  export * from './interaction.cjs';
25
+ export * from './tooltip.cjs';
25
26
  export * from './mermaid.cjs';
26
27
  export * from './ooxml.cjs';
27
28
  export * from './tools.cjs';
@@ -22,6 +22,7 @@ export * from './recommend.js';
22
22
  export * from './controller.js';
23
23
  export * from './commands.js';
24
24
  export * from './interaction.js';
25
+ export * from './tooltip.js';
25
26
  export * from './mermaid.js';
26
27
  export * from './ooxml.js';
27
28
  export * from './tools.js';
@@ -47,7 +47,7 @@ import {
47
47
  monochromeChartPalette,
48
48
  sparklineToSvg,
49
49
  summarizeChart
50
- } from "../chunk-N3ACT6VJ.js";
50
+ } from "../chunk-KPWG3PSW.js";
51
51
  import "../chunk-KGRZMHZB.js";
52
52
  import {
53
53
  CHART_DATE_STEPS,
@@ -55,6 +55,7 @@ import {
55
55
  MAX_CHART_LINE_POINTS,
56
56
  MAX_CHART_MARKERS,
57
57
  chartBoxStats,
58
+ chartCategoryAtPoint,
58
59
  chartDateLabels,
59
60
  chartDateToSerial,
60
61
  chartHistogram,
@@ -98,7 +99,7 @@ import {
98
99
  suggestChartReferences,
99
100
  summarizeChartSeries,
100
101
  trendNumberText
101
- } from "../chunk-M52UCHVZ.js";
102
+ } from "../chunk-OJQVKJYD.js";
102
103
  import {
103
104
  CHART_AXIS_KINDS,
104
105
  CHART_AXIS_SCALES,
@@ -1336,6 +1337,71 @@ function chartHitTest(scene, point, measure = (t, s, w = 400) => t.length * s *
1336
1337
  return point.x >= 0 && point.y >= 0 && point.x <= scene.width && point.y <= scene.height ? { kind: "chart" } : null;
1337
1338
  }
1338
1339
 
1340
+ // src/chart/tooltip.ts
1341
+ var VALUE_FAMILIES = /* @__PURE__ */ new Set(["cartesian", "xy", "pie", "radar", "waterfall", "funnel", "parliament", "slope"]);
1342
+ var SLICE_FAMILIES = /* @__PURE__ */ new Set(["pie", "funnel", "parliament"]);
1343
+ function valueFormat(spec, series) {
1344
+ return series.format ?? (series.axis === "secondary" ? spec.yAxis2.format : spec.yAxis.format);
1345
+ }
1346
+ var percentText = (share) => formatChartNumber(share, { style: "percent", decimals: share < 0.1 ? 1 : 0 });
1347
+ function item(spec, seriesIndex, index, share) {
1348
+ const family = chartFamily(spec);
1349
+ const series = spec.series[seriesIndex];
1350
+ const value = series.values[index] ?? null;
1351
+ const slice = SLICE_FAMILIES.has(family);
1352
+ let text = formatChartNumber(value, valueFormat(spec, series));
1353
+ if (family === "xy") {
1354
+ const x = series.x?.[index];
1355
+ if (x !== null && x !== void 0) text = `${formatChartNumber(x, spec.xAxis.format)}, ${text}`;
1356
+ }
1357
+ if (share !== void 0) text = `${text} (${percentText(share)})`;
1358
+ return {
1359
+ seriesId: series.id,
1360
+ name: slice ? spec.categories[index] ?? series.name : series.name,
1361
+ color: slice ? series.pointColors?.[index] ?? chartSliceColor(spec, index) : chartPointColor(spec, seriesIndex, index),
1362
+ value,
1363
+ ...share !== void 0 ? { share } : {},
1364
+ text
1365
+ };
1366
+ }
1367
+ function chartTooltipFor(spec, index, seriesId, anchor = { x: 0, y: 0 }) {
1368
+ const family = chartFamily(spec);
1369
+ if (!VALUE_FAMILIES.has(family)) return null;
1370
+ if (!Number.isInteger(index) || index < 0 || index >= Math.max(spec.categories.length, ...spec.series.map((s) => s.values.length))) return null;
1371
+ const visible = spec.series.map((s, i) => ({ s, i })).filter(({ s }) => !s.hidden && (!seriesId || s.id === seriesId));
1372
+ const present = visible.filter(({ s }) => s.values[index] !== null && s.values[index] !== void 0 && Number.isFinite(s.values[index]));
1373
+ if (!present.length) return null;
1374
+ const shareOf = (s) => {
1375
+ const v = s.values[index];
1376
+ if (spec.stacking === "percent" && family === "cartesian") {
1377
+ const total = spec.series.filter((t) => !t.hidden && t.axis === s.axis).reduce((sum, t) => sum + Math.abs(t.values[index] ?? 0), 0);
1378
+ return total ? Math.abs(v) / total : void 0;
1379
+ }
1380
+ if (family === "pie") {
1381
+ const total = s.values.reduce((sum, t) => sum + (t !== null && t > 0 ? t : 0), 0);
1382
+ return total && v > 0 ? v / total : void 0;
1383
+ }
1384
+ return void 0;
1385
+ };
1386
+ const items = present.map(({ s, i }) => item(spec, i, index, shareOf(s)));
1387
+ const title = family === "xy" ? spec.categories[index] || present[0].s.name : spec.categories[index] ?? "";
1388
+ return { title, index, items, x: anchor.x, y: anchor.y };
1389
+ }
1390
+ function chartTooltipAt(spec, scene, point, options = {}) {
1391
+ if (options.mode === "category" && scene.categoryScale) {
1392
+ const index = chartCategoryAtPoint(scene, point);
1393
+ if (index !== void 0) return chartTooltipFor(spec, index, void 0, point);
1394
+ }
1395
+ const target = chartHitTest(scene, point, options.measureText ?? estimateChartTextWidth);
1396
+ if (target?.kind === "point") return chartTooltipFor(spec, target.index, target.seriesId, point);
1397
+ if (target?.kind === "category" && scene.categoryScale && options.mode === "category") return chartTooltipFor(spec, target.index, void 0, point);
1398
+ return null;
1399
+ }
1400
+ function sameChartTooltip(a, b) {
1401
+ if (!a || !b) return a === b;
1402
+ return a.index === b.index && a.items.length === b.items.length && a.items.every((it, i) => it.seriesId === b.items[i].seriesId && it.text === b.items[i].text);
1403
+ }
1404
+
1339
1405
  // src/chart/presence.ts
1340
1406
  var ChartPresence = class extends PresenceStore {
1341
1407
  };
@@ -1767,6 +1833,24 @@ function createChartToolProvider(controller, options = {}) {
1767
1833
  return { trendline: line, coefficients: fit.coefficients, r2: fit.r2, equation: fit.equation, points: fit.points };
1768
1834
  }
1769
1835
  };
1836
+ const getTooltip = {
1837
+ name: "get_chart_tooltip",
1838
+ title: "Read a chart tooltip",
1839
+ description: "Returns what a viewer sees when hovering a category (or one series in it): the category label and, for each visible series with a value there, its name, colour, value, share (percent-stacked and pie charts, 0\u20131) and the value as the chart writes it. Returns null for charts without one value per category (histogram, box, treemap, sunburst, sankey, \u2026) or when no visible series has a value there.",
1840
+ inputSchema: {
1841
+ type: "object",
1842
+ properties: { category: CATEGORY_REF, series: { ...SERIES_REF, description: `${SERIES_REF.description} Omit to include every visible series in the category.` } },
1843
+ required: ["category"],
1844
+ additionalProperties: false
1845
+ },
1846
+ annotations: { title: "Read a chart tooltip", ...READ },
1847
+ handler: ({ category, series }) => {
1848
+ const index = categoryIndex(category);
1849
+ const seriesId = series === void 0 ? void 0 : seriesById(series).id;
1850
+ const tooltip = chartTooltipFor(controller.toJSON(), index, seriesId);
1851
+ return tooltip && { title: tooltip.title, index: tooltip.index, items: tooltip.items };
1852
+ }
1853
+ };
1770
1854
  const raceRanking = {
1771
1855
  name: "get_chart_race_ranking",
1772
1856
  title: "Read a bar race ranking",
@@ -2476,7 +2560,7 @@ function createChartToolProvider(controller, options = {}) {
2476
2560
  return { redone, ...redone ? { label } : {} };
2477
2561
  }
2478
2562
  };
2479
- const reads = [getChart, renderChart, checkColors, recommend, getTrendline, raceRanking, mapRegions, listPresets, exportMermaid, exportOoxml];
2563
+ const reads = [getChart, renderChart, checkColors, recommend, getTrendline, getTooltip, raceRanking, mapRegions, listPresets, exportMermaid, exportOoxml];
2480
2564
  const writes = [
2481
2565
  updateChart,
2482
2566
  setData,
@@ -3246,6 +3330,7 @@ export {
3246
3330
  blendChartScenes,
3247
3331
  canDragChartPoint,
3248
3332
  chartBoxStats,
3333
+ chartCategoryAtPoint,
3249
3334
  chartClientToScene,
3250
3335
  chartColorDistance,
3251
3336
  chartColorInputValue,
@@ -3305,6 +3390,8 @@ export {
3305
3390
  chartToMermaid,
3306
3391
  chartToOoxml,
3307
3392
  chartToSvg,
3393
+ chartTooltipAt,
3394
+ chartTooltipFor,
3308
3395
  chartTreeColumnLabels,
3309
3396
  chartTreeDepth,
3310
3397
  chartTreeFromRows,
@@ -3391,6 +3478,7 @@ export {
3391
3478
  removeChartTreeRow,
3392
3479
  resolveChartSvgPaints,
3393
3480
  sameChartSelection,
3481
+ sameChartTooltip,
3394
3482
  setChartLinkField,
3395
3483
  setChartTreeRowName,
3396
3484
  setChartTreeRowValue,
@@ -1,5 +1,5 @@
1
1
  import { type NiceScale } from './scale.cjs';
2
- import type { ChartAxis, ChartMark, ChartMarker, ChartRect, ChartScene, ChartSelection, ChartSpec, ChartTheme, ChartValueScale, ChartWarning } from './types.cjs';
2
+ import type { ChartAxis, ChartCategoryScale, ChartMark, ChartMarker, ChartRect, ChartScene, ChartSelection, ChartSpec, ChartTheme, ChartValueScale, ChartWarning } from './types.cjs';
3
3
  export type ChartTextMeasure = (text: string, size: number, weight?: number) => number;
4
4
  export interface ChartLayoutOptions {
5
5
  width: number;
@@ -73,6 +73,7 @@ export interface FamilyResult {
73
73
  valueScale?: ChartValueScale;
74
74
  valueScale2?: ChartValueScale;
75
75
  xScale?: ChartValueScale;
76
+ categoryScale?: ChartCategoryScale;
76
77
  percent?: boolean;
77
78
  }
78
79
  export type ChartSceneBase = Omit<ChartScene, 'marks' | 'warnings' | 'theme' | 'width' | 'height'>;
@@ -1,5 +1,5 @@
1
1
  import { type NiceScale } from './scale.js';
2
- import type { ChartAxis, ChartMark, ChartMarker, ChartRect, ChartScene, ChartSelection, ChartSpec, ChartTheme, ChartValueScale, ChartWarning } from './types.js';
2
+ import type { ChartAxis, ChartCategoryScale, ChartMark, ChartMarker, ChartRect, ChartScene, ChartSelection, ChartSpec, ChartTheme, ChartValueScale, ChartWarning } from './types.js';
3
3
  export type ChartTextMeasure = (text: string, size: number, weight?: number) => number;
4
4
  export interface ChartLayoutOptions {
5
5
  width: number;
@@ -73,6 +73,7 @@ export interface FamilyResult {
73
73
  valueScale?: ChartValueScale;
74
74
  valueScale2?: ChartValueScale;
75
75
  xScale?: ChartValueScale;
76
+ categoryScale?: ChartCategoryScale;
76
77
  percent?: boolean;
77
78
  }
78
79
  export type ChartSceneBase = Omit<ChartScene, 'marks' | 'warnings' | 'theme' | 'width' | 'height'>;
@@ -18,6 +18,11 @@ export declare function chartValueAtPoint(scene: ChartScene, point: {
18
18
  x: number;
19
19
  y: number;
20
20
  }, secondary?: boolean): number | undefined;
21
+ /** The category index under a scene point inside the plot of a bar, line or area chart, or `undefined`. */
22
+ export declare function chartCategoryAtPoint(scene: ChartScene, point: {
23
+ x: number;
24
+ y: number;
25
+ }): number | undefined;
21
26
  /** The x value at a scene point on a scatter or bubble chart, or `undefined` without a numeric x axis. */
22
27
  export declare function chartXAtPoint(scene: ChartScene, point: {
23
28
  x: number;
@@ -18,6 +18,11 @@ export declare function chartValueAtPoint(scene: ChartScene, point: {
18
18
  x: number;
19
19
  y: number;
20
20
  }, secondary?: boolean): number | undefined;
21
+ /** The category index under a scene point inside the plot of a bar, line or area chart, or `undefined`. */
22
+ export declare function chartCategoryAtPoint(scene: ChartScene, point: {
23
+ x: number;
24
+ y: number;
25
+ }): number | undefined;
21
26
  /** The x value at a scene point on a scatter or bubble chart, or `undefined` without a numeric x axis. */
22
27
  export declare function chartXAtPoint(scene: ChartScene, point: {
23
28
  x: number;
@@ -0,0 +1,53 @@
1
+ import { type ChartTextMeasure } from './layout-shared.cjs';
2
+ import type { ChartScene, ChartSpec, ChartValue } from './types.cjs';
3
+ /** `point`: the bar, point or slice under the pointer. `category`: every series in the category under the pointer (bar, line and area charts; other charts fall back to `point`). */
4
+ export type ChartTooltipMode = 'point' | 'category';
5
+ export interface ChartTooltipItem {
6
+ seriesId: string;
7
+ /** The series name (the category label on pie, funnel and parliament charts). */
8
+ name: string;
9
+ color: string;
10
+ value: ChartValue;
11
+ /** Share of the category (percent-stacked charts) or of the series (pie and donut charts), 0–1. */
12
+ share?: number;
13
+ /** The value as the chart writes it: the series format, else its value axis format. */
14
+ text: string;
15
+ }
16
+ export interface ChartTooltip {
17
+ /** The category label (the series name on scatter and bubble charts with x values). */
18
+ title: string;
19
+ /** The category the items belong to. */
20
+ index: number;
21
+ items: ChartTooltipItem[];
22
+ /** Where to anchor the tooltip, in scene pixels (the pointer). */
23
+ x: number;
24
+ y: number;
25
+ }
26
+ export interface ChartTooltipOptions {
27
+ /** Default `point`. */
28
+ mode?: ChartTooltipMode;
29
+ /** Text width for hit-testing labels (default: an estimate). */
30
+ measureText?: ChartTextMeasure;
31
+ }
32
+ /**
33
+ * What a tooltip shows for one category: the category label and each visible series' value
34
+ * there, written as the chart writes it. `seriesId` narrows it to one series (one bar, point
35
+ * or slice). Returns `null` for charts whose marks are not one value per category (histogram,
36
+ * box, treemap and sunburst with a tree, sankey, …), for an index out of range, or when no
37
+ * visible series has a value there.
38
+ */
39
+ export declare function chartTooltipFor(spec: ChartSpec, index: number, seriesId?: string, anchor?: {
40
+ x: number;
41
+ y: number;
42
+ }): ChartTooltip | null;
43
+ /**
44
+ * The tooltip at a scene point (for hover), or `null` when the pointer is over nothing with a
45
+ * value. In `category` mode, anywhere in a category's band of a bar, line or area chart shows
46
+ * every series there; elsewhere both modes show the one bar, point or slice under the pointer.
47
+ */
48
+ export declare function chartTooltipAt(spec: ChartSpec, scene: ChartScene, point: {
49
+ x: number;
50
+ y: number;
51
+ }, options?: ChartTooltipOptions): ChartTooltip | null;
52
+ /** Whether two tooltips show the same thing (so a host can skip re-rendering while the pointer moves within a mark). */
53
+ export declare function sameChartTooltip(a: ChartTooltip | null, b: ChartTooltip | null): boolean;
@@ -0,0 +1,53 @@
1
+ import { type ChartTextMeasure } from './layout-shared.js';
2
+ import type { ChartScene, ChartSpec, ChartValue } from './types.js';
3
+ /** `point`: the bar, point or slice under the pointer. `category`: every series in the category under the pointer (bar, line and area charts; other charts fall back to `point`). */
4
+ export type ChartTooltipMode = 'point' | 'category';
5
+ export interface ChartTooltipItem {
6
+ seriesId: string;
7
+ /** The series name (the category label on pie, funnel and parliament charts). */
8
+ name: string;
9
+ color: string;
10
+ value: ChartValue;
11
+ /** Share of the category (percent-stacked charts) or of the series (pie and donut charts), 0–1. */
12
+ share?: number;
13
+ /** The value as the chart writes it: the series format, else its value axis format. */
14
+ text: string;
15
+ }
16
+ export interface ChartTooltip {
17
+ /** The category label (the series name on scatter and bubble charts with x values). */
18
+ title: string;
19
+ /** The category the items belong to. */
20
+ index: number;
21
+ items: ChartTooltipItem[];
22
+ /** Where to anchor the tooltip, in scene pixels (the pointer). */
23
+ x: number;
24
+ y: number;
25
+ }
26
+ export interface ChartTooltipOptions {
27
+ /** Default `point`. */
28
+ mode?: ChartTooltipMode;
29
+ /** Text width for hit-testing labels (default: an estimate). */
30
+ measureText?: ChartTextMeasure;
31
+ }
32
+ /**
33
+ * What a tooltip shows for one category: the category label and each visible series' value
34
+ * there, written as the chart writes it. `seriesId` narrows it to one series (one bar, point
35
+ * or slice). Returns `null` for charts whose marks are not one value per category (histogram,
36
+ * box, treemap and sunburst with a tree, sankey, …), for an index out of range, or when no
37
+ * visible series has a value there.
38
+ */
39
+ export declare function chartTooltipFor(spec: ChartSpec, index: number, seriesId?: string, anchor?: {
40
+ x: number;
41
+ y: number;
42
+ }): ChartTooltip | null;
43
+ /**
44
+ * The tooltip at a scene point (for hover), or `null` when the pointer is over nothing with a
45
+ * value. In `category` mode, anywhere in a category's band of a bar, line or area chart shows
46
+ * every series there; elsewhere both modes show the one bar, point or slice under the pointer.
47
+ */
48
+ export declare function chartTooltipAt(spec: ChartSpec, scene: ChartScene, point: {
49
+ x: number;
50
+ y: number;
51
+ }, options?: ChartTooltipOptions): ChartTooltip | null;
52
+ /** Whether two tooltips show the same thing (so a host can skip re-rendering while the pointer moves within a mark). */
53
+ export declare function sameChartTooltip(a: ChartTooltip | null, b: ChartTooltip | null): boolean;
@@ -440,6 +440,18 @@ export interface ChartValueScale {
440
440
  /** Set when the axis is logarithmic: values map by their logarithm in this base. */
441
441
  logBase?: number;
442
442
  }
443
+ /**
444
+ * The category bands of a bar, line or area chart: `count` equal bands of the category axis,
445
+ * from `start` for `length` pixels (the last category first when `reverse` is set).
446
+ */
447
+ export interface ChartCategoryScale {
448
+ /** `x` for vertical charts, `y` for horizontal bars. */
449
+ axis: 'x' | 'y';
450
+ start: number;
451
+ length: number;
452
+ count: number;
453
+ reverse: boolean;
454
+ }
443
455
  /** Something the layout had to leave out or shorten, as a sentence (for agents and screen readers). */
444
456
  export interface ChartWarning {
445
457
  code: 'labels-hidden' | 'labels-truncated' | 'legend-truncated' | 'points-decimated' | 'values-skipped' | 'data-labels-hidden' | 'type-fallback' | 'annotations-skipped' | 'highlight-unmatched';
@@ -459,6 +471,8 @@ export interface ChartScene {
459
471
  valueScale2?: ChartValueScale;
460
472
  /** The numeric x scale of a scatter or bubble chart with x values. */
461
473
  xScale?: ChartValueScale;
474
+ /** The category bands of a bar, line or area chart. */
475
+ categoryScale?: ChartCategoryScale;
462
476
  /** Percent-stacked: values are shown as shares, so dragging is disabled. */
463
477
  percent: boolean;
464
478
  theme: ChartTheme;
@@ -440,6 +440,18 @@ export interface ChartValueScale {
440
440
  /** Set when the axis is logarithmic: values map by their logarithm in this base. */
441
441
  logBase?: number;
442
442
  }
443
+ /**
444
+ * The category bands of a bar, line or area chart: `count` equal bands of the category axis,
445
+ * from `start` for `length` pixels (the last category first when `reverse` is set).
446
+ */
447
+ export interface ChartCategoryScale {
448
+ /** `x` for vertical charts, `y` for horizontal bars. */
449
+ axis: 'x' | 'y';
450
+ start: number;
451
+ length: number;
452
+ count: number;
453
+ reverse: boolean;
454
+ }
443
455
  /** Something the layout had to leave out or shorten, as a sentence (for agents and screen readers). */
444
456
  export interface ChartWarning {
445
457
  code: 'labels-hidden' | 'labels-truncated' | 'legend-truncated' | 'points-decimated' | 'values-skipped' | 'data-labels-hidden' | 'type-fallback' | 'annotations-skipped' | 'highlight-unmatched';
@@ -459,6 +471,8 @@ export interface ChartScene {
459
471
  valueScale2?: ChartValueScale;
460
472
  /** The numeric x scale of a scatter or bubble chart with x values. */
461
473
  xScale?: ChartValueScale;
474
+ /** The category bands of a bar, line or area chart. */
475
+ categoryScale?: ChartCategoryScale;
462
476
  /** Percent-stacked: values are shown as shares, so dragging is disabled. */
463
477
  percent: boolean;
464
478
  theme: ChartTheme;