@pixerate/editor 0.256.0 → 0.258.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/dist/agent/index.cjs +40 -38
  2. package/dist/agent/index.js +9 -7
  3. package/dist/animation/index.cjs +4 -2
  4. package/dist/animation/index.js +4 -2
  5. package/dist/animation-editor/index.cjs +63 -61
  6. package/dist/animation-editor/index.js +7 -5
  7. package/dist/brushes/colour.d.cts +11 -0
  8. package/dist/brushes/colour.d.ts +11 -0
  9. package/dist/brushes/index.cjs +51 -30
  10. package/dist/brushes/index.d.cts +1 -0
  11. package/dist/brushes/index.d.ts +1 -0
  12. package/dist/brushes/index.js +25 -4
  13. package/dist/brushes/resolve.d.cts +2 -0
  14. package/dist/brushes/resolve.d.ts +2 -0
  15. package/dist/brushes/schema.d.cts +1 -0
  16. package/dist/brushes/schema.d.ts +1 -0
  17. package/dist/brushes/tools.d.cts +7 -0
  18. package/dist/brushes/tools.d.ts +7 -0
  19. package/dist/brushes/types.d.cts +10 -2
  20. package/dist/brushes/types.d.ts +10 -2
  21. package/dist/brushes/ui.d.cts +14 -3
  22. package/dist/brushes/ui.d.ts +14 -3
  23. package/dist/{brushstrokeRender-HCOQUSWW.js → brushstrokeRender-JI4W6F2G.js} +2 -2
  24. package/dist/{brushstrokeRender-ZZOXI5O5.cjs → brushstrokeRender-MQ6PSDM5.cjs} +11 -11
  25. package/dist/canvas/index.cjs +20 -18
  26. package/dist/canvas/index.js +7 -5
  27. package/dist/chart/index.cjs +159 -157
  28. package/dist/chart/index.js +9 -7
  29. package/dist/{chunk-7272D3K2.js → chunk-2EUW2XZX.js} +27 -276
  30. package/dist/{chunk-L6Y7TSQ4.js → chunk-2VJ6OVJV.js} +1 -1
  31. package/dist/{chunk-6ZZKRH7R.js → chunk-47V3WXDG.js} +2 -2
  32. package/dist/{chunk-H7ET2ISK.cjs → chunk-4IMD2TLY.cjs} +52 -52
  33. package/dist/{chunk-VUKUTT2W.js → chunk-4M4E3N6X.js} +1 -1
  34. package/dist/{chunk-TBAVCLEQ.cjs → chunk-5Q5MLXAD.cjs} +23 -23
  35. package/dist/{chunk-MONXNVZK.js → chunk-6I23ZCGK.js} +5 -3
  36. package/dist/{chunk-GOBOCZZI.js → chunk-6NQPKNUZ.js} +1 -1
  37. package/dist/{chunk-WYGSR3IJ.js → chunk-AORZ5M5C.js} +1 -1
  38. package/dist/chunk-AVTPZO4Z.cjs +142 -0
  39. package/dist/{chunk-WKMUBATU.js → chunk-C2WEIEUB.js} +1 -1
  40. package/dist/{chunk-P3K2JCJA.js → chunk-D5BXSOSU.js} +3 -3
  41. package/dist/chunk-E2V5DA3W.js +36 -0
  42. package/dist/{chunk-WZOGDKCQ.js → chunk-F7CPSRLN.js} +0 -133
  43. package/dist/chunk-FJBQTZS4.js +17 -0
  44. package/dist/{chunk-SOSBOS5T.cjs → chunk-G23BTQA5.cjs} +22 -22
  45. package/dist/{chunk-EUIDTZNQ.cjs → chunk-I3Y2Q3PM.cjs} +59 -308
  46. package/dist/{chunk-YHUMGBZD.js → chunk-ICTPLVSR.js} +22 -4
  47. package/dist/{chunk-LRNUZKWV.cjs → chunk-IPUTIUGQ.cjs} +1 -1
  48. package/dist/{chunk-6GRUVGKU.js → chunk-ITURMC2M.js} +3 -3
  49. package/dist/{chunk-HYWRU5VX.js → chunk-IXQZHCOF.js} +1 -1
  50. package/dist/chunk-J5UQHWVL.js +142 -0
  51. package/dist/{chunk-YBAPT7NL.js → chunk-JC2SHA4A.js} +1 -23
  52. package/dist/chunk-LRTR4ASZ.cjs +17 -0
  53. package/dist/{chunk-HUAISCUX.cjs → chunk-M2HG5DJ2.cjs} +18 -18
  54. package/dist/{chunk-N76AMOKE.cjs → chunk-MDLEGVEN.cjs} +42 -42
  55. package/dist/chunk-MEGSQUQO.cjs +36 -0
  56. package/dist/chunk-MLLTWAXH.cjs +276 -0
  57. package/dist/{chunk-RHPSE2YR.js → chunk-MQ6MGIQM.js} +2 -2
  58. package/dist/{chunk-PGPFWC35.cjs → chunk-MQGQTKVW.cjs} +51 -33
  59. package/dist/{chunk-5YWWQ36H.cjs → chunk-O3ZVZSNJ.cjs} +9 -9
  60. package/dist/{chunk-GCTUOTCW.cjs → chunk-P3PZ63M3.cjs} +35 -35
  61. package/dist/{chunk-WRZ6C4TU.cjs → chunk-P6WYDBTH.cjs} +4 -4
  62. package/dist/{chunk-CJ6TBL2N.cjs → chunk-RHEMMGCN.cjs} +16 -16
  63. package/dist/{chunk-HIMGBZAM.cjs → chunk-RSIKDWOP.cjs} +201 -38
  64. package/dist/{chunk-SABJMH6M.cjs → chunk-RVGP2XLF.cjs} +2 -135
  65. package/dist/{chunk-JVRLGV3C.js → chunk-SMTUQWGY.js} +184 -21
  66. package/dist/{chunk-DMTEQ6SH.js → chunk-SSFMWJDK.js} +4 -4
  67. package/dist/{chunk-TFF2GTGN.cjs → chunk-SXQHWXEL.cjs} +2 -24
  68. package/dist/{chunk-IWEVV63O.cjs → chunk-UEYLANO3.cjs} +18 -16
  69. package/dist/chunk-VR6OGHU5.js +276 -0
  70. package/dist/{chunk-AF23QK5F.js → chunk-YQ4PQTBB.js} +1 -1
  71. package/dist/{chunk-B7V34GIH.cjs → chunk-YSGW7VV4.cjs} +88 -88
  72. package/dist/{chunk-3NS5C7FB.cjs → chunk-YYBW6DF2.cjs} +76 -76
  73. package/dist/{chunk-UFEDHACQ.js → chunk-Z7BX4TWO.js} +2 -2
  74. package/dist/{chunk-RUONPPMC.cjs → chunk-ZSE25IZA.cjs} +8 -8
  75. package/dist/color/index.cjs +7 -3
  76. package/dist/color/index.js +18 -14
  77. package/dist/export/index.cjs +8 -6
  78. package/dist/export/index.js +7 -5
  79. package/dist/extensions/index.cjs +11 -9
  80. package/dist/extensions/index.js +10 -8
  81. package/dist/gradient/index.cjs +33 -31
  82. package/dist/gradient/index.js +5 -3
  83. package/dist/image-editor/brush-presets.d.cts +4 -1
  84. package/dist/image-editor/brush-presets.d.ts +4 -1
  85. package/dist/image-editor/controller.d.cts +10 -5
  86. package/dist/image-editor/controller.d.ts +10 -5
  87. package/dist/image-editor/index.cjs +1090 -1073
  88. package/dist/image-editor/index.js +37 -20
  89. package/dist/index.cjs +17 -15
  90. package/dist/index.js +11 -9
  91. package/dist/paint-editor/brushes.d.cts +18 -1
  92. package/dist/paint-editor/brushes.d.ts +18 -1
  93. package/dist/paint-editor/collaboration.d.cts +38 -0
  94. package/dist/paint-editor/collaboration.d.ts +38 -0
  95. package/dist/paint-editor/controller.d.cts +60 -0
  96. package/dist/paint-editor/controller.d.ts +60 -0
  97. package/dist/paint-editor/engine.d.cts +7 -0
  98. package/dist/paint-editor/engine.d.ts +7 -0
  99. package/dist/paint-editor/index.cjs +1564 -1324
  100. package/dist/paint-editor/index.js +1198 -958
  101. package/dist/paint-editor/mark-settings.d.cts +1 -1
  102. package/dist/paint-editor/mark-settings.d.ts +1 -1
  103. package/dist/paint-editor/procedural.d.cts +4 -2
  104. package/dist/paint-editor/procedural.d.ts +4 -2
  105. package/dist/paint-editor/stamp-tip.d.cts +45 -0
  106. package/dist/paint-editor/stamp-tip.d.ts +45 -0
  107. package/dist/paint-editor/stroke.d.cts +4 -0
  108. package/dist/paint-editor/stroke.d.ts +4 -0
  109. package/dist/stickers/index.cjs +4 -2
  110. package/dist/stickers/index.js +6 -4
  111. package/dist/typography/index.cjs +3 -2
  112. package/dist/typography/index.js +2 -1
  113. package/dist/vector/index.cjs +971 -969
  114. package/dist/vector/index.js +8 -6
  115. package/dist/video-editor/index.cjs +4 -2
  116. package/dist/video-editor/index.js +4 -2
  117. package/package.json +1 -1
  118. package/skills/pixerate-brushes/SKILL.md +21 -11
  119. package/skills/pixerate-brushes-design/SKILL.md +6 -2
  120. package/skills/pixerate-brushes-design/evals/canvas-chalk.json +8 -8
  121. package/skills/pixerate-canvas/SKILL.md +3 -2
  122. package/skills/pixerate-image-editor/SKILL.md +1 -1
  123. package/skills/pixerate-paint-editor/SKILL.md +5 -1
  124. package/skills/pixerate-vector/SKILL.md +1 -1
@@ -26,7 +26,7 @@ import {
26
26
  traceField,
27
27
  traceVectorImage,
28
28
  vectorTraceContours
29
- } from "../chunk-VUKUTT2W.js";
29
+ } from "../chunk-4M4E3N6X.js";
30
30
  import {
31
31
  applyHomography,
32
32
  homographyFromPoints,
@@ -53,7 +53,7 @@ import {
53
53
  reverseMorphContour,
54
54
  subpathToMorphContour,
55
55
  turnMorphContour
56
- } from "../chunk-HYWRU5VX.js";
56
+ } from "../chunk-IXQZHCOF.js";
57
57
  import {
58
58
  DEFAULT_VECTOR_FONT,
59
59
  IDENTITY_MATRIX,
@@ -528,10 +528,10 @@ import {
528
528
  withVectorStrokes,
529
529
  withVectorTargetPaint,
530
530
  zigzagPaths
531
- } from "../chunk-AF23QK5F.js";
531
+ } from "../chunk-YQ4PQTBB.js";
532
532
  import {
533
533
  snapshotBrushLibrary
534
- } from "../chunk-JVRLGV3C.js";
534
+ } from "../chunk-SMTUQWGY.js";
535
535
  import "../chunk-APX63ILU.js";
536
536
  import {
537
537
  createProvenance
@@ -599,8 +599,10 @@ import "../chunk-M2TPMYJE.js";
599
599
  import {
600
600
  hexToRgba
601
601
  } from "../chunk-2WPNAGHI.js";
602
- import "../chunk-7272D3K2.js";
603
- import "../chunk-YBAPT7NL.js";
602
+ import "../chunk-2EUW2XZX.js";
603
+ import "../chunk-E2V5DA3W.js";
604
+ import "../chunk-VR6OGHU5.js";
605
+ import "../chunk-JC2SHA4A.js";
604
606
  import "../chunk-MHLU6GRJ.js";
605
607
  import "../chunk-6ONMQ6WF.js";
606
608
 
@@ -189,8 +189,10 @@ var _chunkM5GLQQWGcjs = require('../chunk-M5GLQQWG.cjs');
189
189
  var _chunkDWJJI6RTcjs = require('../chunk-DWJJI6RT.cjs');
190
190
  require('../chunk-4ZC5QJE2.cjs');
191
191
  require('../chunk-A6QWKBJP.cjs');
192
- require('../chunk-EUIDTZNQ.cjs');
193
- require('../chunk-TFF2GTGN.cjs');
192
+ require('../chunk-I3Y2Q3PM.cjs');
193
+ require('../chunk-MEGSQUQO.cjs');
194
+ require('../chunk-MLLTWAXH.cjs');
195
+ require('../chunk-SXQHWXEL.cjs');
194
196
  require('../chunk-5UEFBM2M.cjs');
195
197
  require('../chunk-IPJ3TEYR.cjs');
196
198
 
@@ -189,8 +189,10 @@ import {
189
189
  } from "../chunk-5UJHB3BW.js";
190
190
  import "../chunk-M2TPMYJE.js";
191
191
  import "../chunk-2WPNAGHI.js";
192
- import "../chunk-7272D3K2.js";
193
- import "../chunk-YBAPT7NL.js";
192
+ import "../chunk-2EUW2XZX.js";
193
+ import "../chunk-E2V5DA3W.js";
194
+ import "../chunk-VR6OGHU5.js";
195
+ import "../chunk-JC2SHA4A.js";
194
196
  import "../chunk-MHLU6GRJ.js";
195
197
  import "../chunk-6ONMQ6WF.js";
196
198
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.256.0",
3
+ "version": "0.258.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -15,20 +15,23 @@ built-in presets (read-only) and the user's brushes, shared by every editor that
15
15
  | Kind | `mark` lays a material; `tool` works on paint already down. |
16
16
  | Medium and variety | Marks: what it lays (`paint`, `ink`, `dry`, `spray`) and what kind (`oil`, `watercolor`, `gouache`, `pencil`, `charcoal`, `airbrush`, `marker`, `highlighter`…). Descriptive, for finding brushes; a wet brush whose variety is `watercolor`, `gouache` or `oil` paints that medium. |
17
17
  | Tool | Tools: `eraser`, `blender`, `smudge`, and on wet paint `water`, `dryer`, `blow`, `blot`, `wet-blend`, `wet-drag`. |
18
- | Tip | `shape` (`round`, `flat`, `chisel` are ellipses set by `roundness` and `angle`; `bristle` has a tuft; `stamp` lays stickers), size in px, hardness, spacing, opacity, flow, blend mode. |
19
- | Marks | `continuous` dabs (default), `particles` (strands, springs, ribbons, sparks) or `objects` (each mark an editable sticker). |
18
+ | Tip | `shape` (`round`, `flat`, `chisel` are ellipses set by `roundness` and `angle`; `bristle` has a tuft; `stamp` takes the shape of `stamps`: an editor stamp `editor:star` or any sticker ref from `find_stickers`), size in px, hardness, spacing, opacity, flow, blend mode. |
19
+ | Marks | `continuous` dabs (default; on a stamp tip every dab is the first stamp's shape, in the paint colour), `scatter` (a spray: droplets across the tip, `density`, `size`), `particles` (strands, springs, ribbons, sparks; textured dots from the stamps) or `objects` (each mark an editable sticker, in its own colours). |
20
20
  | Dynamics | Pen feel: pressure to size and opacity, smoothing, tilt, velocity, jitter, rotation. |
21
21
  | Behaviour | `surface` `dry`, `wet` (`wet` settings) or `thick` (`thick` impasto settings); paper `grain`; `exchange` (pick-up and mix; a `reservoir` makes a Mixer Brush); tool `strength`. |
22
22
  | Light | Light the brush gives off (`amount`), plus suggested layer `spill` and `glow`. |
23
23
  | Motion | Animation after painting (live effect paint, particle `waveSpeed`). |
24
- | Colour | A `default` colour, a `palette` of up to 16, `own` (always its own colour). A gradient default is always painted; a solid one is a suggestion. |
24
+ | Colour | A `default` colour, a `palette` of up to 16, `own` (always its own colour). Each is a hex or `rgba()` colour, a light / dark pair `{ light, dark }`, a gradient or effect paint. A gradient default is always painted; a solid or light / dark default becomes the editor's colour when the brush is chosen (unless the user locked theirs), resolved for the colour scheme. |
25
25
 
26
26
  How media map to facets: a **pencil** or **charcoal** is `dry` with high `grain` and tilt;
27
27
  **ink** is a hard round tip with pressure to size (a pen thins with velocity); **watercolour**
28
28
  and **wet oil** are `paint` on `surface: 'wet'`; **impasto** is `surface: 'thick'`; an
29
- **airbrush** is `spray`, soft and low flow; a **highlighter** is `ink` / `highlighter`, a
30
- multiply chisel tip with a little light and `spill` radius 0; **particle** and **sticker**
31
- brushes are `marks` particles or objects on a `stamp` tip.
29
+ **airbrush** is `spray`, soft and low flow; a **spray can** is `spray` with `marks` scatter; a
30
+ **ballpoint**, **gel pen** or **fineliner** is `ink` with a small hard tip (a fineliner without
31
+ pressure to size); **crayon** and **chalk** are `dry` with high `grain`; a **highlighter** is
32
+ `ink` / `highlighter`, a multiply chisel tip with a little light, `spill` radius 0, fluorescent
33
+ yellow by default; a **stamp brush** is a `stamp` tip with continuous marks; **particle** and
34
+ **sticker** brushes are `marks` particles or objects on a `stamp` tip.
32
35
 
33
36
  ## Host profiles
34
37
 
@@ -74,16 +77,23 @@ drawn.
74
77
  glow stays on the stroke.
75
78
  - Keep `variety` truthful (it is how people find the brush) and add `tags` for the job
76
79
  (`lettering`, `foliage`).
77
- - Use `colour.palette` for suggested colours and `colour.own` only for brushes that must
80
+ - Give a brush a `colour.default` only when the medium has a typical colour (blue ballpoint,
81
+ yellow highlighter): choosing the brush sets it as the user's colour. Use a light / dark pair
82
+ when the right colour depends on the background (white chalk on a dark board, graphite on
83
+ paper), and a `palette` for the other usual colours. `colour.own` only for brushes that must
78
84
  always paint their own (a rainbow marker).
85
+ - Stamp tips paint the sticker's shape in the paint colour; to keep a sticker's own colours, use
86
+ `marks` objects instead.
79
87
 
80
88
  ## Pitfalls
81
89
 
82
90
  - **A facet the host refuses.** `create_brush` and `update_brush` refuse it with the facet and
83
91
  fix; `check_brush` returns `degraded`, the brush with those facets stripped, to start from.
84
92
  - **Ids.** Built-in ids can't be reused or changed; a taken id is refused (update it instead).
85
- - **Stamps.** Particle marks lay only the editor's stamps (`editor:star`…); sticker (`objects`)
86
- marks take any sticker ref from `find_stickers`.
93
+ - **Stamps.** Any marks take any sticker ref from `find_stickers`, or an editor stamp
94
+ (`editor:star`…). A ref the host's sticker library can't load is refused with the closest
95
+ refs. Only the paint editor renders stamp tips and scatter marks; canvas, vector and the image
96
+ editor refuse them (`check_brush` says so).
87
97
  - **Out-of-range values** are refused by the schema (`tip.size` is 1..2500 px); a stored brush
88
98
  is clamped on import and reported in `problems`.
89
99
  - **The paint editor paints from this library.** Its `set_brush` picks a brush by id here and
@@ -101,8 +111,8 @@ drawn.
101
111
  | Tool | Use |
102
112
  | --- | --- |
103
113
  | `list_brush_presets` | Find brushes this host renders: `kind`, `medium`, `variety`, `tool`, `tag`, `text`, `source` (all, builtin, user), `all`, `offset`, `limit`. Returns `brushes` (id, name, builtin, kind, medium, variety or tool, category, engine, tip, size, marks, surface, light) and `total`. Call first. |
104
- | `describe_brush` | One brush by `id`: its full facet `brush`, `builtin`, `engine`, `category` and `check` for this host. Unknown ids are refused with the closest ones. |
105
- | `create_brush` | Save a new brush: `id`, `name`, optional `basedOn`, and the facets (to keep an editor's current brush, pass the `brush` its `get_brush` returns). Normalised and checked for this host; refused brushes save nothing. Returns `id`, `brush`, `engine`, `problems`, `warnings`. |
114
+ | `describe_brush` | One brush by `id`: its full facet `brush` (with its `colour`: default, palette, own), `builtin`, `engine`, `category` and `check` for this host (stamp stickers included). Unknown ids are refused with the closest ones. |
115
+ | `create_brush` | Save a new brush: `id`, `name`, optional `basedOn`, and the facets, `colour` included (to keep an editor's current brush, pass the `brush` its `get_brush` returns). Normalised and checked for this host (and its sticker library, for stamp tips); refused brushes save nothing. Returns `id`, `brush`, `engine`, `problems`, `warnings`. |
106
116
  | `update_brush` | Change a saved brush: facets to merge in, `unset` facets to drop. Refuses built-ins (duplicate first) and changes the host can't render. Returns `brush`, `changed` paths, `problems`, `warnings`. |
107
117
  | `duplicate_brush` | Copy any brush, built-ins included, to a saved brush (`newId` default `<id>-copy`, `name`). Returns the new `id` and `brush`; then `update_brush` it. |
108
118
  | `remove_brush` | Delete a saved brush for good (export it first to keep a copy). Built-ins are refused. Returns `removed`. |
@@ -57,6 +57,9 @@ Ask when you cannot tell from the request; say what you assumed when you can.
57
57
  | A brush that must work in another editor | `check_brush` with that `host`, then `create_brush` from its `degraded` brush under a new id. |
58
58
  | A texture (chalk, charcoal, dry brush) | Raise `behaviour.grain` (0.8..1) and add `dynamics.sizeJitter`; test on rough paper. |
59
59
  | A light brush (neon, glow) | `light.amount`, and paint it on dark paper; a highlighter keeps `light.spill.radius` 0. |
60
+ | A colour that depends on the background (chalk, white ink) | `colour.default` as a light / dark pair `{ light, dark }`: the editor takes the side for its colour scheme when the brush is chosen. |
61
+ | A shape as the tip (stars, leaves, a logo) | `tip.shape` stamp with `stamps` (an editor stamp or a ref from `find_stickers`), continuous marks; it paints in the user's colour. Keep the sticker's colours with `marks` objects. Paint editor only. |
62
+ | A spray can or mist | `basedOn` `spray-can`, or `medium` spray with `marks` scatter (`density`, `size`). Paint editor only. |
60
63
  | A change to a built-in | `duplicate_brush`, then `update_brush` the copy. |
61
64
 
62
65
  ## Recipes
@@ -64,8 +67,9 @@ Ask when you cannot tell from the request; say what you assumed when you can.
64
67
  **A pink highlighter.** `create_brush` `{ id: "pink-highlighter", name: "Pink highlighter",
65
68
  basedOn: "highlighter", colour: { default: "#ff6ec7" } }`, then `check_brush`.
66
69
 
67
- **Chalk for canvas strokes.** On the canvas host, `create_brush` `{ id: "chalk", name: "Chalk",
68
- basedOn: "soft-pastel", variety: "chalk", behaviour: { grain: 1 }, dynamics: { sizeJitter: 0.12 } }`.
70
+ **Chalk on a blackboard, for canvas strokes.** On the canvas host, `create_brush`
71
+ `{ id: "board-chalk", name: "Board chalk", basedOn: "chalk", colour: { default: { light:
72
+ "#334155", dark: "#f8fafc" } } }`: white chalk in dark mode, slate on light paper.
69
73
 
70
74
  **Port a wet brush to vector.** `check_brush` `{ id, host: "vector" }`; `create_brush` from the
71
75
  `degraded` brush with `id` `<id>-vector`; `check_brush` again.
@@ -1,15 +1,15 @@
1
1
  {
2
- "prompt": "I'm drawing brushed strokes on the canvas. Make a soft, grainy chalk brush with the id chalk that the canvas can actually use.",
2
+ "prompt": "I'm drawing brushed strokes on the canvas, on a dark blackboard in dark mode and on paper in light mode. Make a grainy chalk brush with the id board-chalk that the canvas can use: near-white chalk in dark mode, a dark slate on light backgrounds.",
3
3
  "fixture": { "editor": "brushes", "host": "canvas" },
4
4
  "checks": [
5
- { "why": "the canvas can render it", "tool": "check_brush", "args": { "id": "chalk", "host": "canvas" }, "expect": { "ok": true } },
6
- { "why": "a dry chalk", "tool": "describe_brush", "args": { "id": "chalk" }, "expect": { "brush": { "kind": "mark", "medium": "dry", "variety": "chalk" } } },
7
- { "why": "it catches the paper grain strongly", "tool": "describe_brush", "args": { "id": "chalk" }, "expect": { "brush": { "behaviour": { "grain": { "$min": 0.8 } } } } },
8
- { "why": "soft-edged", "tool": "describe_brush", "args": { "id": "chalk" }, "expect": { "brush": { "tip": { "hardness": { "$max": 0.6 } } } } }
5
+ { "why": "the canvas can render it", "tool": "check_brush", "args": { "id": "board-chalk", "host": "canvas" }, "expect": { "ok": true } },
6
+ { "why": "a dry chalk", "tool": "describe_brush", "args": { "id": "board-chalk" }, "expect": { "brush": { "kind": "mark", "medium": "dry", "variety": "chalk" } } },
7
+ { "why": "it catches the paper grain strongly", "tool": "describe_brush", "args": { "id": "board-chalk" }, "expect": { "brush": { "behaviour": { "grain": { "$min": 0.8 } } } } },
8
+ { "why": "its default colour is a light / dark pair: light chalk on dark, dark on light", "tool": "describe_brush", "args": { "id": "board-chalk" }, "expect": { "brush": { "colour": { "default": { "$expr": "typeof v === 'object' && /^#[c-f]/i.test(v.dark) && /^#[0-6]/i.test(v.light)" } } } } }
9
9
  ],
10
10
  "solution": [
11
- { "tool": "list_brush_presets", "args": { "medium": "dry" } },
12
- { "tool": "create_brush", "args": { "id": "chalk", "name": "Chalk", "basedOn": "soft-pastel", "variety": "chalk", "behaviour": { "grain": 1 }, "dynamics": { "sizeJitter": 0.12 } } },
13
- { "tool": "check_brush", "args": { "id": "chalk", "host": "canvas" } }
11
+ { "tool": "list_brush_presets", "args": { "variety": "chalk" } },
12
+ { "tool": "create_brush", "args": { "id": "board-chalk", "name": "Board chalk", "basedOn": "chalk", "colour": { "default": { "light": "#334155", "dark": "#f8fafc" } } } },
13
+ { "tool": "check_brush", "args": { "id": "board-chalk", "host": "canvas" } }
14
14
  ]
15
15
  }
@@ -94,8 +94,9 @@ you to. Withdraw stale ones with `reject_proposal`: when the user changes what a
94
94
  use the preset's own size (`canvas.listBrushes` gives it); pass it only to make the mark finer
95
95
  or bolder.
96
96
  - **Brush character.** Each preset draws with its own character: charcoal and pastel are broad and
97
- grainy (they catch the paper's tooth), pencil is fine and slightly grainy, ink, marker and brush
98
- pen are clean, the airbrush is a soft spray. The grain and jitter are seeded per stroke, so a
97
+ grainy (they catch the paper's tooth), as are crayon and chalk; pencil is fine and slightly
98
+ grainy; ink, marker, brush pen, ballpoint, gel pen and fineliner are clean (the fineliner keeps
99
+ one width), the airbrush is a soft spray. The grain and jitter are seeded per stroke, so a
99
100
  stroke always looks the same, and neighbouring strokes share one sheet of paper. Pick the brush
100
101
  for the look, then the colour; for a sketch, use one or two brushes rather than all of them.
101
102
  - **Annotate with gestures, not points.** To mark up work the way a designer marks up a print,
@@ -314,7 +314,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
314
314
  | `sample_color` | The Eyedropper: read the composite colour at a document point (`size` averages a square; default 1 px) and, unless `setForeground` is false, make it the paint foreground. Use it to match a colour already in the image before painting or retouching. Fails outside the canvas or on transparent pixels. Returns `color` and `foreground`. |
315
315
  | `sample_gradient` | Read the composite along a line (`start` → `end`, document pixels) and fit the fewest gradient stops to it (`tolerance` OKLab ΔE, `maxStops`, `sampleSize` averaging, `snapStep`). Changes nothing. Returns a linear `gradient` (angle of the line, oklab), its `css` and `pointsRead`. Use it to match a sky, a backdrop or a light falloff before painting a gradient or styling one. |
316
316
  | `get_paint` | Read-only. What the brush paints (`paint`), the foreground and background colours, the brush settings, and `brushPreset` (the preset they came from, null once changed by hand). |
317
- | `set_brush_preset` | Paint with a brush from the brush library: `id` from `list_brush_presets` (the image host lists only what it can paint). Sets the paint tool it needs (`brush`, `mixer` for a Mixer Brush, `eraser`), its brush and mixer settings and a brush's own gradient. Refuses wet, thick, particle, sticker and light brushes with the reason. Returns `tool`, `brush`, `mixer`, `paint`, `warnings` (e.g. paper grain, which the image editor paints without). Then `paint_stroke` with that tool. |
317
+ | `set_brush_preset` | Paint with a brush from the brush library: `id` from `list_brush_presets` (the image host lists only what it can paint: pencils, pastels, charcoal, crayon, chalk, markers, ink pens, ballpoint, gel pen, fineliner, the airbrush, the Mixer Brush, erasers). Sets the paint tool it needs (`brush`, `mixer` for a Mixer Brush, `eraser`), its brush and mixer settings, a brush's own gradient, and its default colour as the foreground (`keepColour` keeps yours; `colorScheme` picks the side of a light / dark colour). Refuses wet, thick, particle, sticker and light brushes with the reason. Returns `tool`, `brush`, `mixer`, `paint`, `warnings` (e.g. paper grain, which the image editor paints without). Then `paint_stroke` with that tool. |
318
318
  | `get_mixer_brush` | Read-only. The Mixer Brush settings and what it carries (`reservoir` #rrggbb or null, `amount` left 0..1, `pickup` #rrggbb or null). Call it before a mixer stroke with `loadEachStroke: false`. |
319
319
  | `load_mixer_brush` | Fill the Mixer Brush reservoir with `color` (default the foreground), amount 1; keeps the pickup. View state, not an undo step. Use before strokes with `loadEachStroke: false`. Returns the load. |
320
320
  | `clean_mixer_brush` | Empty the Mixer Brush pickup well (`emptyReservoir: true` also drops the loaded paint, so it only smears). View state, not an undo step. Returns the load. |
@@ -11,6 +11,9 @@ seed: its grain is a height map (peaks and valleys) that grain-responsive brushe
11
11
  is never part of a layer. Coordinates are document pixels, (0, 0) top-left, y down. See
12
12
  `pixerate-agent-tools` for errors, namespacing and batching.
13
13
 
14
+ - **Brush colours.** Choosing a brush with a default colour (highlighter yellow, ballpoint
15
+ blue, chalk light or dark for the colour scheme) makes it the paint colour, unless the colour is
16
+ locked (`set_color` `locked`). Set the colour after choosing the brush, or lock it first.
14
17
  - **Brushes** come from the shared brush library (`pixerate-brushes`: `list_brush_presets`,
15
18
  `create_brush`, `preview_brush`…) and are made of facets: a tip (size, hardness, spacing, angle,
16
19
  roundness, opacity, flow, blend), dynamics (pressure, tilt, velocity, jitter, a **rotation**
@@ -292,7 +295,7 @@ Listing, saving, sharing and previewing brushes are the brush library's tools (`
292
295
  | Tool | Use |
293
296
  | --- | --- |
294
297
  | `mix_colors` | Predict a mix: `colors` with `weight` parts (and optional tinting `strength`), `model` `pigment` (default, Kubelka-Munk: what pigment blends, the Mixer Brush, pigment blending tools and the pad do) or `rgb` (what normal blending and rgb mixing do). Returns `mixed` by the model plus `pigment` and `rgbLerp` for comparison. Changes nothing. Use it to choose stroke colours. |
295
- | `set_color` | Set the default paint `color` (and `secondaryColor`); back to one colour after gradient paint or effect paint. |
298
+ | `set_color` | Set the default paint `color` (and `secondaryColor`); back to one colour after gradient paint or effect paint. `locked: true` keeps it when you choose a brush with a default colour (by default choosing the highlighter makes your colour its yellow). Returns both colours and `locked`. |
296
299
  | `set_paint` | Set the paint: one colour, or gradient paint (`stops`, `space`, `map`, `softness`; see Gradient paint). Ends effect paint. Errors say what is wrong (unsorted stops, a bad mapping). Returns `paint`, `color`, `secondaryColor`. |
297
300
  | `pick_color` | Read the colour at `x`, `y`: of the painting with its paper, or of one layer (with alpha). |
298
301
  | `get_mixer_brush` | Read what the brush carries: the Mixer Brush `reservoir` and `amount` left, and `pickup` (paint picked up, kept by dirty brushes). |
@@ -318,6 +321,7 @@ Listing, saving, sharing and previewing brushes are the brush library's tools (`
318
321
  | --- | --- |
319
322
  | `paint_stroke` | Paint one stroke through `points` (document px; `pressure` 0..1, `t` ms, `tiltX` / `tiltY` or `altitude` / `azimuth`, `twist`) with `brushId` (default the active brush), optional per-stroke `brush` facet changes (as `set_brush` takes), `color` or `paint` (a gradient for this stroke), `layerId`, `seed`, `snapToPerspective`. The symmetry guide adds its copies; a sampling tracing image supplies the colour when `color` is missing. Returns `layerId`, `bounds`, `dabs`, `strokeId` and, for brushes that pick up paint, `mixerLoad` (what the brush carries now). Refused on locked, hidden or group layers, with the fix in the message. |
320
323
  | `list_strokes` | Read the recorded strokes, oldest first (default the last 100): id, layer, brush, colour, point count, seed, bounds, `live`. With `ids`, their points too; with `layerId`, one layer's. With `live: true`, the editable strokes of stroke layers as they are now, bottom first: the ids for the stroke layer tools. |
324
+ | `get_live_stroke` | Read the stroke the user is painting right now (layer, brush, colour, samples so far; `live: false` when none). For painting along with the user; hosts stream the samples live. |
321
325
  | `stroke_at` | Read the top-most stroke of a stroke layer that painted the pixel at `x`, `y`, or null. Use it to pick one stroke among overlapping ones. |
322
326
  | `update_stroke` | Change a stroke on a stroke layer: `brushId` (keeps its size), `brush` facets (as `set_brush` takes), `color`, `paint` (null drops a gradient), `seed`. Redraws exactly. One undo step. Returns the stroke and the changed `bounds`. |
323
327
  | `edit_stroke_points` | Replace a stroke's `points`, or `transform` it (`dx`, `dy`, `scale`, `rotate`, `flipX`, about its centre or `origin`). One undo step. Returns the stroke with its points and the changed `bounds`. |
@@ -373,7 +373,7 @@ contents whole while you work, without losing the mask.
373
373
 
374
374
  | Command | Use it to |
375
375
  | --- | --- |
376
- | `vector.listBrushes` | List the brushes a brushstroke can use: the dry presets (pencils, pastels, charcoal, markers, inks), then saved brushes from the brush library the vector profile admits, with their sizes and `builtin`. Brushes it refuses (wet, thick, particles, stickers, tools) are refused by `vector.addBrushstroke` with the reason. A saved brush is kept in the layer, so the document renders without the library. |
376
+ | `vector.listBrushes` | List the brushes a brushstroke can use: the dry presets (pencils, pastels, charcoal, crayon, chalk, markers, inks, ballpoint, gel pen, fineliner), then saved brushes from the brush library the vector profile admits, with their sizes and `builtin`. Brushes it refuses (wet, thick, particles, stickers, tools) are refused by `vector.addBrushstroke` with the reason. A saved brush is kept in the layer, so the document renders without the library. |
377
377
  | `vector.addBrushstroke` | Paint paths with a brush along their outline (`brushId`, `size`, `pressure` profile such as [0.2, 1, 0.2] for a taper, `color`): sketchy, hand-drawn or inked lines that stay editable. Pass a `seed` (any integer) so the brush gets its character, the seeded jitter and paper grain that make pencil, pastel and charcoal look real; `paper` picks the grain (`{ pattern, seed, scale, depth }`) or `null` turns it off. Returns the stroke stacks. |
378
378
  | `vector.setBrushstroke` | Change brushstroke layer `index` of path `id`: brush, size, pressure, colour, opacity, `seed` (a new seed is a new take on the same stroke; null: plain dabs) and `paper` (replaced whole; null: no grain; {}: the default sanded paper). Remove it with `vector.removeStroke`. |
379
379