@pixerate/editor 0.161.0 → 0.162.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.
Files changed (109) hide show
  1. package/dist/agent/index.cjs +36 -35
  2. package/dist/agent/index.js +7 -6
  3. package/dist/animation/index.cjs +5 -4
  4. package/dist/animation/index.js +4 -3
  5. package/dist/animation-editor/index.cjs +63 -62
  6. package/dist/animation-editor/index.js +7 -6
  7. package/dist/{brushstrokeRender-Q4LI22QV.js → brushstrokeRender-7LXCU7PD.js} +2 -2
  8. package/dist/{brushstrokeRender-5WANQIG4.cjs → brushstrokeRender-VO4ZTWVA.cjs} +5 -5
  9. package/dist/canvas/index.cjs +40 -39
  10. package/dist/canvas/index.js +7 -6
  11. package/dist/chart/index.cjs +88 -87
  12. package/dist/chart/index.js +7 -6
  13. package/dist/{chunk-LQLZHP6F.js → chunk-36OKQDCF.js} +7 -4
  14. package/dist/{chunk-AKDWAWSY.cjs → chunk-3IHNXRUV.cjs} +45 -45
  15. package/dist/{chunk-72BGL3I2.js → chunk-3VLOOO3G.js} +1 -1
  16. package/dist/{chunk-TVO6W2Q6.cjs → chunk-4NGVHHJB.cjs} +17 -17
  17. package/dist/chunk-4VU4CP7R.js +769 -0
  18. package/dist/{chunk-MHEXLG3M.cjs → chunk-5AARIS3R.cjs} +37 -37
  19. package/dist/{chunk-DBIVICAO.js → chunk-7CKPUUN4.js} +1 -1
  20. package/dist/{chunk-57JLBGJ5.cjs → chunk-7YBDRJXT.cjs} +8 -8
  21. package/dist/{chunk-KY3FYKP3.js → chunk-BQWGN5WH.js} +1 -1
  22. package/dist/{chunk-LEUZ4FRC.cjs → chunk-CIPIESX4.cjs} +3 -3
  23. package/dist/chunk-CJE5MOIS.js +347 -0
  24. package/dist/{chunk-5QBRAIVK.js → chunk-CX6JLDFC.js} +3 -3
  25. package/dist/{chunk-R3QY7PPL.js → chunk-DTSTNRPH.js} +1 -1
  26. package/dist/{chunk-6LUYFHHN.cjs → chunk-EQ46F75O.cjs} +51 -51
  27. package/dist/{chunk-UKCJ43YL.cjs → chunk-ES3UEVUT.cjs} +130 -898
  28. package/dist/{chunk-UM3RPYC5.js → chunk-EVPZ5IKN.js} +1 -1
  29. package/dist/{chunk-XNQ5EFB4.cjs → chunk-EY22AY6R.cjs} +16 -16
  30. package/dist/chunk-FKUKJ3XA.cjs +347 -0
  31. package/dist/{chunk-YABNLNY7.js → chunk-GLMP22FT.js} +3 -3
  32. package/dist/{chunk-SUF6WRXR.cjs → chunk-GTELFJRE.cjs} +2 -2
  33. package/dist/{chunk-AY65VQSU.js → chunk-H3OEM3Q2.js} +1 -1
  34. package/dist/{chunk-4YWAEV4W.cjs → chunk-HDGBM7EA.cjs} +26 -26
  35. package/dist/{chunk-H5XX4KHB.js → chunk-HLOKB7WW.js} +5 -3
  36. package/dist/{chunk-QMI5HZWA.cjs → chunk-JCXMJUQO.cjs} +5 -5
  37. package/dist/{chunk-WPPJH6X4.cjs → chunk-LL3H5XUH.cjs} +6 -4
  38. package/dist/{chunk-4T44CJ5K.cjs → chunk-NA4UVVJ4.cjs} +15 -15
  39. package/dist/{chunk-Q6PA362P.js → chunk-NO5QQDHZ.js} +2 -2
  40. package/dist/{chunk-3YDQLQH2.cjs → chunk-PA3IMPPS.cjs} +81 -81
  41. package/dist/{chunk-WNAAWLZ5.js → chunk-QY4KUDMY.js} +2 -2
  42. package/dist/{chunk-AA34BA7G.js → chunk-QYZGBVNC.js} +30 -798
  43. package/dist/chunk-S6X2QRVU.cjs +769 -0
  44. package/dist/{chunk-6UPGM3JB.js → chunk-SET6KJOP.js} +2 -2
  45. package/dist/{chunk-GNK7SESQ.cjs → chunk-V7GWZIZK.cjs} +7 -4
  46. package/dist/{chunk-EYVVIZJ3.cjs → chunk-WTW7QRTS.cjs} +1 -1
  47. package/dist/{chunk-SSCL7QQ4.js → chunk-XDCFJEU7.js} +2 -2
  48. package/dist/{chunk-JGKCC5OF.cjs → chunk-YQ5C5BVK.cjs} +10 -9
  49. package/dist/{chunk-A5UCR2TO.js → chunk-YXKLVD56.js} +10 -9
  50. package/dist/{chunk-45XXVHVG.js → chunk-ZZGPYNS2.js} +1 -1
  51. package/dist/color/index.cjs +6 -2
  52. package/dist/color/index.d.cts +1 -0
  53. package/dist/color/index.d.ts +1 -0
  54. package/dist/color/index.js +28 -24
  55. package/dist/color/suggest.d.cts +12 -0
  56. package/dist/color/suggest.d.ts +12 -0
  57. package/dist/effects/index.cjs +37 -13
  58. package/dist/effects/index.js +29 -5
  59. package/dist/effects/ui.d.cts +20 -3
  60. package/dist/effects/ui.d.ts +20 -3
  61. package/dist/export/index.cjs +6 -5
  62. package/dist/export/index.js +5 -4
  63. package/dist/extensions/index.cjs +9 -8
  64. package/dist/extensions/index.js +8 -7
  65. package/dist/gradient/index.cjs +28 -26
  66. package/dist/gradient/index.js +7 -5
  67. package/dist/image-editor/index.cjs +641 -640
  68. package/dist/image-editor/index.js +7 -6
  69. package/dist/index.cjs +15 -14
  70. package/dist/index.js +9 -8
  71. package/dist/paint/index.cjs +2 -2
  72. package/dist/paint/index.js +1 -1
  73. package/dist/paint-editor/index.cjs +136 -135
  74. package/dist/paint-editor/index.js +8 -7
  75. package/dist/typography/accessibility.d.cts +44 -0
  76. package/dist/typography/accessibility.d.ts +44 -0
  77. package/dist/typography/catalog.d.cts +98 -0
  78. package/dist/typography/catalog.d.ts +98 -0
  79. package/dist/typography/index.cjs +1099 -0
  80. package/dist/typography/index.d.cts +16 -0
  81. package/dist/typography/index.d.ts +16 -0
  82. package/dist/typography/index.js +1099 -0
  83. package/dist/typography/metrics.d.cts +31 -0
  84. package/dist/typography/metrics.d.ts +31 -0
  85. package/dist/typography/picker.d.cts +132 -0
  86. package/dist/typography/picker.d.ts +132 -0
  87. package/dist/typography/registry.d.cts +66 -0
  88. package/dist/typography/registry.d.ts +66 -0
  89. package/dist/typography/scale.d.cts +62 -0
  90. package/dist/typography/scale.d.ts +62 -0
  91. package/dist/typography/segment.d.cts +26 -0
  92. package/dist/typography/segment.d.ts +26 -0
  93. package/dist/typography/stack.d.cts +26 -0
  94. package/dist/typography/stack.d.ts +26 -0
  95. package/dist/typography/tools.d.cts +34 -0
  96. package/dist/typography/tools.d.ts +34 -0
  97. package/dist/typography/types.d.cts +96 -0
  98. package/dist/typography/types.d.ts +96 -0
  99. package/dist/typography/weight.d.cts +13 -0
  100. package/dist/typography/weight.d.ts +13 -0
  101. package/dist/vector/index.cjs +951 -950
  102. package/dist/vector/index.js +6 -5
  103. package/dist/video-editor/index.cjs +5 -4
  104. package/dist/video-editor/index.js +4 -3
  105. package/package.json +13 -3
  106. package/skills/README.md +1 -0
  107. package/skills/pixerate-chart/SKILL.md +92 -9
  108. package/skills/pixerate-paint-editor/SKILL.md +0 -2
  109. package/skills/pixerate-typography/SKILL.md +91 -0
@@ -38,7 +38,7 @@ import {
38
38
  reverseMorphContour,
39
39
  subpathToMorphContour,
40
40
  turnMorphContour
41
- } from "../chunk-WNAAWLZ5.js";
41
+ } from "../chunk-QY4KUDMY.js";
42
42
  import {
43
43
  DEFAULT_VECTOR_FONT,
44
44
  IDENTITY_MATRIX,
@@ -507,8 +507,8 @@ import {
507
507
  withVectorStrokes,
508
508
  withVectorTargetPaint,
509
509
  zigzagPaths
510
- } from "../chunk-6UPGM3JB.js";
511
- import "../chunk-A5UCR2TO.js";
510
+ } from "../chunk-SET6KJOP.js";
511
+ import "../chunk-YXKLVD56.js";
512
512
  import {
513
513
  createProvenance
514
514
  } from "../chunk-M6DNZNVY.js";
@@ -566,11 +566,12 @@ import {
566
566
  HistoryManager
567
567
  } from "../chunk-RNSANXPT.js";
568
568
  import "../chunk-5UJHB3BW.js";
569
- import "../chunk-KY3FYKP3.js";
569
+ import "../chunk-BQWGN5WH.js";
570
570
  import {
571
571
  hexToRgba
572
572
  } from "../chunk-2WPNAGHI.js";
573
- import "../chunk-AA34BA7G.js";
573
+ import "../chunk-QYZGBVNC.js";
574
+ import "../chunk-4VU4CP7R.js";
574
575
  import "../chunk-6ONMQ6WF.js";
575
576
 
576
577
  // src/vector/trace.ts
@@ -168,14 +168,15 @@ require('../chunk-2BSBFBOA.cjs');
168
168
  var _chunkFJ2MSI43cjs = require('../chunk-FJ2MSI43.cjs');
169
169
 
170
170
 
171
- var _chunkSUF6WRXRcjs = require('../chunk-SUF6WRXR.cjs');
171
+ var _chunkGTELFJREcjs = require('../chunk-GTELFJRE.cjs');
172
172
 
173
173
 
174
174
 
175
175
  var _chunkDWJJI6RTcjs = require('../chunk-DWJJI6RT.cjs');
176
- require('../chunk-LEUZ4FRC.cjs');
176
+ require('../chunk-CIPIESX4.cjs');
177
177
  require('../chunk-A6QWKBJP.cjs');
178
- require('../chunk-UKCJ43YL.cjs');
178
+ require('../chunk-ES3UEVUT.cjs');
179
+ require('../chunk-S6X2QRVU.cjs');
179
180
  require('../chunk-IPJ3TEYR.cjs');
180
181
 
181
182
  // src/video-editor/parse.ts
@@ -3356,7 +3357,7 @@ var VideoEditorController = (_class7 = class _VideoEditorController {
3356
3357
  this.readOnly = _nullishCoalesce(options.readOnly, () => ( false));
3357
3358
  this.previewScale = checkPreviewScale(_nullishCoalesce(options.previewScale, () => ( "auto")));
3358
3359
  this.onChange = options.onChange;
3359
- this.player = new (0, _chunkSUF6WRXRcjs.AnimationPlayer)({ clock: options.clock, duration: () => _chunk3OSXHLITcjs.videoProjectDuration.call(void 0, this.project), loop: "none" });
3360
+ this.player = new (0, _chunkGTELFJREcjs.AnimationPlayer)({ clock: options.clock, duration: () => _chunk3OSXHLITcjs.videoProjectDuration.call(void 0, this.project), loop: "none" });
3360
3361
  this.state = this.computeState();
3361
3362
  this.connect();
3362
3363
  }
@@ -168,14 +168,15 @@ import {
168
168
  } from "../chunk-RNSANXPT.js";
169
169
  import {
170
170
  AnimationPlayer
171
- } from "../chunk-AY65VQSU.js";
171
+ } from "../chunk-H3OEM3Q2.js";
172
172
  import {
173
173
  CUBIC_BEZIER_PRESETS,
174
174
  evaluateEasing
175
175
  } from "../chunk-5UJHB3BW.js";
176
- import "../chunk-KY3FYKP3.js";
176
+ import "../chunk-BQWGN5WH.js";
177
177
  import "../chunk-2WPNAGHI.js";
178
- import "../chunk-AA34BA7G.js";
178
+ import "../chunk-QYZGBVNC.js";
179
+ import "../chunk-4VU4CP7R.js";
179
180
  import "../chunk-6ONMQ6WF.js";
180
181
 
181
182
  // src/video-editor/parse.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.161.0",
3
+ "version": "0.162.1",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -244,6 +244,16 @@
244
244
  "default": "./dist/color/index.cjs"
245
245
  }
246
246
  },
247
+ "./typography": {
248
+ "import": {
249
+ "types": "./dist/typography/index.d.ts",
250
+ "default": "./dist/typography/index.js"
251
+ },
252
+ "require": {
253
+ "types": "./dist/typography/index.d.cts",
254
+ "default": "./dist/typography/index.cjs"
255
+ }
256
+ },
247
257
  "./gradient": {
248
258
  "import": {
249
259
  "types": "./dist/gradient/index.d.ts",
@@ -361,7 +371,7 @@
361
371
  "prosemirror-markdown": "^1.13.9"
362
372
  },
363
373
  "devDependencies": {
364
- "@anthropic-ai/sdk": "0.137.0",
374
+ "@anthropic-ai/sdk": "1.1.0",
365
375
  "@dagrejs/dagre": "^1.1.8",
366
376
  "@tiptap/core": "^2.27.3",
367
377
  "@tiptap/pm": "^2.27.3",
@@ -369,7 +379,7 @@
369
379
  "@types/markdown-it": "^14.2.0",
370
380
  "@types/node": "^26.6.5",
371
381
  "assemblyscript": "0.28.20",
372
- "fast-check": "^3.23.2",
382
+ "fast-check": "^4.10.2",
373
383
  "mermaid": "12.2.0",
374
384
  "tiptap-markdown": "^0.8.10",
375
385
  "tsup": "^9.0.0",
package/skills/README.md CHANGED
@@ -25,6 +25,7 @@ that uses the tools can load the matching skill.
25
25
  | [`pixerate-tokens`](pixerate-tokens/SKILL.md) | Design tokens: collections, modes, variables and aliases, through the whole-set document tools. |
26
26
  | [`pixerate-effect-authoring`](pixerate-effect-authoring/SKILL.md) | Authoring shaders and procedural effects: inspecting parameters and source code, setting values and presets, keyframing animation tracks, playback timing, and previewing. |
27
27
  | [`pixerate-color`](pixerate-color/SKILL.md) | Colour inspection, WCAG accessibility and contrast optimization, CVD simulation, palette audits, harmonies, and OKLab/OKLCH blends. |
28
+ | [`pixerate-typography`](pixerate-typography/SKILL.md) | Fonts and fallback stacks, font loading, modular type scales and hierarchies, fluid sizes, readability and type-aware contrast audits, text segmentation, the font picker. |
28
29
 
29
30
  ## Using them
30
31
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pixerate-chart
3
- description: How to build, edit and check charts in the Pixerate chart editor through its agent tools and chart.* commands - bar, column, line, area, scatter, bubble, pie, donut, radar, waterfall, histogram, box plot, funnel and treemap charts; series, categories, stacking, secondary axis, log axis, number formats, data labels, legend, trendline, error bars, colour palettes and colour-blind checks, CSV and Mermaid import, Office chart XML export, and reading charts out of open .xlsx, .pptx and .docx files. Use when a chart editor's tools (get_chart, update_chart, set_chart_values, list_file_charts...) are available or the user asks to make, restyle, fix or explain a chart.
3
+ description: How to build, edit and check charts in the Pixerate chart editor through its agent tools and chart.* commands - bar, column, line, area, scatter, bubble, pie, donut, radar, waterfall, histogram, box plot, funnel, treemap (flat or grouped), sunburst, slope, Likert (survey) and parliament charts; series, categories, stacking, secondary axis, log axis, number formats, data labels, legend, trendline, error bars, reference lines and target bands, callouts, highlighting, footnotes, colour palettes and colour-blind checks, CSV and Mermaid import, Office chart XML export, and reading charts out of open .xlsx, .pptx and .docx files. Use when a chart editor's tools (get_chart, update_chart, set_chart_values, list_file_charts...) are available or the user asks to make, restyle, fix or explain a chart.
4
4
  ---
5
5
 
6
6
  # Charts
@@ -10,10 +10,19 @@ A chart editor holds one `ChartSpec`: a `type`, `title` / `subtitle`, `xAxis` /
10
10
  a list of `categories` (strings, in order) and a list of `series`. Each series has an `id`
11
11
  (such as `series-1`), a `name` and one value per category (`null` is a gap), plus optional
12
12
  per-series settings (type for combos, secondary axis, marker, colour, format, trendlines,
13
- error bars). Tools take a series by id or exact name and a category by 0-based index or
14
- exact label; `get_chart` returns both. Pie, donut, funnel and treemap draw only the first
15
- visible series, one slice per category; histogram and box read every value of the visible
16
- series.
13
+ error bars, callouts). Annotations sit on the spec: `note` (footnote), `highlight` (names of
14
+ categories and series to keep bright), `referenceLines`, `referenceBands` and `varyColors`. Tools take a series by id or exact name and a category by 0-based index or
15
+ exact label; `get_chart` returns both. Pie, donut, funnel, treemap and parliament draw only
16
+ the first visible series, one slice (or group of seats) per category; histogram and box read
17
+ every value of the visible series. A slope chart joins the first series (before) to the
18
+ second (after) for each category; a Likert chart reads each category as a question and the
19
+ series as its answers, ordered from most negative to most positive.
20
+
21
+ Grouped data lives in `spec.tree` (items `{name, value}`, groups `{name, children}`, up to 6
22
+ levels). When a tree is set it *is* the data: the categories are its top-level group names
23
+ and the first series their totals, so a treemap nests the groups, a sunburst draws them as
24
+ rings, and any other type draws the totals. Nodes are addressed by `path`, the names from the
25
+ top down (`["Europe", "France"]`); `get_chart` returns them as `treeTable`.
17
26
 
18
27
  Read `pixerate-agent-tools` first for tool naming, errors and `run_commands` batching.
19
28
  Charts inside a rich-text document are not edited with these tools: use `insert_chart`,
@@ -34,10 +43,15 @@ Charts inside a rich-text document are not edited with these tools: use `insert_
34
43
  4. **Style in one call.** Put type, titles, axes, legend, stacking and labels in a single
35
44
  `update_chart`; it is all-or-nothing and one undo step. Use `apply_chart_preset` for a
36
45
  coherent layout or colour scheme, `update_chart_series` for per-series settings.
37
- 5. **Check.** Read the `warnings` that `get_chart` and `render_chart` return (hidden or
46
+ 5. **Point the reader at the message.** Once the data and style are right, annotate:
47
+ `add_chart_reference_line` for a target or average, `add_chart_reference_band` for a
48
+ normal or target range, `set_chart_callout` on the one or two points the title talks
49
+ about, `highlight_chart` to mute what does not matter, and `note` (in `update_chart`) for
50
+ the source.
51
+ 6. **Check.** Read the `warnings` that `get_chart` and `render_chart` return (hidden or
38
52
  thinned labels, crowded legend, skipped values) and run `check_chart_colors`. Fix every
39
53
  problem.
40
- 6. **Look once.** `render_chart` with `format: "png"` after a batch of edits, at the size
54
+ 7. **Look once.** `render_chart` with `format: "png"` after a batch of edits, at the size
41
55
  the chart will be shown, to check overlap and legibility. Use `undo_chart` for your own
42
56
  mistakes.
43
57
 
@@ -49,7 +63,15 @@ Charts inside a rich-text document are not edited with these tools: use `insert_
49
63
  stacked bar or `stacking: "percent"` for shares across groups; pie or donut only for one
50
64
  whole with few parts. Distribution: histogram (`bins`, default the square root of n) or
51
65
  box. Relationship: scatter with `series.x`, bubble with `sizes` for a third measure.
52
- Running totals and bridges: waterfall (`showTotal`). Staged drop-off: funnel.
66
+ Running totals and bridges: waterfall (`showTotal`). Staged drop-off: funnel. What rose
67
+ and what fell between two points in time: slope (two series, the names are the column
68
+ captions). Agree-to-disagree survey answers: likert (`likertSort: "positive"` ranks the
69
+ questions; `likertNeutral: "separate"` keeps the middle answer out of the comparison).
70
+ Parts of parts (regions → countries, budget → departments → teams): a treemap with grouped
71
+ data for comparing sizes, a sunburst for showing the levels (`sunburstLabel` captions the
72
+ total in the middle). Load the levels with `import_chart_tree_csv` or `set_chart_tree`.
73
+ Seats or votes per group with a majority: parliament (`parliamentMajority`,
74
+ `parliamentArc`, `parliamentLabel`).
53
75
  - **Bars start at zero.** Bar length encodes the value, so never set a non-zero `min` on the
54
76
  value axis of a bar, column or area chart. Line and scatter charts may zoom to the data
55
77
  range when the change, not the magnitude, is the point.
@@ -81,6 +103,19 @@ Charts inside a rich-text document are not edited with these tools: use `insert_
81
103
  (`showR2`) and read it with `get_chart_trendline` before describing the trend; label any
82
104
  `forward` forecast as a projection. Exponential, power and log fits need positive values;
83
105
  category charts fit against x = 1, 2, 3.
106
+ - **Annotate sparingly.** One or two reference lines, one band and a couple of callouts at
107
+ most; every label competes with the data. Label every reference line (`label`) so the
108
+ reader knows what it is, and keep callouts to a few words that say *why* the point
109
+ matters ("Launch", "Strike"), not its value (that is what data labels are for).
110
+ - **Highlight instead of recolouring.** To show one bar or series against the rest, use
111
+ `highlight_chart` (it mutes the rest and keeps the palette) rather than greying series by
112
+ hand. Name categories to pick bars or points, series to pick whole lines. Clear it with
113
+ `names: []`.
114
+ - **One series, many categories:** `varyColors: true` gives each bar its own palette colour
115
+ and a category legend. Use it only when colour carries meaning (brands, teams); a ranked
116
+ bar chart reads better in one colour.
117
+ - **Cite the source** in `note` ("Source: ONS, 2025"); it sits under the chart, apart from
118
+ the title.
84
119
  - **Error bars:** say what they are (standard deviation, standard error, confidence) in the
85
120
  title or subtitle.
86
121
 
@@ -109,7 +144,7 @@ it changed.
109
144
 
110
145
  | Tool | Use it to |
111
146
  | --- | --- |
112
- | `update_chart` | Change presentation in one all-or-nothing call: `type`, `title`, `subtitle`, `xAxis` / `yAxis` / `yAxis2` (show, title, min, max, step, gridlines, format, kind `"date"`, scale `"log"`, logBase, reverse), `legend` (show, position top / bottom / left / right), `stacking` (none / stacked / percent), `orientation`, `curve`, `points`, `dataLabels`, `labelPosition`, `palette`, `innerRadius` (donut hole, 0-0.9), `gapWidth`, `overlap`, `bins`, `showTotal`, `radarTicks` (radar ring count 1-50, null for round numbers), `radarGraticule` (radar rings as `polygon` or `circle`). Invalid values change nothing and come back as sentences. Returns the spec. |
147
+ | `update_chart` | Change presentation in one all-or-nothing call: `type`, `title`, `subtitle`, `xAxis` / `yAxis` / `yAxis2` (show, title, min, max, step, gridlines, format, kind `"date"`, scale `"log"`, logBase, reverse), `legend` (show, position top / bottom / left / right), `stacking` (none / stacked / percent), `orientation`, `curve`, `points`, `dataLabels`, `labelPosition`, `palette`, `innerRadius` (donut hole, 0-0.9), `gapWidth`, `overlap`, `bins`, `showTotal`, `radarTicks` (radar ring count 1-50, null for round numbers), `radarGraticule` (radar rings as `polygon` or `circle`), `note` (footnote, "" for none), `highlight` (whole list of names), `referenceLines` / `referenceBands` (whole lists; prefer the add tools), `varyColors` (one colour per category on a one-series chart), `likertNeutral` (`split` / `separate`), `likertSort` (`none` / `positive` / `negative`), `parliamentArc` (90-360 degrees), `parliamentMajority` (seats, null for half plus one), `parliamentLabel` (caption under the total), `sunburstLabel` (caption under a sunburst's total). Invalid values change nothing and come back as sentences. Returns the spec. |
113
148
  | `set_chart_data` | Replace `categories` and / or all `series` (each with `name`, `values` and optional settings). Values are padded with gaps to the categories. Use for whole tables and for re-sorting. Returns the table and series ids. |
114
149
  | `set_chart_values` | Set individual `cells` (`series`, `category`, `value`) all-or-nothing. Values may be numbers, numeric text (`"1,200"`, `"12%"`, `"$40"`, `"(3)"`) or `null` for a gap. Returns the stored values. Acted out with a cursor when the host has a director. |
115
150
  | `add_chart_series` | Add a series (`name` required; `values`, `color`, `type` for a combo, `axis: "secondary"`, `x` / `sizes` for scatter and bubble, marker, format, error bars, `id`, `at`). Selects it and returns its id. |
@@ -123,6 +158,19 @@ it changed.
123
158
  | `load_file_chart` | Replace the editor's chart with one from an Office file (`file` or `data`, `chartId`). Returns the spec, read problems and the features the editor could not keep; tell the user about any. |
124
159
  | `add_chart_trendline` | Fit a trendline to a series (`type`: linear, exponential, logarithmic, power, polynomial with `order` 2-6, moving-average with `period`; `forward` / `backward` in x units, `intercept`, `showEquation`, `showR2`). Unstacked bar, line, area, scatter and bubble charts only. Returns its index, coefficients, R2 and equation. |
125
160
  | `remove_chart_trendline` | Delete a trendline by `series` and `index`. |
161
+ | `add_chart_reference_line` | Draw a dashed line across the plot at `value` (a target, floor or average) with an optional `label`, `color` and `axis` (`y` default, `y2` for the secondary axis, `x` for numeric x on scatter and bubble). Bar, line, area, scatter and bubble charts; the axis grows to keep it in view. Returns its `index`. |
162
+ | `update_chart_reference_line` | Change a reference line by `index` (from `add_chart_reference_line` or `spec.referenceLines`); `null` removes the label, colour or axis. Returns the line as stored. |
163
+ | `remove_chart_reference_line` | Delete a reference line by `index`; later ones move up. Returns the rest. |
164
+ | `add_chart_reference_band` | Shade a range from `from` to `to` (a target or normal range) under the data, with an optional `label`, `color` and `axis`. Same charts as reference lines. Returns its `index`. |
165
+ | `update_chart_reference_band` | Change a reference band by `index`; `null` removes the label, colour or axis. |
166
+ | `remove_chart_reference_band` | Delete a reference band by `index`. Returns the rest. |
167
+ | `set_chart_callout` | Pin a short `label` to one bar or point (`series`, `category`) with a leader line; `label: null` removes it. The point needs a value. Bar, line, area, scatter and bubble charts. Selects the point. |
168
+ | `highlight_chart` | Keep the named categories and series (`names`) bright and mute everything else; `[]` clears it. Unknown names change nothing and are listed. Returns the matched category indexes and series ids. |
169
+ | `set_chart_tree` | Replace the data with grouped data (`tree`: items `{name, value}`, groups `{name, children}`, up to 6 levels, sibling names unique) for grouped treemaps and sunbursts; `[]` goes back to plain data and keeps the totals. Returns the tree, group totals and depth. |
170
+ | `import_chart_tree_csv` | Replace the data with grouped data from CSV / TSV: names in every column but the last (group, ..., item), the value last; a blank group name repeats the one above. The quickest way to load a spreadsheet's hierarchy. |
171
+ | `add_chart_tree_node` | Add an item or a group (`node`) under the group at `parent` (a path; `[]` for the top level) at `at`. Returns its `path` and the parent's new total. |
172
+ | `update_chart_tree_node` | Rename a node, set an item's `value`, or set a node's `color` (null for the palette) by `path`. Groups have no value of their own. Returns the path (new after a rename). |
173
+ | `remove_chart_tree_node` | Delete a node and everything under it by `path`. Returns the group totals left. |
126
174
  | `apply_chart_preset` | Apply a quick `layout` and / or a `colors` scheme from `list_chart_presets` in one step. |
127
175
  | `transpose_chart` | Swap series and categories. Drops per-series colours and settings, so restyle afterwards. |
128
176
  | `select_chart_element` | Select part of the chart (`kind`: none, chart, title, subtitle, legend, axis, series, point, category, trendline) for the user to see or before `press_chart_key`. |
@@ -166,6 +214,19 @@ several charts may register them with another prefix.
166
214
  | `chart.removeCategory` | Delete a category by index or label. |
167
215
  | `chart.addTrendline` | Fit a trendline (as `add_chart_trendline`). |
168
216
  | `chart.removeTrendline` | Delete a trendline. |
217
+ | `chart.addReferenceLine` | Add a reference line (as `add_chart_reference_line`). |
218
+ | `chart.updateReferenceLine` | Change a reference line by index. |
219
+ | `chart.removeReferenceLine` | Delete a reference line by index. |
220
+ | `chart.addReferenceBand` | Add a reference band (as `add_chart_reference_band`). |
221
+ | `chart.updateReferenceBand` | Change a reference band by index. |
222
+ | `chart.removeReferenceBand` | Delete a reference band by index. |
223
+ | `chart.setCallout` | Pin or remove a point's callout (as `set_chart_callout`). |
224
+ | `chart.highlight` | Highlight categories and series (as `highlight_chart`). |
225
+ | `chart.setTree` | Replace the grouped data (as `set_chart_tree`). |
226
+ | `chart.importTreeCsv` | Replace the data with grouped data from CSV / TSV. |
227
+ | `chart.addTreeNode` | Add an item or group; returns its path. |
228
+ | `chart.updateTreeNode` | Rename, revalue or recolour a node by path. |
229
+ | `chart.removeTreeNode` | Delete a node by path. |
169
230
  | `chart.applyPreset` | Apply a quick layout and / or colour scheme. |
170
231
  | `chart.transpose` | Swap series and categories. |
171
232
  | `chart.importCsv` | Replace data from CSV / TSV (no automatic chart type). |
@@ -190,6 +251,28 @@ several charts may register them with another prefix.
190
251
  - Percent-stacked charts, pies and gaps cannot be dragged; use `set_chart_values`.
191
252
  - Radar needs at least three categories (it draws as a line chart until then); trendlines
192
253
  are refused on stacked charts, pies and the other single-series families.
254
+ - Likert answers must be entered in order, most negative first: the chart puts the first
255
+ half left of the centre line and colours them orange, the last half right in purple.
256
+ Values are counts or shares; each question is scaled to its own total.
257
+ - A slope chart needs exactly two visible series; with one it draws a line chart (a
258
+ `type-fallback` warning), and a third is not drawn. Hide the extra series or move them.
259
+ - Parliament seats are rounded to whole numbers; more than 2,000 seats are not drawn.
260
+ - While a chart has grouped data, cell edits (`set_chart_values`, adding, renaming or deleting
261
+ categories, `transpose_chart`) are refused, because the totals come from the tree. Edit the
262
+ nodes, or replace the data (`set_chart_data`, `import_chart_csv`), which clears the tree.
263
+ - Highlight, selection and colours work per top-level group: every item in a group selects
264
+ and highlights that group. Give a node its own `color` with `update_chart_tree_node`.
265
+ - Office export writes a grouped treemap or sunburst as a pie or doughnut of the group totals
266
+ (DrawingML has no hierarchy); Office chartex imports keep the levels.
267
+ - Reference lines, bands and callouts are drawn on bar, line, area, scatter and bubble
268
+ charts only. Switching to another type keeps them in the spec but leaves them out of the
269
+ drawing (an `annotations-skipped` warning says so); they come back when you switch back.
270
+ - Reference values on a log axis must be above 0, and `axis: "y2"` needs a visible series on
271
+ the secondary axis. A line outside a fixed `min` / `max` is not drawn.
272
+ - Callouts and highlight names follow the data: removing a category drops its callouts and
273
+ its highlight entry, renaming a category or series keeps it highlighted.
274
+ - Office export has no reference lines, bands, callouts, highlight or note; `export_chart_ooxml`
275
+ lists them as not written. `varyColors` is kept.
193
276
  - Radar `radarTicks` and `radarGraticule` are Mermaid's `ticks` and `graticule`. A
194
277
  `radar-beta` block with no `graticule` line imports as `circle`, because that is what
195
278
  Mermaid draws; set `radarGraticule: "polygon"` if the user wants the editor's default look.
@@ -420,8 +420,6 @@ for the controls and presets.
420
420
  | `export_psd` | Export a PSD with every layer, the paper as a locked bottom layer, and the composite. Pigment and painterly modes are written as their nearest standard mode (`list_blend_modes` `psd`). Returns base64, size, `psb` and `warnings`. |
421
421
  | `export_time_lapse` | Repaint the painting from its recorded strokes and events as a time-lapse: `frames` (about how many), `fps`, `maxSide`, `wetStepsPerFrame` (frames while wet paint flows), `holdMs`. Returns a contact sheet image, the frame count, size, duration, a video project of the frames and the ffmpeg command that renders `timelapse.mp4`; `includeFrames` adds every frame as base64 PNG. Changes nothing. |
422
422
  | `export_high_res` | Repaint the painting from its strokes at `scale` 1..8 (brushes, grain, fills and wet events scaled): a PNG with real detail instead of upscaled pixels. Returns base64, size. Layers not painted with strokes come out empty. |
423
- | `save_document` | Deprecated, kept for one minor version: use `get_paint_document` or `save_paint_document`. Returns the document JSON with PNG data URLs inline (large). |
424
- | `open_document` | Deprecated, kept for one minor version: use `load_paint_document` or `open_saved_paint_document`. Opens a document JSON from `save_document`, clearing the undo history. |
425
423
 
426
424
  ### Save and open
427
425
 
@@ -0,0 +1,91 @@
1
+ ---
2
+ name: pixerate-typography
3
+ description: How to choose and load fonts, build resilient font-family fallback stacks, generate modular type scales and heading hierarchies, compute fluid clamp() sizes, audit body text for line length and line spacing, check text contrast with the real font size and weight, split text into letters, words or lines for animation, and drive a font picker. Use when asked to pick or pair fonts, set up type tokens, check readability or legibility, or animate live text.
4
+ ---
5
+
6
+ # Typography tools
7
+
8
+ The typography module is DOM-free: it works on numbers (px sizes, unitless line-heights, em
9
+ tracking) and family names, never on rendered pixels. Fonts come from a **font provider** the
10
+ host supplies (installed system fonts, a web font service, a design system's families); by
11
+ default it is a catalog of common system fonts, which says what is *usually* installed, not
12
+ what this machine has.
13
+
14
+ ## Mental model
15
+
16
+ - A **font spec** is `{ family, weight, style, size (px), lineHeight (unitless), letterSpacing (em) }`.
17
+ - A **type scale** multiplies a base size by a ratio per step (`16 × 1.25² = 25`). A **hierarchy**
18
+ names the steps: `caption` (−2), `body-sm`, `body` (0), `h4`, `h3`, `h2`, `h1`, `display` (5).
19
+ - A **fallback stack** is the `font-family` value: your family, then a tested cross-platform stack
20
+ for its category, ending in a generic family, so text still renders when the font is missing.
21
+ - **Large text** under WCAG 2 is 24px, or 18.67px (14pt) at weight 700+. It needs 3:1 contrast
22
+ for AA instead of 4.5:1. The accessibility tool decides this from the spec for you.
23
+
24
+ ## Working method
25
+
26
+ 1. **Find fonts.** `list_fonts` with `category`, `weight`, `style` or `query`. Only use families it
27
+ returns (or that the user names), and always ship them inside a stack from `resolve_font_stack`.
28
+ 2. **Load before rendering.** `load_font` before drawing or capturing text in a web font, so the
29
+ first frame does not use the fallback. `loaded: false` means the fallback will show.
30
+ 3. **Set up the scale.** `generate_type_hierarchy` for named tokens and ready CSS custom
31
+ properties; `suggest_type_scale` for arbitrary steps. Smaller ratios (minor/major third) suit
32
+ dense UI; larger ones (perfect fourth/fifth, golden) suit editorial and marketing pages.
33
+ 4. **Make it fluid (optional).** `compute_fluid_type_size` for headings that grow between two
34
+ viewport widths. Bounds are in rem so browser zoom and font-size settings still work.
35
+ 5. **Audit the body text.** `audit_text_readability` with the real text, `fontSize`, `lineHeight`
36
+ and the column `width`: fix `lineLength.status` (45–75 characters) and `lineSpacing`
37
+ (at least 1.5) before polishing anything else.
38
+ 6. **Check contrast with the type.** `check_typography_accessibility` with foreground, background,
39
+ `fontSize` and `fontWeight`. Use `suggestion` when `passesAA` is false. For pure colour work
40
+ (palettes, dual colours) use the `pixerate-color` tools.
41
+ 7. **Animate live text.** `segment_text` splits into `grapheme`, `word` (with `wordsOnly`),
42
+ `sentence` or `line` (with `maxCharacters` to wrap) and returns UTF-16 `start` / `end`
43
+ offsets, so letters and words can be staggered without converting text to outlines.
44
+ 8. **Drive the on-screen picker.** When a font picker is mounted, `get_font_selection` shows what
45
+ is chosen, `list_picker_fonts` what it offers, and `set_font_selection` changes any fields in
46
+ one step and returns the new selection.
47
+
48
+ ## Best practices
49
+
50
+ - **Two families are usually enough**: one for text, optionally one for headings. Pair by contrast
51
+ of category (serif headings with sans text, or the reverse), not by two similar sans-serifs.
52
+ - **Body text at 16px or more**, line-height 1.5–1.7, about 66 characters per line
53
+ (`max-width: 66ch`). Headings get tighter leading (1.1–1.3) and slightly negative tracking; small
54
+ captions slightly positive tracking. The scale tools already recommend these.
55
+ - **Use weight 400+ below 24px.** Thin weights lower effective contrast even when the ratio passes.
56
+ - **Avoid long runs of capitals** for body text; keep all-caps to short labels.
57
+ - **Prefer rem and clamp()** over fixed px for type on the web, so user settings apply.
58
+ - **Never assume a font is installed.** A system font in the catalog may be missing on Linux or
59
+ Android; the fallback stack is what makes that safe.
60
+
61
+ ## Tool reference
62
+
63
+ | Tool | When to use | Returns |
64
+ | --- | --- | --- |
65
+ | `list_fonts` | Discover families the host offers; filter by `category`, `source`, `weight`, `style`, `query`; `limit` default 50 | `{ fonts: [{ family, category, weights, styles, isVariable?, source }], total, truncated }` |
66
+ | `resolve_font_stack` | Turn a family (or stack) into a resilient `font-family` value for its category; also says if the provider lists it | `{ stack, families, category, available }` |
67
+ | `suggest_type_scale` | Sizes for arbitrary integer `steps` (-12…12) from `baseSize` (px, default 16) and `ratio` (name or number, default major-third) | `{ ratio, baseSize, steps: [{ name, step, size, lineHeight, letterSpacing }] }` |
68
+ | `generate_type_hierarchy` | Named tokens caption…display on one scale, with CSS custom properties (`prefix`, `selector`) | `{ ratio, ratioName?, baseSize, steps: { body, h1, … }, css }` |
69
+ | `compute_fluid_type_size` | A size that grows from `minSize` at `minViewport` to `maxSize` at `maxViewport` (all px) | `{ clampCss, slope, intercept, samples }` |
70
+ | `audit_text_readability` | Check body text: line length (own lines, or `width` + `fontSize`), line spacing, capitals, tiny or tight type, reading time | `{ lineLength, lineSpacing, allCaps, wordCount, estimatedReadingTimeSeconds, issues }` |
71
+ | `check_typography_accessibility` | Contrast for text of a given `fontSize` (px) and `fontWeight`, deciding large text itself; suggests a passing foreground | `{ ratio, large, level, passesAA, passesAAA, required, suggestion?, largeTextMinSize, issues }` |
72
+ | `segment_text` | Split text into graphemes, words, sentences or lines with offsets, for staggered animation | `{ segments: [{ text, start, end, index, isWordLike? }], count }` |
73
+ | `load_font` | Load a family's face (`weight`, `style`) before rendering or capturing; only listed when the provider can load | `{ family, weight, style, loaded }` |
74
+ | `get_font_selection` | Read the mounted font picker's value, CSS and the chosen family's weights and styles | `{ value, css, availableWeights, availableStyles }` |
75
+ | `list_picker_fonts` | Families the mounted picker offers (by `category`, `query`), plus recent families | `{ fonts, recent, total, truncated }` |
76
+ | `set_font_selection` | Change the picker's family, weight, style, size, lineHeight or letterSpacing in one validated step; weights snap to what the family has | `{ value, css, availableWeights, availableStyles }` |
77
+
78
+ ## Pitfalls
79
+
80
+ - **Sizes are CSS px.** 12pt is 16px; 18pt is 24px. Convert before calling the tools.
81
+ - **lineHeight is a multiplier**, not px: 24px leading on 16px text is `1.5`. When it is omitted
82
+ the audit assumes the browser's `normal` (about 1.2) and says so with `assumed: true`.
83
+ - **`line` segmentation does not know the renderer's wrapping.** Without `maxCharacters` it only
84
+ splits on line breaks; with it, it wraps by character count, which approximates proportional
85
+ fonts.
86
+ - **A one-line string is not a measure.** `too-short` on a single heading is expected; pass the
87
+ column `width` and `fontSize` to estimate how paragraphs will wrap.
88
+ - **The system catalog is not detection.** Without the host opting into Local Font Access,
89
+ `list_fonts` lists commonly installed fonts. Always use `resolve_font_stack` output.
90
+ - **Picker fonts may differ from `list_fonts`.** `set_font_selection` only accepts families the
91
+ picker lists; check with `list_picker_fonts`.