@pixerate/editor 0.171.0 → 0.174.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/README.md +4 -0
- package/dist/agent/index.cjs +28 -28
- package/dist/agent/index.js +3 -3
- package/dist/animation-editor/index.cjs +9 -9
- package/dist/animation-editor/index.js +1 -1
- package/dist/canvas/index.cjs +10 -10
- package/dist/canvas/index.js +2 -2
- package/dist/chart/index.cjs +156 -68
- package/dist/chart/index.d.cts +1 -0
- package/dist/chart/index.d.ts +1 -0
- package/dist/chart/index.js +91 -3
- package/dist/chart/layout-shared.d.cts +2 -1
- package/dist/chart/layout-shared.d.ts +2 -1
- package/dist/chart/layout.d.cts +5 -0
- package/dist/chart/layout.d.ts +5 -0
- package/dist/chart/tooltip.d.cts +53 -0
- package/dist/chart/tooltip.d.ts +53 -0
- package/dist/chart/types.d.cts +14 -0
- package/dist/chart/types.d.ts +14 -0
- package/dist/{chunk-CYKWYP6D.cjs → chunk-6DUEV36F.cjs} +14 -14
- package/dist/{chunk-7KWJEYIK.js → chunk-6G2NLMVK.js} +36 -4
- package/dist/{chunk-RUZQMYXH.cjs → chunk-A3A5TI6R.cjs} +43 -11
- package/dist/{chunk-SBAPXFPB.cjs → chunk-FHNGNL3V.cjs} +44 -44
- package/dist/{chunk-2F4GAZL5.cjs → chunk-GXEC4FS6.cjs} +16 -16
- package/dist/{chunk-T62BRXMS.js → chunk-IMHRN4ZZ.js} +1 -1
- package/dist/{chunk-N3ACT6VJ.js → chunk-KPWG3PSW.js} +1 -1
- package/dist/chunk-NZ3CTY7K.js +260 -0
- package/dist/{chunk-M52UCHVZ.js → chunk-OJQVKJYD.js} +12 -1
- package/dist/{chunk-YN4AYYBJ.js → chunk-OZSA5YEJ.js} +1 -1
- package/dist/{chunk-ZUPP4NKM.js → chunk-R2VHVQVY.js} +2 -2
- package/dist/{chunk-GD34ICEX.cjs → chunk-RLOIEDA3.cjs} +10 -10
- package/dist/{chunk-WOIXNDHV.cjs → chunk-TUXKLM5W.cjs} +13 -2
- package/dist/chunk-VCL7CB56.cjs +260 -0
- package/dist/{chunk-HCC2LBCW.js → chunk-WMVEYSOA.js} +1 -1
- package/dist/{chunk-3OSXHLIT.cjs → chunk-Z64NRDEH.cjs} +7 -7
- package/dist/commands/format.d.cts +43 -0
- package/dist/commands/format.d.ts +43 -0
- package/dist/commands/index.cjs +7 -1
- package/dist/commands/index.d.cts +1 -0
- package/dist/commands/index.d.ts +1 -0
- package/dist/commands/index.js +6 -0
- package/dist/effects/helpers.d.cts +11 -2
- package/dist/effects/helpers.d.ts +11 -2
- package/dist/effects/index.cjs +40 -34
- package/dist/effects/index.js +8 -2
- package/dist/effects/types.d.cts +2 -0
- package/dist/effects/types.d.ts +2 -0
- package/dist/extensions/index.cjs +5 -5
- package/dist/extensions/index.js +4 -4
- package/dist/image-editor/index.cjs +13 -13
- package/dist/image-editor/index.js +1 -1
- package/dist/index.cjs +16 -10
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +10 -4
- package/dist/paint-editor/index.cjs +7 -7
- package/dist/paint-editor/index.js +2 -2
- package/dist/vector/index.cjs +6 -6
- package/dist/vector/index.js +1 -1
- package/dist/video-editor/index.cjs +309 -309
- package/dist/video-editor/index.js +3 -3
- package/package.json +114 -11
- package/skills/pixerate-chart/SKILL.md +1 -0
package/dist/chart/index.d.cts
CHANGED
|
@@ -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';
|
package/dist/chart/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/chart/index.js
CHANGED
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
monochromeChartPalette,
|
|
48
48
|
sparklineToSvg,
|
|
49
49
|
summarizeChart
|
|
50
|
-
} from "../chunk-
|
|
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-
|
|
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'>;
|
package/dist/chart/layout.d.cts
CHANGED
|
@@ -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;
|
package/dist/chart/layout.d.ts
CHANGED
|
@@ -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;
|
package/dist/chart/types.d.cts
CHANGED
|
@@ -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;
|
package/dist/chart/types.d.ts
CHANGED
|
@@ -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;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
var
|
|
6
|
+
var _chunkA3A5TI6Rcjs = require('./chunk-A3A5TI6R.cjs');
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
var _chunkFJ2MSI43cjs = require('./chunk-FJ2MSI43.cjs');
|
|
@@ -26,7 +26,7 @@ function documentErrors(doc, description) {
|
|
|
26
26
|
const isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
|
|
27
27
|
if (doc.values !== void 0) {
|
|
28
28
|
if (!isRecord(doc.values)) errors.push({ path: "/values", message: "must be an object of control values" });
|
|
29
|
-
else errors.push(...
|
|
29
|
+
else errors.push(..._chunkA3A5TI6Rcjs.effectValueErrors.call(void 0, description, doc.values).map((e) => ({ ...e, path: `/values${e.path}` })));
|
|
30
30
|
}
|
|
31
31
|
if (doc.tracks !== void 0) {
|
|
32
32
|
if (!isRecord(doc.tracks)) errors.push({ path: "/tracks", message: "must be an object of keyframe lists" });
|
|
@@ -39,7 +39,7 @@ function documentErrors(doc, description) {
|
|
|
39
39
|
kfs.forEach((kf, i) => {
|
|
40
40
|
if (!isRecord(kf)) return errors.push({ path: `${at}/${i}`, message: "must be a keyframe object" });
|
|
41
41
|
if (typeof kf.time !== "number" || !Number.isFinite(kf.time) || kf.time < 0) errors.push({ path: `${at}/${i}/time`, message: "must be a finite number of ms >= 0" });
|
|
42
|
-
errors.push(...
|
|
42
|
+
errors.push(..._chunkA3A5TI6Rcjs.effectValueErrors.call(void 0, description, { [control]: kf.value }).map((e) => ({ ...e, path: `${at}/${i}/value` })));
|
|
43
43
|
if (kf.easing !== void 0 && !_chunkDWJJI6RTcjs.isAnimationEasing.call(void 0, kf.easing)) errors.push({ path: `${at}/${i}/easing`, message: "is not a valid easing" });
|
|
44
44
|
});
|
|
45
45
|
}
|
|
@@ -49,7 +49,7 @@ function documentErrors(doc, description) {
|
|
|
49
49
|
else
|
|
50
50
|
doc.presets.forEach((p, i) => {
|
|
51
51
|
if (!isRecord(p) || typeof p.name !== "string" || !p.name) return errors.push({ path: `/presets/${i}`, message: "must have a name" });
|
|
52
|
-
errors.push(...
|
|
52
|
+
errors.push(..._chunkA3A5TI6Rcjs.effectValueErrors.call(void 0, description, p.values).map((e) => ({ ...e, path: `/presets/${i}/values${e.path}` })));
|
|
53
53
|
});
|
|
54
54
|
}
|
|
55
55
|
if (doc.duration !== void 0 && (typeof doc.duration !== "number" || !Number.isFinite(doc.duration) || !(doc.duration > 0))) {
|
|
@@ -170,7 +170,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
170
170
|
if (isDocumentSource(source)) {
|
|
171
171
|
const doc = source.document;
|
|
172
172
|
if (doc.version !== 1 || !doc.effectId) {
|
|
173
|
-
throw new (0,
|
|
173
|
+
throw new (0, _chunkA3A5TI6Rcjs.EffectUnsupportedError)("Invalid effect authoring document: version 1 required");
|
|
174
174
|
}
|
|
175
175
|
effectId = doc.effectId;
|
|
176
176
|
initialDoc = doc;
|
|
@@ -178,15 +178,15 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
178
178
|
effectId = source.effectId;
|
|
179
179
|
requestedPreset = source.preset;
|
|
180
180
|
} else {
|
|
181
|
-
throw new (0,
|
|
181
|
+
throw new (0, _chunkA3A5TI6Rcjs.EffectUnsupportedError)("Invalid effect authoring source");
|
|
182
182
|
}
|
|
183
183
|
const description = await provider.describe(effectId);
|
|
184
184
|
if (initialDoc) {
|
|
185
185
|
const problems = documentErrors(initialDoc, description);
|
|
186
|
-
if (problems.length) throw new (0,
|
|
186
|
+
if (problems.length) throw new (0, _chunkA3A5TI6Rcjs.EffectControlError)(effectId, problems);
|
|
187
187
|
}
|
|
188
188
|
if (description.kind === "motion") {
|
|
189
|
-
throw new (0,
|
|
189
|
+
throw new (0, _chunkA3A5TI6Rcjs.EffectUnsupportedError)("Motion effects cannot be authored as pixel effects", effectId);
|
|
190
190
|
}
|
|
191
191
|
let disposed = false;
|
|
192
192
|
let playing = false;
|
|
@@ -207,7 +207,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
207
207
|
const found = description.presets.find((p) => p.name === requestedPreset);
|
|
208
208
|
if (!found) {
|
|
209
209
|
const validNames = description.presets.map((p) => `"${p.name}"`).join(", ");
|
|
210
|
-
throw new (0,
|
|
210
|
+
throw new (0, _chunkA3A5TI6Rcjs.EffectUnsupportedError)(
|
|
211
211
|
`Unknown preset "${requestedPreset}" for effect "${effectId}". Valid presets are: ${validNames || "none"}`,
|
|
212
212
|
effectId
|
|
213
213
|
);
|
|
@@ -321,7 +321,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
321
321
|
if (!validNames.includes(name)) {
|
|
322
322
|
return { ok: false, message: `there is no control "${name}"; the controls are ${validNames.join(", ")}` };
|
|
323
323
|
}
|
|
324
|
-
const errors =
|
|
324
|
+
const errors = _chunkA3A5TI6Rcjs.effectValueErrors.call(void 0, description, { [name]: value });
|
|
325
325
|
if (errors.length > 0) {
|
|
326
326
|
return { ok: false, message: valueErrorMessage(errors) };
|
|
327
327
|
}
|
|
@@ -391,7 +391,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
391
391
|
if (!validNames.includes(k)) {
|
|
392
392
|
return { ok: false, message: `there is no control "${k}"; the controls are ${validNames.join(", ")}` };
|
|
393
393
|
}
|
|
394
|
-
const errors =
|
|
394
|
+
const errors = _chunkA3A5TI6Rcjs.effectValueErrors.call(void 0, description, { [k]: v });
|
|
395
395
|
if (errors.length > 0) {
|
|
396
396
|
return { ok: false, message: valueErrorMessage(errors) };
|
|
397
397
|
}
|
|
@@ -528,7 +528,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
528
528
|
return { ok: false, message: `invalid easing: ${String(easing)}` };
|
|
529
529
|
}
|
|
530
530
|
const targetValue = value !== void 0 ? value : valuesAtTime(time, baseValues, tracks)[name];
|
|
531
|
-
const errors =
|
|
531
|
+
const errors = _chunkA3A5TI6Rcjs.effectValueErrors.call(void 0, description, { [name]: targetValue });
|
|
532
532
|
if (errors.length > 0) {
|
|
533
533
|
return { ok: false, message: valueErrorMessage(errors) };
|
|
534
534
|
}
|
|
@@ -705,7 +705,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
705
705
|
},
|
|
706
706
|
async createPreview(opts) {
|
|
707
707
|
if (disposed) {
|
|
708
|
-
throw new (0,
|
|
708
|
+
throw new (0, _chunkA3A5TI6Rcjs.EffectDisposedError)(effectId);
|
|
709
709
|
}
|
|
710
710
|
let renderer;
|
|
711
711
|
try {
|
|
@@ -730,7 +730,7 @@ async function createEffectAuthoringSession(source, options) {
|
|
|
730
730
|
height: renderer.height,
|
|
731
731
|
depth: renderer.depth,
|
|
732
732
|
render: async (req) => {
|
|
733
|
-
if (disposed) throw new (0,
|
|
733
|
+
if (disposed) throw new (0, _chunkA3A5TI6Rcjs.EffectDisposedError)(effectId);
|
|
734
734
|
const effectiveValues = _nullishCoalesce(req.values, () => ( valuesAtTime(req.time, baseValues, tracks)));
|
|
735
735
|
const pixels = await renderer.render({
|
|
736
736
|
...req,
|
|
@@ -68,7 +68,20 @@ function resolveEffectValues(description, values) {
|
|
|
68
68
|
if (errors.length) throw new EffectControlError(description.id, errors);
|
|
69
69
|
return { ...description.defaults, ...values ?? {} };
|
|
70
70
|
}
|
|
71
|
-
|
|
71
|
+
function normaliseEffectTag(tag) {
|
|
72
|
+
return tag.trim().toLowerCase().replace(/[\s_-]+/g, "-");
|
|
73
|
+
}
|
|
74
|
+
function hasEffectTags(e, tags) {
|
|
75
|
+
if (!tags?.length) return true;
|
|
76
|
+
const own = new Set((e.tags ?? []).map(normaliseEffectTag));
|
|
77
|
+
return tags.every((t) => own.has(normaliseEffectTag(t)));
|
|
78
|
+
}
|
|
79
|
+
function countEffectTags(effects) {
|
|
80
|
+
const counts = /* @__PURE__ */ new Map();
|
|
81
|
+
for (const e of effects) for (const tag of new Set((e.tags ?? []).map(normaliseEffectTag))) if (tag) counts.set(tag, (counts.get(tag) ?? 0) + 1);
|
|
82
|
+
return [...counts].map(([tag, count]) => ({ tag, count })).sort((a, b) => b.count - a.count || a.tag.localeCompare(b.tag));
|
|
83
|
+
}
|
|
84
|
+
var matchesFilters = (e, o) => (!o.kind || e.kind === o.kind) && (!o.category || e.category === o.category) && (!o.use || e.uses.includes(o.use)) && hasEffectTags(e, o.tags);
|
|
72
85
|
function searchEffectSummaries(catalog, query = "", options = {}) {
|
|
73
86
|
const words = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
74
87
|
const scored = [];
|
|
@@ -174,6 +187,12 @@ var object = (properties, required = []) => ({
|
|
|
174
187
|
additionalProperties: false
|
|
175
188
|
});
|
|
176
189
|
var ENUM_LIMIT = 60;
|
|
190
|
+
var TAG_LIMIT = 40;
|
|
191
|
+
function nearestTags(tag, known) {
|
|
192
|
+
const t = normaliseEffectTag(tag);
|
|
193
|
+
const stem = t.slice(0, 4);
|
|
194
|
+
return known.filter((k) => k.tag.includes(t) || t.includes(k.tag) || stem.length === 4 && k.tag.startsWith(stem)).slice(0, 5).map((k) => k.tag);
|
|
195
|
+
}
|
|
177
196
|
var summaryOut = (e) => ({
|
|
178
197
|
id: e.id,
|
|
179
198
|
name: e.name,
|
|
@@ -181,6 +200,7 @@ var summaryOut = (e) => ({
|
|
|
181
200
|
category: e.category,
|
|
182
201
|
uses: e.uses,
|
|
183
202
|
...e.description ? { description: e.description } : {},
|
|
203
|
+
...e.tags?.length ? { tags: e.tags } : {},
|
|
184
204
|
transparent: e.transparent,
|
|
185
205
|
loop: e.loop,
|
|
186
206
|
...e.duration !== void 0 ? { duration: e.duration } : {}
|
|
@@ -241,21 +261,30 @@ function createEffectToolProvider(host) {
|
|
|
241
261
|
{
|
|
242
262
|
name: "search_effects",
|
|
243
263
|
title: "Search effects",
|
|
244
|
-
description: `Searches the effects provider's catalog. Every word of query must appear in an effect's id, name, category, tags or description; filter by kind (shader, vector, motion), category
|
|
264
|
+
description: `Searches the effects provider's catalog. Every word of query must appear in an effect's id, name, category, tags or description; filter by kind (shader, vector, motion), category, use (${EFFECT_USES.join(", ")}) or tags (effects carrying every one). For this editor, apply_effect takes uses ${host.applyUses.join(" or ")}. Returns { provider, effects: [{ id, name, kind, category, uses, description, tags, transparent, loop, duration (ms) }], total, tags: [{ tag, count }] }: tags lists the tags of every match, the most used first, so a broad search (or none) shows the tags to narrow by. A tag the catalog does not have comes back in unknownTags with the nearest ones. With no provider registered it returns available: false and the reason. Call describe_effect next for an effect's controls.`,
|
|
245
265
|
inputSchema: object({
|
|
246
266
|
query: { type: "string", description: "Words to match (default: every effect)." },
|
|
247
267
|
kind: { type: "string", enum: [...EFFECT_KINDS], description: "shader (pixels), vector (animated SVG) or motion (animates vector art)." },
|
|
248
268
|
category: { type: "string", minLength: 1, description: `The provider's category, e.g. "gradient", "light", "transition".` },
|
|
249
269
|
use: { type: "string", enum: [...EFFECT_USES], description: "What the effect is for: filter changes a picture, transition blends two, generator draws a background, overlay draws over content, title draws a title, motion animates vector art, interactive reacts to input." },
|
|
270
|
+
tags: { type: "array", items: { type: "string", minLength: 1 }, description: "Only effects carrying every one of these tags, as search_effects lists them (any case; spaces count as hyphens)." },
|
|
250
271
|
limit: { type: "integer", minimum: 1, maximum: 200, description: "Most effects to return (default 20)." }
|
|
251
272
|
}),
|
|
252
273
|
annotations: { title: "Search effects", ...ANNOTATIONS.read },
|
|
253
274
|
handler: async (args) => {
|
|
254
275
|
const p = host.provider();
|
|
255
276
|
if (!p) return { available: false, reason: NO_PROVIDER, effects: [], total: 0 };
|
|
256
|
-
const
|
|
277
|
+
const tags = args.tags;
|
|
278
|
+
const found = (await explain(async () => p.search(args.query ?? "", { kind: args.kind, category: args.category, use: args.use, tags }))).filter((e) => hasEffectTags(e, tags));
|
|
257
279
|
const limit = args.limit ?? 20;
|
|
258
|
-
|
|
280
|
+
const out = { available: true, provider: { id: p.id, name: p.name, capabilities: p.capabilities }, effects: found.slice(0, limit).map(summaryOut), total: found.length, tags: countEffectTags(found).slice(0, TAG_LIMIT) };
|
|
281
|
+
if (tags?.length && !found.length) {
|
|
282
|
+
const known = countEffectTags(await explain(async () => p.list()));
|
|
283
|
+
const have = new Set(known.map((k) => k.tag));
|
|
284
|
+
const unknown = tags.filter((t) => !have.has(normaliseEffectTag(t)));
|
|
285
|
+
if (unknown.length) out.unknownTags = unknown.map((tag) => ({ tag, nearest: nearestTags(tag, known) }));
|
|
286
|
+
}
|
|
287
|
+
return out;
|
|
259
288
|
}
|
|
260
289
|
},
|
|
261
290
|
{
|
|
@@ -374,6 +403,9 @@ export {
|
|
|
374
403
|
isEffectError,
|
|
375
404
|
effectValueErrors,
|
|
376
405
|
resolveEffectValues,
|
|
406
|
+
normaliseEffectTag,
|
|
407
|
+
hasEffectTags,
|
|
408
|
+
countEffectTags,
|
|
377
409
|
searchEffectSummaries,
|
|
378
410
|
createEffectPixels,
|
|
379
411
|
isEffectPixels,
|