@pixerate/editor 0.250.0 → 0.252.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 (137) hide show
  1. package/dist/agent/index.cjs +40 -41
  2. package/dist/agent/index.js +9 -10
  3. package/dist/animation/index.cjs +6 -7
  4. package/dist/animation/index.js +5 -6
  5. package/dist/animation-editor/index.cjs +70 -70
  6. package/dist/animation-editor/index.js +12 -12
  7. package/dist/brushes/index.cjs +2087 -0
  8. package/dist/brushes/index.d.cts +17 -0
  9. package/dist/brushes/index.d.ts +17 -0
  10. package/dist/brushes/index.js +2087 -0
  11. package/dist/brushes/library.d.cts +104 -0
  12. package/dist/brushes/library.d.ts +104 -0
  13. package/dist/brushes/migrate.d.cts +17 -0
  14. package/dist/brushes/migrate.d.ts +17 -0
  15. package/dist/brushes/normalize.d.cts +15 -0
  16. package/dist/brushes/normalize.d.ts +15 -0
  17. package/dist/brushes/presets.d.cts +12 -0
  18. package/dist/brushes/presets.d.ts +12 -0
  19. package/dist/brushes/profiles.d.cts +85 -0
  20. package/dist/brushes/profiles.d.ts +85 -0
  21. package/dist/brushes/resolve.d.cts +56 -0
  22. package/dist/brushes/resolve.d.ts +56 -0
  23. package/dist/brushes/schema.d.cts +29 -0
  24. package/dist/brushes/schema.d.ts +29 -0
  25. package/dist/brushes/tools.d.cts +37 -0
  26. package/dist/brushes/tools.d.ts +37 -0
  27. package/dist/brushes/types.d.cts +323 -0
  28. package/dist/brushes/types.d.ts +323 -0
  29. package/dist/{brushstrokeRender-2FHFLSEG.js → brushstrokeRender-5PYSKZIJ.js} +8 -8
  30. package/dist/{brushstrokeRender-QGRXOKDA.cjs → brushstrokeRender-7FLLSQEC.cjs} +14 -14
  31. package/dist/canvas/index.cjs +57 -58
  32. package/dist/canvas/index.js +31 -32
  33. package/dist/chart/index.cjs +159 -160
  34. package/dist/chart/index.js +9 -10
  35. package/dist/{chunk-NOHJP5TK.js → chunk-2ZJ4DT3M.js} +1 -1
  36. package/dist/{chunk-XZNU4DUX.cjs → chunk-3NS5C7FB.cjs} +76 -76
  37. package/dist/{chunk-UTI5IITZ.cjs → chunk-4ZC5QJE2.cjs} +3 -3
  38. package/dist/{chunk-SMSGP2HQ.js → chunk-5JNA6LLY.js} +1 -1
  39. package/dist/{chunk-RD7RX52G.cjs → chunk-5UEFBM2M.cjs} +207 -18
  40. package/dist/{chunk-WA44NKPF.js → chunk-62Z5WLRA.js} +1 -1
  41. package/dist/{chunk-BVRRIW7F.js → chunk-6GRUVGKU.js} +3 -3
  42. package/dist/{chunk-QARCM66V.js → chunk-6ZZKRH7R.js} +3 -3
  43. package/dist/{chunk-BP6KSGVD.js → chunk-7272D3K2.js} +4 -6
  44. package/dist/{chunk-NTRAVM2I.js → chunk-7P6VF3FG.js} +2 -2
  45. package/dist/chunk-APX63ILU.js +173 -0
  46. package/dist/{chunk-W7L4HUT2.cjs → chunk-CJ6TBL2N.cjs} +16 -16
  47. package/dist/{chunk-7PT4RJL5.cjs → chunk-EUIDTZNQ.cjs} +111 -113
  48. package/dist/{chunk-CXXXAZ7H.js → chunk-F6R67I3A.js} +1 -1
  49. package/dist/{chunk-I7NJPHWV.cjs → chunk-FQPUF2W2.cjs} +35 -606
  50. package/dist/{chunk-D5KGAO4Z.js → chunk-GCVFEPJG.js} +2 -573
  51. package/dist/{chunk-LEQKZGQI.js → chunk-GOBOCZZI.js} +1 -1
  52. package/dist/{chunk-KDLBJ6QR.js → chunk-GYMMNCFT.js} +3 -3
  53. package/dist/{chunk-NUTMINJZ.cjs → chunk-H7ET2ISK.cjs} +52 -52
  54. package/dist/chunk-HS6QDWDF.cjs +1029 -0
  55. package/dist/{chunk-T7KQEMAE.cjs → chunk-HUAISCUX.cjs} +18 -18
  56. package/dist/{chunk-YGQVCQTG.js → chunk-ILP77UD6.js} +1 -1
  57. package/dist/{chunk-S6OT6CTS.cjs → chunk-IWEVV63O.cjs} +31 -33
  58. package/dist/{chunk-M2VKFE5L.cjs → chunk-JFHBXNR2.cjs} +88 -88
  59. package/dist/{chunk-WPUXGDH4.cjs → chunk-JPEHXIB5.cjs} +10 -10
  60. package/dist/chunk-JSDND7DF.cjs +173 -0
  61. package/dist/{chunk-36ZTV7WO.js → chunk-L6Y7TSQ4.js} +1 -1
  62. package/dist/{chunk-YW43M56C.cjs → chunk-LFMKWGFL.cjs} +1 -1
  63. package/dist/{chunk-V6DT2CDH.js → chunk-M2TPMYJE.js} +1 -1
  64. package/dist/{chunk-LJZRHVZV.cjs → chunk-M5GLQQWG.cjs} +2 -2
  65. package/dist/{chunk-JEGJP4OW.js → chunk-MHLU6GRJ.js} +196 -7
  66. package/dist/{chunk-KMPM5JM5.js → chunk-MONXNVZK.js} +4 -6
  67. package/dist/chunk-MTKY6K3Q.js +1029 -0
  68. package/dist/{chunk-MOW3JMNG.cjs → chunk-N76AMOKE.cjs} +42 -42
  69. package/dist/{chunk-75K3W4D6.cjs → chunk-NKGB64CW.cjs} +16 -16
  70. package/dist/{chunk-SGPIVHJB.js → chunk-P3K2JCJA.js} +3 -3
  71. package/dist/chunk-PXDIAYB6.cjs +594 -0
  72. package/dist/{chunk-3TWOCSCY.js → chunk-QC3OXRE4.js} +9 -9
  73. package/dist/{chunk-YM6ZBTXG.cjs → chunk-QLSGTCYT.cjs} +23 -23
  74. package/dist/{chunk-7YW7T5TJ.js → chunk-RHPSE2YR.js} +2 -2
  75. package/dist/{chunk-44BMJJVA.cjs → chunk-RUONPPMC.cjs} +8 -8
  76. package/dist/{chunk-ATK6ABN3.cjs → chunk-SABJMH6M.cjs} +145 -12
  77. package/dist/{chunk-XWOLPNBW.cjs → chunk-SOSBOS5T.cjs} +22 -22
  78. package/dist/{chunk-PPFJHTW5.cjs → chunk-TFF2GTGN.cjs} +22 -24
  79. package/dist/{chunk-X7WRL5ZZ.cjs → chunk-TLOGFIIV.cjs} +37 -37
  80. package/dist/chunk-TXFWZ5ZJ.js +594 -0
  81. package/dist/{chunk-GXAK6Q5J.js → chunk-UFEDHACQ.js} +2 -2
  82. package/dist/{chunk-GA4EU4P3.cjs → chunk-UXETV2NC.cjs} +4 -4
  83. package/dist/{chunk-N4DFDI73.js → chunk-WKMUBATU.js} +1 -1
  84. package/dist/{chunk-3R5P3DVY.cjs → chunk-WRZ6C4TU.cjs} +6 -6
  85. package/dist/{chunk-U4NUOKBT.js → chunk-WZOGDKCQ.js} +134 -1
  86. package/dist/{chunk-6ADHGQ73.js → chunk-YBAPT7NL.js} +2 -4
  87. package/dist/color/index.cjs +4 -6
  88. package/dist/color/index.js +7 -9
  89. package/dist/export/index.cjs +7 -8
  90. package/dist/export/index.js +6 -7
  91. package/dist/extensions/index.cjs +10 -11
  92. package/dist/extensions/index.js +9 -10
  93. package/dist/gradient/index.cjs +33 -34
  94. package/dist/gradient/index.js +4 -5
  95. package/dist/image-editor/index.cjs +845 -844
  96. package/dist/image-editor/index.js +39 -38
  97. package/dist/index.cjs +16 -17
  98. package/dist/index.js +10 -11
  99. package/dist/paint/index.cjs +6 -4
  100. package/dist/paint/index.js +21 -19
  101. package/dist/paint-editor/brushes.d.cts +1 -2
  102. package/dist/paint-editor/brushes.d.ts +1 -2
  103. package/dist/paint-editor/index.cjs +2193 -694
  104. package/dist/paint-editor/index.js +1575 -76
  105. package/dist/paint-editor/mark-settings.d.cts +104 -0
  106. package/dist/paint-editor/mark-settings.d.ts +104 -0
  107. package/dist/paint-editor/procedural.d.cts +2 -75
  108. package/dist/paint-editor/procedural.d.ts +2 -75
  109. package/dist/paint-editor/stickers.d.cts +1 -22
  110. package/dist/paint-editor/stickers.d.ts +1 -22
  111. package/dist/raster/index.cjs +2 -2
  112. package/dist/raster/index.js +9 -9
  113. package/dist/stickers/index.cjs +10 -10
  114. package/dist/stickers/index.js +9 -9
  115. package/dist/tokens/index.cjs +3 -4
  116. package/dist/tokens/index.js +2 -3
  117. package/dist/tools/skill-evals.d.cts +8 -1
  118. package/dist/tools/skill-evals.d.ts +8 -1
  119. package/dist/typography/index.cjs +7 -8
  120. package/dist/typography/index.js +2 -3
  121. package/dist/vector/index.cjs +974 -974
  122. package/dist/vector/index.js +12 -12
  123. package/dist/video-editor/index.cjs +6 -7
  124. package/dist/video-editor/index.js +5 -6
  125. package/package.json +11 -1
  126. package/skills/README.md +2 -0
  127. package/skills/pixerate-brushes/SKILL.md +104 -0
  128. package/skills/pixerate-brushes-design/SKILL.md +88 -0
  129. package/skills/pixerate-brushes-design/evals/canvas-chalk.json +15 -0
  130. package/skills/pixerate-brushes-design/evals/pink-highlighter.json +15 -0
  131. package/skills/pixerate-brushes-design/evals/port-to-vector.json +17 -0
  132. package/dist/chunk-4JNWA3VT.cjs +0 -302
  133. package/dist/chunk-7AHBW2CQ.cjs +0 -200
  134. package/dist/chunk-F67XMVDI.js +0 -200
  135. package/dist/chunk-GT77U7QG.cjs +0 -2637
  136. package/dist/chunk-OTEPAQJX.js +0 -2637
  137. package/dist/chunk-WKH5ZAUA.js +0 -302
@@ -26,7 +26,7 @@ import {
26
26
  traceField,
27
27
  traceVectorImage,
28
28
  vectorTraceContours
29
- } from "../chunk-NOHJP5TK.js";
29
+ } from "../chunk-2ZJ4DT3M.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-NTRAVM2I.js";
56
+ } from "../chunk-7P6VF3FG.js";
57
57
  import {
58
58
  DEFAULT_VECTOR_FONT,
59
59
  IDENTITY_MATRIX,
@@ -526,9 +526,9 @@ import {
526
526
  withVectorStrokes,
527
527
  withVectorTargetPaint,
528
528
  zigzagPaths
529
- } from "../chunk-KDLBJ6QR.js";
530
- import "../chunk-OTEPAQJX.js";
531
- import "../chunk-WKH5ZAUA.js";
529
+ } from "../chunk-GYMMNCFT.js";
530
+ import "../chunk-MTKY6K3Q.js";
531
+ import "../chunk-APX63ILU.js";
532
532
  import {
533
533
  createProvenance
534
534
  } from "../chunk-M6DNZNVY.js";
@@ -566,16 +566,17 @@ import {
566
566
  import "../chunk-V2ZCJRDM.js";
567
567
  import {
568
568
  GRAIN_PATTERNS
569
- } from "../chunk-D5KGAO4Z.js";
569
+ } from "../chunk-TXFWZ5ZJ.js";
570
+ import "../chunk-GCVFEPJG.js";
570
571
  import "../chunk-4D6KP5BV.js";
571
572
  import "../chunk-ODBT55Q7.js";
572
- import "../chunk-IRSTHLQQ.js";
573
573
  import {
574
574
  bytesToBase64,
575
575
  decodePng,
576
576
  isPng,
577
577
  parseDataUrl
578
578
  } from "../chunk-OTTT5N3K.js";
579
+ import "../chunk-IRSTHLQQ.js";
579
580
  import "../chunk-HVVR5PZW.js";
580
581
  import {
581
582
  deflate
@@ -590,14 +591,13 @@ import {
590
591
  HistoryManager
591
592
  } from "../chunk-NS62CQ2B.js";
592
593
  import "../chunk-5UJHB3BW.js";
593
- import "../chunk-V6DT2CDH.js";
594
+ import "../chunk-M2TPMYJE.js";
594
595
  import {
595
596
  hexToRgba
596
597
  } from "../chunk-2WPNAGHI.js";
597
- import "../chunk-BP6KSGVD.js";
598
- import "../chunk-6ADHGQ73.js";
599
- import "../chunk-JEGJP4OW.js";
600
- import "../chunk-F67XMVDI.js";
598
+ import "../chunk-7272D3K2.js";
599
+ import "../chunk-YBAPT7NL.js";
600
+ import "../chunk-MHLU6GRJ.js";
601
601
  import "../chunk-6ONMQ6WF.js";
602
602
 
603
603
  // src/vector/stylize.ts
@@ -182,17 +182,16 @@ require('../chunk-2BSBFBOA.cjs');
182
182
  var _chunkWKPXOGZZcjs = require('../chunk-WKPXOGZZ.cjs');
183
183
 
184
184
 
185
- var _chunkLJZRHVZVcjs = require('../chunk-LJZRHVZV.cjs');
185
+ var _chunkM5GLQQWGcjs = require('../chunk-M5GLQQWG.cjs');
186
186
 
187
187
 
188
188
 
189
189
  var _chunkDWJJI6RTcjs = require('../chunk-DWJJI6RT.cjs');
190
- require('../chunk-UTI5IITZ.cjs');
190
+ require('../chunk-4ZC5QJE2.cjs');
191
191
  require('../chunk-A6QWKBJP.cjs');
192
- require('../chunk-7PT4RJL5.cjs');
193
- require('../chunk-PPFJHTW5.cjs');
194
- require('../chunk-RD7RX52G.cjs');
195
- require('../chunk-7AHBW2CQ.cjs');
192
+ require('../chunk-EUIDTZNQ.cjs');
193
+ require('../chunk-TFF2GTGN.cjs');
194
+ require('../chunk-5UEFBM2M.cjs');
196
195
  require('../chunk-IPJ3TEYR.cjs');
197
196
 
198
197
  // src/video-editor/parse.ts
@@ -3675,7 +3674,7 @@ var VideoEditorController = (_class8 = class _VideoEditorController {
3675
3674
  this.readOnly = _nullishCoalesce(options.readOnly, () => ( false));
3676
3675
  this.previewScale = checkPreviewScale(_nullishCoalesce(options.previewScale, () => ( "auto")));
3677
3676
  this.onChange = options.onChange;
3678
- this.player = new (0, _chunkLJZRHVZVcjs.AnimationPlayer)({ clock: options.clock, duration: () => _chunkZBCT5MEAcjs.videoProjectDuration.call(void 0, this.project), loop: "none" });
3677
+ this.player = new (0, _chunkM5GLQQWGcjs.AnimationPlayer)({ clock: options.clock, duration: () => _chunkZBCT5MEAcjs.videoProjectDuration.call(void 0, this.project), loop: "none" });
3679
3678
  this.state = this.computeState();
3680
3679
  this.connect();
3681
3680
  }
@@ -182,17 +182,16 @@ import {
182
182
  } from "../chunk-NS62CQ2B.js";
183
183
  import {
184
184
  AnimationPlayer
185
- } from "../chunk-CXXXAZ7H.js";
185
+ } from "../chunk-F6R67I3A.js";
186
186
  import {
187
187
  CUBIC_BEZIER_PRESETS,
188
188
  evaluateEasing
189
189
  } from "../chunk-5UJHB3BW.js";
190
- import "../chunk-V6DT2CDH.js";
190
+ import "../chunk-M2TPMYJE.js";
191
191
  import "../chunk-2WPNAGHI.js";
192
- import "../chunk-BP6KSGVD.js";
193
- import "../chunk-6ADHGQ73.js";
194
- import "../chunk-JEGJP4OW.js";
195
- import "../chunk-F67XMVDI.js";
192
+ import "../chunk-7272D3K2.js";
193
+ import "../chunk-YBAPT7NL.js";
194
+ import "../chunk-MHLU6GRJ.js";
196
195
  import "../chunk-6ONMQ6WF.js";
197
196
 
198
197
  // src/video-editor/parse.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.250.0",
3
+ "version": "0.252.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -194,6 +194,16 @@
194
194
  "default": "./dist/paint-editor/index.cjs"
195
195
  }
196
196
  },
197
+ "./brushes": {
198
+ "import": {
199
+ "types": "./dist/brushes/index.d.ts",
200
+ "default": "./dist/brushes/index.js"
201
+ },
202
+ "require": {
203
+ "types": "./dist/brushes/index.d.cts",
204
+ "default": "./dist/brushes/index.cjs"
205
+ }
206
+ },
197
207
  "./commands": {
198
208
  "import": {
199
209
  "types": "./dist/commands/index.d.ts",
package/skills/README.md CHANGED
@@ -30,6 +30,7 @@ model and its general practice.
30
30
  | [`pixerate-color`](pixerate-color/SKILL.md) | Colour inspection, WCAG accessibility and contrast optimization, CVD simulation, palette audits, harmonies, and OKLab/OKLCH blends. |
31
31
  | [`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. |
32
32
  | [`pixerate-stickers`](pixerate-stickers/SKILL.md) | Sticker libraries: finding stickers by ref (`provider:id`), categories, tags, tintable art and licences, for the editors that spray or place them. |
33
+ | [`pixerate-brushes`](pixerate-brushes/SKILL.md) | The shared brush library: brushes made of facets (medium, tip, marks, dynamics, behaviour, light, motion, colour), the presets, host profiles for paint, image, canvas and vector, and creating, checking and sharing brushes. |
33
34
 
34
35
  ## Workflow skills
35
36
 
@@ -51,6 +52,7 @@ request like "clean up this export" finds it.
51
52
  | [`pixerate-paint-watercolour`](pixerate-paint-watercolour/SKILL.md) | Paint (`pixerate-paint-editor`) | Watercolour paper, light to dark with the paper's white kept, wet-in-wet and wet-on-dry washes, drying before glazing, steering the water. |
52
53
  | [`pixerate-paint-oil-impasto`](pixerate-paint-oil-impasto/SKILL.md) | Paint (`pixerate-paint-editor`) | A toned ground, thin block-in to thick paint, pigment mixing, impasto relief and its light. |
53
54
  | [`pixerate-paint-sketch-to-finish`](pixerate-paint-sketch-to-finish/SKILL.md) | Paint (`pixerate-paint-editor`) | Sketch, ink, flats under the lines, clipped shading and light, one named layer per pass. |
55
+ | [`pixerate-brushes-design`](pixerate-brushes-design/SKILL.md) | Brushes (`pixerate-brushes`) | From a description to a saved brush: the nearest preset, only the facets that change, checked for the host that will use it, previewed. |
54
56
  | [`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. |
55
57
  | [`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. |
56
58
  | [`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. |
@@ -0,0 +1,104 @@
1
+ ---
2
+ name: pixerate-brushes
3
+ description: How to find, describe, create, change, check and share brushes in the Pixerate brush library through list_brush_presets, describe_brush, create_brush, update_brush, duplicate_brush, remove_brush, check_brush, export_brushes, import_brushes and preview_brush - brushes made of facets (kind, medium and variety or tool, tip, marks, dynamics, behaviour, light, motion, colour), the built-in presets, and the host profiles that say what the paint editor, image editor, canvas and vector strokes can render. Use when asked to make, design, tweak, save, copy, delete, export, import or check a brush, find a brush for a medium (pencil, ink, watercolour, oil, airbrush, highlighter), or make a brush work in another editor.
4
+ ---
5
+
6
+ # Brushes
7
+
8
+ A brush is made of **facets** that say what it is and how it behaves, not which engine draws it.
9
+ Only `id`, `name`, `kind` and `tip.shape` are required; every other field takes the default of
10
+ the engine the facets pick, so a brush stores only what makes it what it is. The library holds the
11
+ built-in presets (read-only) and the user's brushes, shared by every editor that paints.
12
+
13
+ | Facet (field) | What it holds |
14
+ | --- | --- |
15
+ | Kind | `mark` lays a material; `tool` works on paint already down. |
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
+ | 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). |
20
+ | Dynamics | Pen feel: pressure to size and opacity, smoothing, tilt, velocity, jitter, rotation. |
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
+ | Light | Light the brush gives off (`amount`), plus suggested layer `spill` and `glow`. |
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. |
25
+
26
+ How media map to facets: a **pencil** or **charcoal** is `dry` with high `grain` and tilt;
27
+ **ink** is a hard round tip with pressure to size (a pen thins with velocity); **watercolour**
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.
32
+
33
+ ## Host profiles
34
+
35
+ Each host renders a part of this. `check_brush` says what a host refuses (errors) and what it
36
+ paints without (warnings), each with a fix; lists and writes use the current host.
37
+
38
+ | Host | Renders |
39
+ | --- | --- |
40
+ | Paint editor (`paint`) | Everything. |
41
+ | Image editor (`image`) | Dry dabs on round, flat and chisel tips, the Mixer Brush and the eraser; no wet or thick surface, particles, objects, light or motion. |
42
+ | Canvas (`canvas`) | Dry dab brushes along a path (no tools, wet, thick, particles, objects or light); velocity is ignored. |
43
+ | Vector strokes (`vector`) | As canvas, but light brushes paint as plain colour. |
44
+
45
+ ## Working method
46
+
47
+ 1. **Find the nearest brush.** `list_brush_presets` with `medium`, `variety`, `kind` or `text`.
48
+ It lists only what this host can render (`all: true` lists the rest with `renderable`).
49
+ 2. **Read it.** `describe_brush` for the full spec, the engine it resolves to and its check.
50
+ 3. **Make a brush.** `create_brush` with a new `id`, a `name`, `basedOn` the nearest preset and
51
+ only the facets that change (objects merge; lists replace). Read `problems` (values clamped or
52
+ dropped) and `warnings`. A brush the host can't render is refused and nothing is saved.
53
+ 4. **Look at it.** `preview_brush` when the host has a renderer; otherwise paint a test stroke
54
+ in the editor (`set_brush` with the new id, then `paint_stroke`).
55
+ 5. **Tune it.** `update_brush` with the facets to change, or `unset` to drop whole facets. It
56
+ returns the leaf paths `changed`. Built-in presets can't be changed: `duplicate_brush` first.
57
+ 6. **Share it.** `export_brushes` writes a library file; `import_brushes` adds one (version 1
58
+ files of the paint editor's older brushes are migrated). `check_brush` with `host` before
59
+ handing a brush to another editor.
60
+
61
+ ## Best practices
62
+
63
+ - Start from the nearest preset and change two or three facets; a brush from nothing gets
64
+ engine defaults that rarely suit a medium.
65
+ - Test on the paper and colour the brush will be used on: grain response and glazes look
66
+ different on smooth and rough paper.
67
+ - Give light brushes dark paper, and give highlighters a `light.spill` radius of 0 so the
68
+ glow stays on the stroke.
69
+ - Keep `variety` truthful (it is how people find the brush) and add `tags` for the job
70
+ (`lettering`, `foliage`).
71
+ - Use `colour.palette` for suggested colours and `colour.own` only for brushes that must
72
+ always paint their own (a rainbow marker).
73
+
74
+ ## Pitfalls
75
+
76
+ - **A facet the host refuses.** `create_brush` and `update_brush` refuse it with the facet and
77
+ fix; `check_brush` returns `degraded`, the brush with those facets stripped, to start from.
78
+ - **Ids.** Built-in ids can't be reused or changed; a taken id is refused (update it instead).
79
+ - **Stamps.** Particle marks lay only the editor's stamps (`editor:star`…); sticker (`objects`)
80
+ marks take any sticker ref from `find_stickers`.
81
+ - **Out-of-range values** are refused by the schema (`tip.size` is 1..2500 px); a stored brush
82
+ is clamped on import and reported in `problems`.
83
+ - **The paint editor's own brush tools** (`set_brush`, `save_brush`, its `export_brushes`)
84
+ still work on its brushes; this library's tools are namespaced apart.
85
+
86
+ ## Workflow skills
87
+
88
+ - `pixerate-brushes-design`: from a description ("a soft pink highlighter", "chalk for the
89
+ canvas") to a saved, checked brush.
90
+
91
+ ## Tool reference
92
+
93
+ | Tool | Use |
94
+ | --- | --- |
95
+ | `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. |
96
+ | `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. |
97
+ | `create_brush` | Save a new brush: `id`, `name`, optional `basedOn`, and the facets. Normalised and checked for this host; refused brushes save nothing. Returns `id`, `brush`, `engine`, `problems`, `warnings`. |
98
+ | `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`. |
99
+ | `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. |
100
+ | `remove_brush` | Delete a saved brush for good (export it first to keep a copy). Built-ins are refused. Returns `removed`. |
101
+ | `check_brush` | Check a brush (`id`, or an unsaved `brush` spec) for a `host` (paint, image, canvas, vector). Returns `ok`, `issues` (facet, severity, reason, fix) and, when refused, `degraded`. |
102
+ | `export_brushes` | Library file (pixerate-brushes v2) of the saved brushes or the `ids` given. Returns `file`, for `import_brushes`. |
103
+ | `import_brushes` | Add a library `file` (v2, v1 migrated, or a bare list): built-in ids and taken ids are skipped unless `overwrite`. Returns `added`, `replaced`, `skipped`, `problems`. |
104
+ | `preview_brush` | Only when the host renders previews: a sample stroke with a brush (`id`, `width`, `height`, `color`) as an image. |
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: pixerate-brushes-design
3
+ description: The brush design workflow in the Pixerate brush library - turning a description ("a soft pink highlighter", "chalk for canvas strokes", "make this brush work in the vector editor") into a saved brush, by picking the nearest preset, changing only the facets that matter, checking it against the host that will use it and previewing it. Use when asked to make, design, customise, adapt or port a brush, or when create_brush, check_brush or list_brush_presets are available. Load with pixerate-brushes, which lists every tool.
4
+ metadata:
5
+ kind: workflow
6
+ reference: pixerate-brushes
7
+ ---
8
+
9
+ # Designing a brush
10
+
11
+ A brush is a preset with a few facets changed. The presets already hold good settings for each
12
+ medium (pencil grain, ink velocity, watercolour water), so the fastest good brush starts from
13
+ the nearest one and changes two or three facets: the colour, the tip, how it feels. What a brush
14
+ may use depends on the editor that will paint with it: check it against that host before saving.
15
+
16
+ This skill is the method. `pixerate-brushes` has the full tool reference.
17
+
18
+ ## Goal and quality bar
19
+
20
+ - The brush is saved under a clear `id` and `name`, with a truthful `medium` and `variety`.
21
+ - `check_brush` for the host it is for returns `ok: true`; any warnings are understood.
22
+ - It differs from its base preset only where the request asks (`describe_brush` shows it).
23
+ - Built-in presets are untouched.
24
+
25
+ ## Inputs to confirm
26
+
27
+ Ask when you cannot tell from the request; say what you assumed when you can.
28
+
29
+ - **Host:** which editor will use it (paint by default; canvas and vector strokes take only dry
30
+ dab brushes, the image editor no wet, thick or particle brushes).
31
+ - **Medium and character:** what it imitates (pencil, ink, chalk, watercolour, marker…), and
32
+ the size, softness and texture.
33
+ - **Colour:** a default colour or palette, or the user's own colour (the default).
34
+
35
+ ## Working method
36
+
37
+ 1. **Find the base.** `list_brush_presets` with `medium` or `variety`, or `text`. Pick the
38
+ closest; `describe_brush` it to read its facets.
39
+ **Check:** the base's `check` is `ok` for the target host (or `check_brush` with `host`).
40
+ 2. **Create from it.** `create_brush` with a new `id`, `name`, `basedOn` and only the facets
41
+ that change: `variety` for what it is now, `colour.default` / `colour.palette`, `tip`
42
+ (size, hardness, roundness, angle), `dynamics`, `behaviour.grain`.
43
+ **Check:** `problems` is empty or understood; nothing refused.
44
+ 3. **Check it for the host.** `check_brush` with `id` and `host`. A refusal names the facet and
45
+ fix; its `degraded` brush is a good next spec.
46
+ **Check:** `ok: true`.
47
+ 4. **Look at it.** `preview_brush` when available; then `update_brush` to tune (each call
48
+ returns the paths `changed`).
49
+ **Check:** `describe_brush` shows the intended facets.
50
+
51
+ ## Decision table
52
+
53
+ | Situation | Approach |
54
+ | --- | --- |
55
+ | A colour variant of a preset | `basedOn` it, set `colour.default` (and a `palette`), keep everything else. |
56
+ | For canvas or vector strokes | Base on a dry preset (pencil, pastel, charcoal, marker, ink); no wet surface, particles, stickers or tools; no light on canvas. |
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
+ | A texture (chalk, charcoal, dry brush) | Raise `behaviour.grain` (0.8..1) and add `dynamics.sizeJitter`; test on rough paper. |
59
+ | A light brush (neon, glow) | `light.amount`, and paint it on dark paper; a highlighter keeps `light.spill.radius` 0. |
60
+ | A change to a built-in | `duplicate_brush`, then `update_brush` the copy. |
61
+
62
+ ## Recipes
63
+
64
+ **A pink highlighter.** `create_brush` `{ id: "pink-highlighter", name: "Pink highlighter",
65
+ basedOn: "highlighter", colour: { default: "#ff6ec7" } }`, then `check_brush`.
66
+
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 } }`.
69
+
70
+ **Port a wet brush to vector.** `check_brush` `{ id, host: "vector" }`; `create_brush` from the
71
+ `degraded` brush with `id` `<id>-vector`; `check_brush` again.
72
+
73
+ ## Pitfalls
74
+
75
+ - **Starting from nothing.** A brush without `basedOn` gets engine defaults (a hard round dab);
76
+ it rarely looks like the medium asked for.
77
+ - **A host that refuses the base.** A wet or particle preset can't be the base of a canvas
78
+ brush; set `behaviour.surface` to `dry` or start from a dry preset.
79
+ - **Lists replace.** Giving `colour.palette` or `tip.stamps` replaces the whole list; include
80
+ every entry you want.
81
+ - **Changing a built-in.** `update_brush` refuses built-ins; duplicate first.
82
+
83
+ ## Tools used
84
+
85
+ - `list_brush_presets`, `describe_brush`: find the base preset and read its facets.
86
+ - `create_brush`, `update_brush`, `duplicate_brush`: make and tune the brush.
87
+ - `check_brush`: prove it works for the host, and get a degraded spec when it doesn't.
88
+ - `preview_brush`: look at a sample stroke when the host renders previews.
@@ -0,0 +1,15 @@
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.",
3
+ "fixture": { "editor": "brushes", "host": "canvas" },
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 } } } } }
9
+ ],
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" } }
14
+ ]
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "prompt": "Make me a pink highlighter brush, saved as pink-highlighter, that behaves like the built-in highlighter but starts out pink.",
3
+ "fixture": { "editor": "brushes" },
4
+ "checks": [
5
+ { "why": "a saved brush, not a built-in", "tool": "describe_brush", "args": { "id": "pink-highlighter" }, "expect": { "builtin": false, "check": { "ok": true } } },
6
+ { "why": "still a highlighter: multiply chisel ink with no spill", "tool": "describe_brush", "args": { "id": "pink-highlighter" }, "expect": { "brush": { "medium": "ink", "variety": "highlighter", "tip": { "shape": "chisel", "blend": "multiply" }, "light": { "spill": { "radius": 0 } } } } },
7
+ { "why": "it starts out pink", "tool": "describe_brush", "args": { "id": "pink-highlighter" }, "expect": { "brush": { "colour": { "default": { "$expr": "(([r, g, b]) => r > 200 && b > 120 && g < r - 60)([1, 3, 5].map((i) => parseInt(v.slice(i, i + 2), 16)))" } } } } },
8
+ { "why": "the built-in highlighter is untouched", "tool": "describe_brush", "args": { "id": "highlighter" }, "expect": { "brush": { "colour": { "default": "#fff200" } } } }
9
+ ],
10
+ "solution": [
11
+ { "tool": "list_brush_presets", "args": { "variety": "highlighter" } },
12
+ { "tool": "create_brush", "args": { "id": "pink-highlighter", "name": "Pink highlighter", "basedOn": "highlighter", "colour": { "default": "#ff6ec7" } } },
13
+ { "tool": "check_brush", "args": { "id": "pink-highlighter" } }
14
+ ]
15
+ }
@@ -0,0 +1,17 @@
1
+ {
2
+ "prompt": "A teammate's brush team-wash is in the library, but it won't work for vector brushstrokes. Make a copy called team-wash-vector that does, and leave theirs as it is.",
3
+ "fixture": {
4
+ "editor": "brushes",
5
+ "setup": [{ "tool": "create_brush", "args": { "id": "team-wash", "name": "Team wash", "basedOn": "watercolor-wash", "tags": ["team"] } }]
6
+ },
7
+ "checks": [
8
+ { "why": "the copy works in the vector editor", "tool": "check_brush", "args": { "id": "team-wash-vector", "host": "vector" }, "expect": { "ok": true } },
9
+ { "why": "the copy keeps the wash's tip", "tool": "describe_brush", "args": { "id": "team-wash-vector" }, "expect": { "brush": { "tip": { "size": 90, "roundness": 0.45 } } } },
10
+ { "why": "the teammate's brush still paints wet", "tool": "describe_brush", "args": { "id": "team-wash" }, "expect": { "brush": { "behaviour": { "surface": "wet" } } } }
11
+ ],
12
+ "solution": [
13
+ { "tool": "check_brush", "args": { "id": "team-wash", "host": "vector" } },
14
+ { "tool": "create_brush", "args": { "id": "team-wash-vector", "name": "Team wash (vector)", "basedOn": "team-wash", "behaviour": { "surface": "dry" } } },
15
+ { "tool": "check_brush", "args": { "id": "team-wash-vector", "host": "vector" } }
16
+ ]
17
+ }