@pixerate/editor 0.223.0 → 0.223.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,7 +20,7 @@ export interface SkillEvalCheck extends SkillEvalToolCall {
20
20
  }
21
21
  export interface SkillEval {
22
22
  prompt: string;
23
- fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture;
23
+ fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture | ChartEvalFixture;
24
24
  checks: SkillEvalCheck[];
25
25
  solution: SkillEvalToolCall[];
26
26
  }
@@ -67,6 +67,11 @@ export interface VideoEvalFixture {
67
67
  /** Tool calls that build the starting project (settings, assets, clips, markers), run in order. */
68
68
  setup?: SkillEvalToolCall[];
69
69
  }
70
+ export interface ChartEvalFixture {
71
+ editor: 'chart';
72
+ /** Tool calls that build the starting chart (`set_chart_data`, `update_chart`), run in order. */
73
+ setup?: SkillEvalToolCall[];
74
+ }
70
75
  /** One eval file of a skill. */
71
76
  export interface SkillEvalFile {
72
77
  skill: string;
@@ -20,7 +20,7 @@ export interface SkillEvalCheck extends SkillEvalToolCall {
20
20
  }
21
21
  export interface SkillEval {
22
22
  prompt: string;
23
- fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture;
23
+ fixture: SpreadsheetEvalFixture | ImageEvalFixture | VectorEvalFixture | PaintEvalFixture | VideoEvalFixture | ChartEvalFixture;
24
24
  checks: SkillEvalCheck[];
25
25
  solution: SkillEvalToolCall[];
26
26
  }
@@ -67,6 +67,11 @@ export interface VideoEvalFixture {
67
67
  /** Tool calls that build the starting project (settings, assets, clips, markers), run in order. */
68
68
  setup?: SkillEvalToolCall[];
69
69
  }
70
+ export interface ChartEvalFixture {
71
+ editor: 'chart';
72
+ /** Tool calls that build the starting chart (`set_chart_data`, `update_chart`), run in order. */
73
+ setup?: SkillEvalToolCall[];
74
+ }
70
75
  /** One eval file of a skill. */
71
76
  export interface SkillEvalFile {
72
77
  skill: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.223.0",
3
+ "version": "0.223.1",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
package/skills/README.md CHANGED
@@ -53,6 +53,8 @@ request like "clean up this export" finds it.
53
53
  | [`pixerate-video-social-cut`](pixerate-video-social-cut/SKILL.md) | Video (`pixerate-video-editor`) | Vertical or square reframes that fill the frame, the hook first, platform lengths, titles in the safe area. |
54
54
  | [`pixerate-video-colour-grade`](pixerate-video-colour-grade/SKILL.md) | Video (`pixerate-video-editor`) | Correct and match shots, then one look on an adjustment clip; LUTs at moderate strength with skin protected. |
55
55
  | [`pixerate-video-montage`](pixerate-video-montage/SKILL.md) | Video (`pixerate-video-editor`) | Selects on the beat, varied pacing, short transitions with handles, music under everything and a clean ending. |
56
+ | [`pixerate-chart-choose-and-build`](pixerate-chart-choose-and-build/SKILL.md) | Chart (`pixerate-chart`) | From the question to the type, clean ordered data, honest axes, units and colour-blind-safe colours. |
57
+ | [`pixerate-chart-annotate`](pixerate-chart-annotate/SKILL.md) | Chart (`pixerate-chart`) | A takeaway title, highlights, labelled targets and ranges, callouts and a source note, kept few. |
56
58
  | [`pixerate-talking-head-edit`](pixerate-talking-head-edit/SKILL.md) | Video (`pixerate-video-editor`) | Talking-head cleanup by transcript: fillers, long pauses and stutters found in one pass and cut as one plan reviewed before it applies. Owns the dialogue tools. |
57
59
  | [`pixerate-bgm-alignment`](pixerate-bgm-alignment/SKILL.md) | Video (`pixerate-video-editor`) | Background music: beats and cuts on the beat, ducking under speech, loudness in LUFS. Owns the music tools. |
58
60
 
@@ -117,7 +119,8 @@ same change.
117
119
  user's words), a `fixture` (the starting document: `{ "editor": "spreadsheet", "rows" }`,
118
120
  `{ "editor": "image", "width", "height", "background", "shapes" }` or `{ "editor":
119
121
  "vector", "width", "height", "setup" }` with commands that draw the starting art,
120
- `{ "editor": "paint", "width", "height", "setup" }` or `{ "editor": "video", "setup" }` with tool calls), `checks` (a read-only
122
+ `{ "editor": "paint", "width", "height", "setup" }` `{ "editor": "video", "setup" }` or `{ "editor":
123
+ "chart", "setup" }` with tool calls), `checks` (a read-only
121
124
  tool, or `run_command` with a read-only command such as `vector.list`, its `args`, what its result must match in `expect`, and `why`) and a `solution` (the
122
125
  tool calls an expert would make). The test proves each eval: some check fails on the fixture,
123
126
  and all pass after the solution. A model harness runs the same file with the model in place
@@ -44,6 +44,14 @@ Charts inside a rich-text document are not edited with these tools: use `insert_
44
44
  `list_document_charts` and `update_chart_in_document` from the `pixerate-rich-text` skill
45
45
  (a chart block goes in the prose, never inside a table row).
46
46
 
47
+ ## Workflow skills
48
+
49
+ For these jobs, load the workflow skill too: it has the method, the checks between steps and
50
+ the pitfalls, and uses the tools below.
51
+
52
+ - `pixerate-chart-choose-and-build`: the type from the question, clean data, honest axes, units, colours.
53
+ - `pixerate-chart-annotate`: a takeaway title, highlights, targets, ranges, callouts and the source.
54
+
47
55
  ## Working method
48
56
 
49
57
  0. **House style.** When the host has one (`list_toolsets` shows a `style` field on the chart
@@ -0,0 +1,96 @@
1
+ ---
2
+ name: pixerate-chart-annotate
3
+ description: The annotation workflow in the Pixerate chart editor - turning a correct chart into one that makes its point - a title that states the takeaway, highlighting the categories or series that matter, reference lines for targets and averages, bands for ranges, callouts on the points that need explaining, units, a source note and a check that the annotations stay few and honest. Use when asked to make a chart tell a story, highlight or call out a point, show a target, goal, threshold, average or range, add a source or footnote, or make a chart ready for a report, slide or board pack, or when highlight_chart, add_chart_reference_line, add_chart_reference_band or set_chart_callout are available. Load with pixerate-chart, which lists every tool.
4
+ metadata:
5
+ kind: workflow
6
+ reference: pixerate-chart
7
+ ---
8
+
9
+ # Annotating a chart
10
+
11
+ A chart for a report or a slide should say what it means, not leave the reader to find it. Once
12
+ the type and data are right (`pixerate-chart-choose-and-build`), add the few marks that point at
13
+ the message: a title that states it, a highlight on what matters, a reference line or band for
14
+ the benchmark, a callout where something needs explaining, and a source note. Fewer is better:
15
+ every annotation competes with the data.
16
+
17
+ This skill is the method. `pixerate-chart` has the full tool reference.
18
+
19
+ ## Goal and quality bar
20
+
21
+ - The title states the takeaway ("Q3 was our record quarter"), the subtitle the measure and
22
+ units if the axis does not.
23
+ - What matters stands out (`highlight`), and everything else recedes.
24
+ - Benchmarks are drawn, labelled, on the right axis: a target line, an average, a normal range.
25
+ - At most one or two reference lines, one band and a couple of callouts.
26
+ - The source is cited in `note`; units are stated.
27
+ - The data is unchanged by annotating.
28
+
29
+ ## Inputs to confirm
30
+
31
+ - **The message:** the one thing the reader should take away.
32
+ - **Benchmarks:** targets, averages, thresholds or ranges, with their values and units.
33
+ - **Source** of the data, and the audience (report, slide, dashboard).
34
+
35
+ ## Working method
36
+
37
+ 1. **Read.** `get_chart`: the type, categories, series, existing annotations and warnings.
38
+ **Check:** the chart is already the right type with clean data; if not, fix that first.
39
+ 2. **Title the takeaway.** `update_chart` `title` with the message, `subtitle` with the measure
40
+ and units.
41
+ 3. **Highlight.** `highlight_chart` with the category labels or series names that carry the
42
+ message; the rest dims.
43
+ **Check:** `get_chart` `spec.highlight`.
44
+ 4. **Benchmarks.** `add_chart_reference_line` for a target or average (`value`, `label`, `axis`);
45
+ `add_chart_reference_band` for a range (`from`, `to`, `label`).
46
+ **Check:** `spec.referenceLines` / `referenceBands` have the values and labels.
47
+ 5. **Callouts** for points that need a word (`set_chart_callout` on a series and category), such
48
+ as "Launch" or "Strike".
49
+ 6. **Source and units.** `update_chart` `note` ("Source: …"); the axis `format` or title for
50
+ units.
51
+ 7. **Review.** `render_chart`; remove any annotation that does not help the message.
52
+
53
+ ## Decision table
54
+
55
+ | Want to say | Annotation |
56
+ | --- | --- |
57
+ | This one is the point | `highlight_chart` on it; title states it |
58
+ | Above or below a goal | `add_chart_reference_line` at the target, labelled |
59
+ | Inside or outside a normal range | `add_chart_reference_band`, labelled |
60
+ | Compared with the average | Reference line at the average, labelled "Average" |
61
+ | Something happened here | `set_chart_callout` on that point |
62
+ | Where the numbers came from | `note` |
63
+ | The trend, not the noise | `add_chart_trendline` (say what it models) |
64
+
65
+ ## Recipes
66
+
67
+ **Record quarter.** `highlight_chart` `{ names: ["Q3"] }` → `update_chart` `{ title: "Q3 was our
68
+ record quarter" }`.
69
+
70
+ **Target.** `add_chart_reference_line` `{ value: 100000, label: "Target" }`.
71
+
72
+ **Board-ready.** `update_chart` `{ note: "Source: internal CRM, 2026", yAxis: { format: { style:
73
+ "currency", currency: "USD" } } }`.
74
+
75
+ ## Pitfalls
76
+
77
+ - **Neutral titles** ("Quarterly revenue") make the reader do the work; state the finding when
78
+ the chart has one.
79
+ - **Annotation clutter**: many lines, bands and callouts hide the data.
80
+ - **Recolouring instead of highlighting**: changing one series' colour by hand breaks palettes
81
+ and colour checks; `highlight_chart` keeps them.
82
+ - **Unlabelled lines**: a line without a label is a puzzle.
83
+ - **Targets on the wrong axis**: put a reference line on the axis of the series it belongs to
84
+ (`axis: "y2"` for a secondary-axis series).
85
+ - **Claims the data does not support**: the title must be true of the chart as drawn.
86
+
87
+ ## Tools used
88
+
89
+ - `get_chart`, `render_chart`: read and look
90
+ - `update_chart`: title, subtitle, note and axis formats
91
+ - `highlight_chart`: what stands out
92
+ - `add_chart_reference_line`, `update_chart_reference_line`, `remove_chart_reference_line`: targets and averages
93
+ - `add_chart_reference_band`, `update_chart_reference_band`, `remove_chart_reference_band`: ranges
94
+ - `set_chart_callout`: notes on points
95
+ - `add_chart_trendline`: a fitted trend
96
+ - `undo_chart`: take back a step
@@ -0,0 +1,78 @@
1
+ {
2
+ "prompt": "Make this chart tell the reader that Q3 was our record quarter.",
3
+ "fixture": {
4
+ "editor": "chart",
5
+ "setup": [
6
+ {
7
+ "tool": "set_chart_data",
8
+ "args": {
9
+ "categories": [
10
+ "Q1",
11
+ "Q2",
12
+ "Q3",
13
+ "Q4"
14
+ ],
15
+ "series": [
16
+ {
17
+ "name": "Revenue",
18
+ "values": [
19
+ 82000,
20
+ 91000,
21
+ 118000,
22
+ 96000
23
+ ]
24
+ }
25
+ ]
26
+ }
27
+ },
28
+ {
29
+ "tool": "update_chart",
30
+ "args": {
31
+ "type": "bar",
32
+ "title": "Quarterly revenue"
33
+ }
34
+ }
35
+ ]
36
+ },
37
+ "checks": [
38
+ {
39
+ "why": "Q3 stands out",
40
+ "tool": "get_chart",
41
+ "expect": {
42
+ "spec": {
43
+ "$expr": "v.highlight.includes('Q3') || v.series.some((s) => (s.callouts || [])[2]) || v.series.some((s) => s.pointColors && s.pointColors[2] && s.pointColors.filter(Boolean).length === 1)"
44
+ }
45
+ }
46
+ },
47
+ {
48
+ "why": "the title states the takeaway",
49
+ "tool": "get_chart",
50
+ "expect": {
51
+ "spec": {
52
+ "title": {
53
+ "$match": [
54
+ "q3|third quarter",
55
+ "i"
56
+ ]
57
+ }
58
+ }
59
+ }
60
+ }
61
+ ],
62
+ "solution": [
63
+ {
64
+ "tool": "highlight_chart",
65
+ "args": {
66
+ "names": [
67
+ "Q3"
68
+ ]
69
+ }
70
+ },
71
+ {
72
+ "tool": "update_chart",
73
+ "args": {
74
+ "title": "Q3 was our record quarter"
75
+ }
76
+ }
77
+ ]
78
+ }
@@ -0,0 +1,76 @@
1
+ {
2
+ "prompt": "Before this chart goes in the board report, add the source (internal CRM, 2026) and make the units clear.",
3
+ "fixture": {
4
+ "editor": "chart",
5
+ "setup": [
6
+ {
7
+ "tool": "set_chart_data",
8
+ "args": {
9
+ "categories": [
10
+ "Q1",
11
+ "Q2",
12
+ "Q3",
13
+ "Q4"
14
+ ],
15
+ "series": [
16
+ {
17
+ "name": "Revenue",
18
+ "values": [
19
+ 82000,
20
+ 91000,
21
+ 118000,
22
+ 96000
23
+ ]
24
+ }
25
+ ]
26
+ }
27
+ },
28
+ {
29
+ "tool": "update_chart",
30
+ "args": {
31
+ "type": "bar",
32
+ "title": "Quarterly revenue"
33
+ }
34
+ }
35
+ ]
36
+ },
37
+ "checks": [
38
+ {
39
+ "why": "a source note",
40
+ "tool": "get_chart",
41
+ "expect": {
42
+ "spec": {
43
+ "note": {
44
+ "$match": [
45
+ "crm",
46
+ "i"
47
+ ]
48
+ }
49
+ }
50
+ }
51
+ },
52
+ {
53
+ "why": "the units are stated (axis title, format or subtitle)",
54
+ "tool": "get_chart",
55
+ "expect": {
56
+ "spec": {
57
+ "$expr": "/usd|\\$|dollar/i.test(v.yAxis.title + ' ' + v.subtitle) || v.yAxis.format.style === 'currency'"
58
+ }
59
+ }
60
+ }
61
+ ],
62
+ "solution": [
63
+ {
64
+ "tool": "update_chart",
65
+ "args": {
66
+ "note": "Source: internal CRM, 2026",
67
+ "yAxis": {
68
+ "format": {
69
+ "style": "currency",
70
+ "currency": "USD"
71
+ }
72
+ }
73
+ }
74
+ }
75
+ ]
76
+ }
@@ -0,0 +1,85 @@
1
+ {
2
+ "prompt": "Show our quarterly target of 100,000 on the revenue chart so it's clear which quarters hit it.",
3
+ "fixture": {
4
+ "editor": "chart",
5
+ "setup": [
6
+ {
7
+ "tool": "set_chart_data",
8
+ "args": {
9
+ "categories": [
10
+ "Q1",
11
+ "Q2",
12
+ "Q3",
13
+ "Q4"
14
+ ],
15
+ "series": [
16
+ {
17
+ "name": "Revenue",
18
+ "values": [
19
+ 82000,
20
+ 91000,
21
+ 118000,
22
+ 96000
23
+ ]
24
+ }
25
+ ]
26
+ }
27
+ },
28
+ {
29
+ "tool": "update_chart",
30
+ "args": {
31
+ "type": "bar",
32
+ "title": "Quarterly revenue"
33
+ }
34
+ }
35
+ ]
36
+ },
37
+ "checks": [
38
+ {
39
+ "why": "a labelled reference line at 100,000 on the value axis",
40
+ "tool": "get_chart",
41
+ "expect": {
42
+ "spec": {
43
+ "referenceLines": {
44
+ "$some": {
45
+ "value": 100000,
46
+ "label": {
47
+ "$match": [
48
+ "target",
49
+ "i"
50
+ ]
51
+ }
52
+ }
53
+ }
54
+ }
55
+ }
56
+ },
57
+ {
58
+ "why": "the data is unchanged",
59
+ "tool": "get_chart",
60
+ "expect": {
61
+ "spec": {
62
+ "series": [
63
+ {
64
+ "values": [
65
+ 82000,
66
+ 91000,
67
+ 118000,
68
+ 96000
69
+ ]
70
+ }
71
+ ]
72
+ }
73
+ }
74
+ }
75
+ ],
76
+ "solution": [
77
+ {
78
+ "tool": "add_chart_reference_line",
79
+ "args": {
80
+ "value": 100000,
81
+ "label": "Target"
82
+ }
83
+ }
84
+ ]
85
+ }
@@ -0,0 +1,100 @@
1
+ ---
2
+ name: pixerate-chart-choose-and-build
3
+ description: The chart building workflow in the Pixerate chart editor - starting from the question the chart answers, choosing the chart type for it (change over time, comparison, part of a whole, distribution, relationship, flow, geography), loading clean data in order, honest axes (bars from zero, log only for orders of magnitude), units and number formats, colours that colour-blind readers can tell apart, and checking before handing it over. Use when asked to make, build, create, choose, recommend or fix a chart or graph, visualise or plot data, pick the right chart, or make a chart clearer, more honest or accessible, or when recommend_chart, set_chart_data, update_chart or check_chart_colors are available. Load with pixerate-chart, which lists every tool.
4
+ metadata:
5
+ kind: workflow
6
+ reference: pixerate-chart
7
+ ---
8
+
9
+ # Choosing and building a chart
10
+
11
+ A chart answers one question. Name the question first ("how did revenue change over the
12
+ year?", "which region sells most?"), and the type, the order of the data, the axes and the
13
+ title follow from it. Build in few calls: data in one, style in one, then check the warnings,
14
+ the colours and the numbers before anyone sees it.
15
+
16
+ This skill is the method. `pixerate-chart` has the full tool reference; for titles, highlights,
17
+ reference lines and notes that make the point, see `pixerate-chart-annotate`.
18
+
19
+ ## Goal and quality bar
20
+
21
+ - The type fits the question (see the decision table).
22
+ - The data is in a meaningful order: time in calendar order, categories sorted by value unless
23
+ they have a natural order.
24
+ - Axes are honest: bars and areas start at zero; a log scale only when values span orders of
25
+ magnitude, and labelled as such; a second axis only for a second unit.
26
+ - Units are stated (axis title or number `format`); the title says what the chart shows.
27
+ - Colours pass `check_chart_colors` (`ok: true`) and there are at most about seven series
28
+ colours.
29
+ - `get_chart` `summary.warnings` and `issues` are empty.
30
+
31
+ ## Inputs to confirm
32
+
33
+ - **The question** the chart must answer, and for whom (a report, a slide, a dashboard).
34
+ - **The data**: where it is (pasted, CSV, a spreadsheet, an Office file) and its units.
35
+ - **House style** if the host has one (`check_chart_style`).
36
+
37
+ ## Working method
38
+
39
+ 1. **Read.** `get_chart` (the spec, the data table, warnings); `list_file_charts` when the
40
+ data is in an open Office file.
41
+ 2. **Name the question and choose the type.** `recommend_chart` suggests types for the data;
42
+ check its suggestion against the question (see the table).
43
+ **Check:** you can say the question in one sentence and why this type answers it.
44
+ 3. **Load clean data in one call.** `set_chart_data` with categories in the right order and one
45
+ series per measure (or `import_chart_csv`); numbers as numbers, gaps as `null`.
46
+ **Check:** `get_chart` `spec.categories` and each series' `values` line up as intended.
47
+ 4. **Style in one call.** `update_chart` with `type`, `title`, axis `title` and `format`,
48
+ `legend`, `stacking`, `orientation`, `dataLabels`. Bars from zero (`yAxis.min` null or 0).
49
+ **Check:** `get_chart` spec; `summary.description` reads as the chart you meant.
50
+ 5. **Colours.** `check_chart_colors`; fix failing pairs with `update_chart_series` `color` (a
51
+ palette such as `#4269d0` / `#efb118` separates for most colour vision), or
52
+ `apply_chart_preset` for a tested palette.
53
+ **Check:** `check_chart_colors` `ok: true`.
54
+ 6. **Look.** `render_chart` once after the batch; fix anything that does not read.
55
+
56
+ ## Decision table
57
+
58
+ | Question | Type |
59
+ | --- | --- |
60
+ | How did it change over time? | `line` (one or a few series), `area` for totals, columns for few periods |
61
+ | Which is biggest? Compare categories | `bar`, horizontal for long labels, sorted by value |
62
+ | Part of a whole (few parts) | `pie` or `donut` (at most five slices), or a stacked bar |
63
+ | Part of a whole, nested | `treemap`, `sunburst` (`set_chart_tree`) |
64
+ | How are values distributed? | `histogram`, `box` |
65
+ | Do two measures relate? | `scatter`, `bubble` for a third measure |
66
+ | Before and after per category | `slope` |
67
+ | Contributions to a total | `waterfall` |
68
+ | Flows between stages | `sankey` (`set_chart_links`), `funnel` for one path |
69
+ | Survey answers | `likert` |
70
+ | By region on a map | `choropleth` |
71
+
72
+ ## Recipes
73
+
74
+ **Trend over time.** `set_chart_data` with categories in calendar order → `update_chart`
75
+ `{ type: "line", title, yAxis: { title: "Revenue (USD thousands)" } }` → `get_chart`.
76
+
77
+ **Colour-blind fix.** `check_chart_colors` → `update_chart_series` `color` on the clashing series
78
+ → `check_chart_colors` again.
79
+
80
+ **Honest bars.** `update_chart` `{ yAxis: { min: 0 } }`; when small differences matter, show the
81
+ differences themselves (a bar of the change) or a dot plot rather than a cut axis.
82
+
83
+ ## Pitfalls
84
+
85
+ - **Truncated bar axes** exaggerate differences; bars start at zero.
86
+ - **Pie charts with many or similar slices** cannot be read; use a sorted bar.
87
+ - **Red and green** as the only distinction fail for the most common colour-vision deficiency.
88
+ - **Unsorted categories** without a natural order hide the ranking.
89
+ - **Mixed units on one axis**, or a second axis for the same unit, mislead.
90
+ - **Missing units** leave the reader guessing; put them in the axis title or format.
91
+
92
+ ## Tools used
93
+
94
+ - `get_chart`, `render_chart`: the spec, data table, warnings, and a picture
95
+ - `recommend_chart`: types that suit the data
96
+ - `set_chart_data`, `import_chart_csv`, `transpose_chart`: load the data in order
97
+ - `update_chart`, `update_chart_series`, `apply_chart_preset`: type, titles, axes, formats and colours
98
+ - `check_chart_colors`, `check_chart_style`: colour-blind and house-style checks
99
+ - `list_file_charts`, `load_file_chart`: data from open Office files
100
+ - `undo_chart`: take back a step
@@ -0,0 +1,113 @@
1
+ {
2
+ "prompt": "Some of our readers are colour-blind and can't tell the two years apart in this chart. Fix the colours without changing the data.",
3
+ "fixture": {
4
+ "editor": "chart",
5
+ "setup": [
6
+ {
7
+ "tool": "set_chart_data",
8
+ "args": {
9
+ "categories": [
10
+ "North",
11
+ "South",
12
+ "East",
13
+ "West"
14
+ ],
15
+ "series": [
16
+ {
17
+ "name": "2025",
18
+ "values": [
19
+ 12,
20
+ 15,
21
+ 9,
22
+ 11
23
+ ],
24
+ "color": "#d62728"
25
+ },
26
+ {
27
+ "name": "2026",
28
+ "values": [
29
+ 14,
30
+ 13,
31
+ 12,
32
+ 15
33
+ ],
34
+ "color": "#2ca02c"
35
+ }
36
+ ]
37
+ }
38
+ },
39
+ {
40
+ "tool": "update_chart",
41
+ "args": {
42
+ "type": "bar",
43
+ "title": "Sales by region"
44
+ }
45
+ }
46
+ ]
47
+ },
48
+ "checks": [
49
+ {
50
+ "why": "the colours pass the colour-blind check",
51
+ "tool": "check_chart_colors",
52
+ "expect": {
53
+ "ok": true
54
+ }
55
+ },
56
+ {
57
+ "why": "the data is unchanged",
58
+ "tool": "get_chart",
59
+ "expect": {
60
+ "spec": {
61
+ "categories": [
62
+ "North",
63
+ "South",
64
+ "East",
65
+ "West"
66
+ ],
67
+ "series": [
68
+ {
69
+ "name": "2025",
70
+ "values": [
71
+ 12,
72
+ 15,
73
+ 9,
74
+ 11
75
+ ]
76
+ },
77
+ {
78
+ "name": "2026",
79
+ "values": [
80
+ 14,
81
+ 13,
82
+ 12,
83
+ 15
84
+ ]
85
+ }
86
+ ]
87
+ }
88
+ }
89
+ }
90
+ ],
91
+ "solution": [
92
+ {
93
+ "tool": "check_chart_colors"
94
+ },
95
+ {
96
+ "tool": "update_chart_series",
97
+ "args": {
98
+ "series": "2025",
99
+ "color": "#4269d0"
100
+ }
101
+ },
102
+ {
103
+ "tool": "update_chart_series",
104
+ "args": {
105
+ "series": "2026",
106
+ "color": "#efb118"
107
+ }
108
+ },
109
+ {
110
+ "tool": "check_chart_colors"
111
+ }
112
+ ]
113
+ }
@@ -0,0 +1,81 @@
1
+ {
2
+ "prompt": "This bar chart makes the differences between teams look huge, but they're small. Make it honest.",
3
+ "fixture": {
4
+ "editor": "chart",
5
+ "setup": [
6
+ {
7
+ "tool": "set_chart_data",
8
+ "args": {
9
+ "categories": [
10
+ "Team A",
11
+ "Team B",
12
+ "Team C",
13
+ "Team D"
14
+ ],
15
+ "series": [
16
+ {
17
+ "name": "Satisfaction (%)",
18
+ "values": [
19
+ 92,
20
+ 95,
21
+ 94,
22
+ 97
23
+ ]
24
+ }
25
+ ]
26
+ }
27
+ },
28
+ {
29
+ "tool": "update_chart",
30
+ "args": {
31
+ "type": "bar",
32
+ "title": "Satisfaction by team",
33
+ "yAxis": {
34
+ "min": 90,
35
+ "max": 98
36
+ }
37
+ }
38
+ }
39
+ ]
40
+ },
41
+ "checks": [
42
+ {
43
+ "why": "bars start at zero (or the chart is no longer bars)",
44
+ "tool": "get_chart",
45
+ "expect": {
46
+ "spec": {
47
+ "$expr": "v.type !== 'bar' || v.yAxis.min === null || v.yAxis.min === 0"
48
+ }
49
+ }
50
+ },
51
+ {
52
+ "why": "the data is unchanged",
53
+ "tool": "get_chart",
54
+ "expect": {
55
+ "spec": {
56
+ "series": [
57
+ {
58
+ "values": [
59
+ 92,
60
+ 95,
61
+ 94,
62
+ 97
63
+ ]
64
+ }
65
+ ]
66
+ }
67
+ }
68
+ }
69
+ ],
70
+ "solution": [
71
+ {
72
+ "tool": "update_chart",
73
+ "args": {
74
+ "yAxis": {
75
+ "min": 0,
76
+ "max": 100
77
+ }
78
+ }
79
+ }
80
+ ]
81
+ }
@@ -0,0 +1,175 @@
1
+ {
2
+ "prompt": "This is our 2026 monthly revenue in thousands of US dollars, pasted in a muddle. Make a chart that shows how revenue changed over the year.",
3
+ "fixture": {
4
+ "editor": "chart",
5
+ "setup": [
6
+ {
7
+ "tool": "set_chart_data",
8
+ "args": {
9
+ "categories": [
10
+ "Mar",
11
+ "Jan",
12
+ "Feb",
13
+ "Jun",
14
+ "Apr",
15
+ "May",
16
+ "Sep",
17
+ "Jul",
18
+ "Aug",
19
+ "Dec",
20
+ "Oct",
21
+ "Nov"
22
+ ],
23
+ "series": [
24
+ {
25
+ "name": "Revenue",
26
+ "values": [
27
+ 51,
28
+ 42,
29
+ 45,
30
+ 61,
31
+ 48,
32
+ 55,
33
+ 70,
34
+ 58,
35
+ 64,
36
+ 82,
37
+ 68,
38
+ 75
39
+ ]
40
+ }
41
+ ]
42
+ }
43
+ },
44
+ {
45
+ "tool": "update_chart",
46
+ "args": {
47
+ "type": "pie"
48
+ }
49
+ }
50
+ ]
51
+ },
52
+ "checks": [
53
+ {
54
+ "why": "a line or area chart for change over time",
55
+ "tool": "get_chart",
56
+ "expect": {
57
+ "spec": {
58
+ "type": {
59
+ "$in": [
60
+ "line",
61
+ "area"
62
+ ]
63
+ }
64
+ }
65
+ }
66
+ },
67
+ {
68
+ "why": "months in calendar order, with their own values",
69
+ "tool": "get_chart",
70
+ "expect": {
71
+ "spec": {
72
+ "categories": [
73
+ "Jan",
74
+ "Feb",
75
+ "Mar",
76
+ "Apr",
77
+ "May",
78
+ "Jun",
79
+ "Jul",
80
+ "Aug",
81
+ "Sep",
82
+ "Oct",
83
+ "Nov",
84
+ "Dec"
85
+ ],
86
+ "series": [
87
+ {
88
+ "values": [
89
+ 42,
90
+ 45,
91
+ 51,
92
+ 48,
93
+ 55,
94
+ 61,
95
+ 58,
96
+ 64,
97
+ 70,
98
+ 68,
99
+ 75,
100
+ 82
101
+ ]
102
+ }
103
+ ]
104
+ }
105
+ }
106
+ },
107
+ {
108
+ "why": "a title and a value axis that says the unit",
109
+ "tool": "get_chart",
110
+ "expect": {
111
+ "spec": {
112
+ "title": {
113
+ "$match": "\\S"
114
+ },
115
+ "yAxis": {
116
+ "$expr": "/usd|\\$|dollar/i.test(v.title) || v.format.style === 'currency' || /\\$/.test(v.format.prefix || '')"
117
+ }
118
+ }
119
+ }
120
+ }
121
+ ],
122
+ "solution": [
123
+ {
124
+ "tool": "get_chart"
125
+ },
126
+ {
127
+ "tool": "set_chart_data",
128
+ "args": {
129
+ "categories": [
130
+ "Jan",
131
+ "Feb",
132
+ "Mar",
133
+ "Apr",
134
+ "May",
135
+ "Jun",
136
+ "Jul",
137
+ "Aug",
138
+ "Sep",
139
+ "Oct",
140
+ "Nov",
141
+ "Dec"
142
+ ],
143
+ "series": [
144
+ {
145
+ "name": "Revenue",
146
+ "values": [
147
+ 42,
148
+ 45,
149
+ 51,
150
+ 48,
151
+ 55,
152
+ 61,
153
+ 58,
154
+ 64,
155
+ 70,
156
+ 68,
157
+ 75,
158
+ 82
159
+ ]
160
+ }
161
+ ]
162
+ }
163
+ },
164
+ {
165
+ "tool": "update_chart",
166
+ "args": {
167
+ "type": "line",
168
+ "title": "Revenue grew through 2026",
169
+ "yAxis": {
170
+ "title": "Revenue (USD thousands)"
171
+ }
172
+ }
173
+ }
174
+ ]
175
+ }