@pixerate/editor 0.266.2 → 0.268.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 (149) hide show
  1. package/dist/agent/index.cjs +2 -2
  2. package/dist/agent/index.js +1 -1
  3. package/dist/animation-editor/controller.d.cts +8 -0
  4. package/dist/animation-editor/controller.d.ts +8 -0
  5. package/dist/animation-editor/effect-tools.d.cts +1 -1
  6. package/dist/animation-editor/effect-tools.d.ts +1 -1
  7. package/dist/animation-editor/index.cjs +175 -108
  8. package/dist/animation-editor/index.js +94 -27
  9. package/dist/brushes/index.cjs +201 -75
  10. package/dist/brushes/index.d.cts +4 -3
  11. package/dist/brushes/index.d.ts +4 -3
  12. package/dist/brushes/index.js +138 -12
  13. package/dist/brushes/library.d.cts +27 -1
  14. package/dist/brushes/library.d.ts +27 -1
  15. package/dist/brushes/profiles.d.cts +10 -1
  16. package/dist/brushes/profiles.d.ts +10 -1
  17. package/dist/brushes/schema.d.cts +4 -0
  18. package/dist/brushes/schema.d.ts +4 -0
  19. package/dist/brushes/sources.d.cts +114 -0
  20. package/dist/brushes/sources.d.ts +114 -0
  21. package/dist/brushes/types.d.cts +37 -5
  22. package/dist/brushes/types.d.ts +37 -5
  23. package/dist/brushes/ui.d.cts +40 -4
  24. package/dist/brushes/ui.d.ts +40 -4
  25. package/dist/{brushstrokeRender-KD453OBX.js → brushstrokeRender-A727TTNQ.js} +7 -7
  26. package/dist/{brushstrokeRender-FSUWXUZY.cjs → brushstrokeRender-WR2N5HHY.cjs} +23 -23
  27. package/dist/canvas/brushTypes.d.cts +12 -0
  28. package/dist/canvas/brushTypes.d.ts +12 -0
  29. package/dist/canvas/brushstroke.d.cts +1 -1
  30. package/dist/canvas/brushstroke.d.ts +1 -1
  31. package/dist/canvas/drawController.d.cts +19 -4
  32. package/dist/canvas/drawController.d.ts +19 -4
  33. package/dist/canvas/index.cjs +144 -105
  34. package/dist/canvas/index.js +46 -7
  35. package/dist/{chunk-TOID45I2.cjs → chunk-2WOGXEDX.cjs} +1 -1
  36. package/dist/{chunk-XORJ7UZO.cjs → chunk-3VHH6V67.cjs} +13 -12
  37. package/dist/{chunk-3C33YTNN.js → chunk-3XDSNIGF.js} +73 -6
  38. package/dist/chunk-62MFUHSI.cjs +802 -0
  39. package/dist/{chunk-R44Y3EFS.js → chunk-74ODPZLX.js} +17 -345
  40. package/dist/{chunk-GCVFEPJG.js → chunk-ACTHSQOI.js} +182 -0
  41. package/dist/chunk-ADMRNWR7.js +340 -0
  42. package/dist/{chunk-MVUQSK5J.cjs → chunk-B3G7BKUV.cjs} +111 -439
  43. package/dist/{chunk-DXYHQ2VN.js → chunk-BAZMCFVL.js} +1 -1
  44. package/dist/{chunk-VRK322ZC.cjs → chunk-BPJSI4NT.cjs} +2 -2
  45. package/dist/{chunk-RFJZGTHA.cjs → chunk-EU2ZZA6L.cjs} +400 -69
  46. package/dist/{chunk-XC6DPRYI.js → chunk-GSTSZPSR.js} +154 -949
  47. package/dist/{chunk-EZOJBP4D.js → chunk-HFY5LG5J.js} +1 -1
  48. package/dist/chunk-HVO6XCI4.js +802 -0
  49. package/dist/{chunk-YHDHFJUC.cjs → chunk-J5VQYK7T.cjs} +217 -1012
  50. package/dist/{chunk-XRXFVRGY.cjs → chunk-JBB3GDBB.cjs} +2483 -208
  51. package/dist/{chunk-ACFSFUQH.js → chunk-JL6YH5WD.js} +4 -0
  52. package/dist/{chunk-CIJBSUM5.js → chunk-KZI3YWKW.js} +353 -22
  53. package/dist/{chunk-4ODRXQRW.js → chunk-LETF75CA.js} +1 -1
  54. package/dist/{chunk-FQPUF2W2.cjs → chunk-NIBFKXMD.cjs} +217 -35
  55. package/dist/{chunk-AQZQWXEU.cjs → chunk-NPWWSCWT.cjs} +35 -35
  56. package/dist/{chunk-DOZ6F2K2.js → chunk-NSETJI74.js} +3 -2
  57. package/dist/{chunk-PE6YJMQT.cjs → chunk-PFHJS4HE.cjs} +20 -19
  58. package/dist/{chunk-7KNW3WXZ.cjs → chunk-QEVLRLUN.cjs} +63 -0
  59. package/dist/{chunk-LK4SXL3V.js → chunk-QNY7BDFP.js} +1 -1
  60. package/dist/{chunk-OTN6VB5S.cjs → chunk-RDFHWAHL.cjs} +5 -1
  61. package/dist/{chunk-PXDIAYB6.cjs → chunk-STLTOISW.cjs} +34 -214
  62. package/dist/{chunk-T2LDTEXK.js → chunk-UVTO3D4E.js} +2333 -58
  63. package/dist/{chunk-6JL2D5SG.js → chunk-VE6UKYQ2.js} +3 -2
  64. package/dist/{chunk-TXFWZ5ZJ.js → chunk-W6JDUXP3.js} +4 -184
  65. package/dist/{chunk-3SBLVGOM.cjs → chunk-YOVXQHA3.cjs} +23 -23
  66. package/dist/{chunk-WLUVZKVD.cjs → chunk-YXP6X3SY.cjs} +103 -36
  67. package/dist/chunk-YZWM6ZIP.cjs +340 -0
  68. package/dist/{chunk-472INMWH.js → chunk-ZA7L5YJB.js} +63 -0
  69. package/dist/commands/builtins.d.cts +12 -1
  70. package/dist/commands/builtins.d.ts +12 -1
  71. package/dist/commands/index.cjs +2 -2
  72. package/dist/commands/index.js +1 -1
  73. package/dist/image-editor/controller.d.cts +1 -1
  74. package/dist/image-editor/controller.d.ts +1 -1
  75. package/dist/image-editor/index.cjs +868 -864
  76. package/dist/image-editor/index.js +123 -119
  77. package/dist/paint/index.cjs +8 -6
  78. package/dist/paint/index.js +56 -54
  79. package/dist/paint-editor/brush-library-tools.d.cts +12 -2
  80. package/dist/paint-editor/brush-library-tools.d.ts +12 -2
  81. package/dist/paint-editor/brush-model.d.cts +29 -1
  82. package/dist/paint-editor/brush-model.d.ts +29 -1
  83. package/dist/paint-editor/brush-source-art.d.cts +32 -0
  84. package/dist/paint-editor/brush-source-art.d.ts +32 -0
  85. package/dist/paint-editor/controller.d.cts +11 -4
  86. package/dist/paint-editor/controller.d.ts +11 -4
  87. package/dist/paint-editor/dab-stroke.d.cts +2 -0
  88. package/dist/paint-editor/dab-stroke.d.ts +2 -0
  89. package/dist/paint-editor/engine.d.cts +11 -2
  90. package/dist/paint-editor/engine.d.ts +11 -2
  91. package/dist/paint-editor/index.cjs +967 -2335
  92. package/dist/paint-editor/index.d.cts +3 -1
  93. package/dist/paint-editor/index.d.ts +3 -1
  94. package/dist/paint-editor/index.js +394 -1762
  95. package/dist/paint-editor/path-samples.d.cts +2 -0
  96. package/dist/paint-editor/path-samples.d.ts +2 -0
  97. package/dist/paint-editor/presence.d.cts +20 -1
  98. package/dist/paint-editor/presence.d.ts +20 -1
  99. package/dist/paint-editor/stamp-tip.d.cts +8 -5
  100. package/dist/paint-editor/stamp-tip.d.ts +8 -5
  101. package/dist/paint-editor/thumbnails.d.cts +6 -0
  102. package/dist/paint-editor/thumbnails.d.ts +6 -0
  103. package/dist/paint-editor/tip-grain.d.cts +34 -0
  104. package/dist/paint-editor/tip-grain.d.ts +34 -0
  105. package/dist/raster/index.cjs +2 -2
  106. package/dist/raster/index.js +9 -9
  107. package/dist/stickers/helpers.d.cts +2 -0
  108. package/dist/stickers/helpers.d.ts +2 -0
  109. package/dist/stickers/index.cjs +41 -35
  110. package/dist/stickers/index.js +22 -16
  111. package/dist/stickers/types.d.cts +4 -2
  112. package/dist/stickers/types.d.ts +4 -2
  113. package/dist/vector/backdrop.d.cts +13 -0
  114. package/dist/vector/backdrop.d.ts +13 -0
  115. package/dist/vector/brushstroke.d.cts +54 -7
  116. package/dist/vector/brushstroke.d.ts +54 -7
  117. package/dist/vector/brushstrokeMedia.d.cts +96 -0
  118. package/dist/vector/brushstrokeMedia.d.ts +96 -0
  119. package/dist/vector/controller.d.cts +17 -3
  120. package/dist/vector/controller.d.ts +17 -3
  121. package/dist/vector/index.cjs +1850 -1390
  122. package/dist/vector/index.d.cts +2 -0
  123. package/dist/vector/index.d.ts +2 -0
  124. package/dist/vector/index.js +520 -60
  125. package/dist/vector/render.d.cts +6 -0
  126. package/dist/vector/render.d.ts +6 -0
  127. package/dist/vector/types.d.cts +14 -0
  128. package/dist/vector/types.d.ts +14 -0
  129. package/package.json +1 -1
  130. package/skills/pixerate-animation-editor/SKILL.md +1 -0
  131. package/skills/pixerate-brushes/SKILL.md +37 -10
  132. package/skills/pixerate-brushes-design/SKILL.md +15 -5
  133. package/skills/pixerate-brushes-design/evals/port-to-vector.json +8 -8
  134. package/skills/pixerate-canvas/SKILL.md +11 -1
  135. package/skills/pixerate-image-editor/SKILL.md +19 -64
  136. package/skills/pixerate-image-editor/references/color-management.md +2 -1
  137. package/skills/pixerate-image-editor/references/free-transform.md +12 -0
  138. package/skills/pixerate-image-editor/references/layer-effects.md +10 -0
  139. package/skills/pixerate-image-editor/references/painting.md +17 -0
  140. package/skills/pixerate-image-editor/references/photoshop-files.md +1 -1
  141. package/skills/pixerate-image-editor/references/shapes-and-type.md +14 -0
  142. package/skills/pixerate-image-editor/references/smart-objects.md +10 -0
  143. package/skills/pixerate-paint-editor/SKILL.md +14 -4
  144. package/skills/pixerate-paint-editor/references/brush-facets.md +3 -1
  145. package/skills/pixerate-paint-editor/references/wet-media.md +9 -0
  146. package/skills/pixerate-paint-sketch-to-finish/SKILL.md +1 -0
  147. package/skills/pixerate-vector/SKILL.md +16 -6
  148. package/skills/pixerate-vector/references/best-practices.md +1 -0
  149. package/skills/pixerate-vector/references/commands.md +19 -0
@@ -35,3 +35,13 @@ How to work with smart objects in the image editor (see the main skill for the t
35
35
  the user may want to mask, blend or reorder them later.
36
36
  - **Linked or not**: duplicate a smart object `linked: true` for repeated elements that must stay
37
37
  identical (a logo placed twice); use `linked: false` when the copies will be edited apart.
38
+
39
+ ## Pitfalls
40
+
41
+ - While contents are open (`edit_smart_object_contents`), `list_layers` and every tool see the
42
+ contents, not the document, and `export_psd` / saving write the document without the open
43
+ edits. Close with `close_smart_object_contents` before anything else.
44
+ - Smart filters run over the canvas, like layer filters: a blur clamps at the canvas edge, and a
45
+ smart object is drawn only up to 512 px beyond the canvas. `get_transform_frame` on a smart
46
+ object returns the contents' rectangle (contents px) as the `frame`, and a warp `mesh` in
47
+ contents px.
@@ -42,6 +42,12 @@ is never part of a layer. Coordinates are document pixels, (0, 0) top-left, y do
42
42
  same two ratios. `get_mixer_brush` reads what the brush carries; `clean_mixer_brush` wipes it.
43
43
  - **Grain**: a brush's grain response times the paper's `depth` decides how much light pressure
44
44
  skips the valleys. Pencil, pastel and charcoal are grainy; airbrush, marker and ink are not.
45
+ A brush can also carry its own texture, `tip.grain` (a pattern such as `sanded` or `canvas`, or
46
+ an image source; `texturised` stays put, `moving` rolls with the tip), which shows on any paper.
47
+ - **Image tips**: a stamp tip whose stamp is an image source (`source:<id>`, added with the
48
+ brush library's `add_brush_source`) paints every dab as that image in the paint colour, with
49
+ hardness, roundness, angle, pressure size, `dynamics.count`, `flip` and jitter. Strokes record
50
+ the source, and the saved painting carries its bytes, so replays and exports match.
45
51
  - **Strokes are data.** Every stroke is recorded (brush, colour, raw points, seed, symmetry
46
52
  copies) and gets a `strokeId`. `replay_strokes` paints recorded strokes again, exactly or
47
53
  varied. That is the main way an agent should make many similar marks.
@@ -321,7 +327,7 @@ Listing, saving, sharing and previewing brushes are the brush library's tools (`
321
327
  | Tool | Use |
322
328
  | --- | --- |
323
329
  | `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. |
324
- | `stroke_path` | Brush SVG path data (`path`, document px; M, L, C, Q, A, Z…) with the active brush or `brushId` / `brush`, as if drawn along it by hand: each subpath one recorded stroke, sampled every `spacing` px (default 2), with a `pressure` profile spread along each subpath (`[0.2, 1, 0.2]` tapers both ends). One undo step. Returns `layerId`, `bounds`, `dabs` and `strokeIds` (for `replay_strokes`, `update_stroke`). Use it for clean curves, outlines of shapes and lettering guides instead of working out points; use `paint_stroke` when the marks need time or tilt. |
330
+ | `stroke_path` | Brush SVG path data (`path`, document px; M, L, C, Q, A, Z…) with the active brush or `brushId` / `brush`, as if drawn along it by hand: each subpath one recorded stroke, sampled every `spacing` px (default 2), with a `pressure` profile spread along each subpath (`[0.2, 1, 0.2]` tapers both ends) and optional `speed` (px/ms, stamps each sample with `t`). With an agent director, each subpath is painted live in turn. One undo step. Returns `layerId`, `bounds`, `dabs` and `strokeIds` (for `replay_strokes`, `update_stroke`). Use it for clean curves, outlines of shapes and lettering guides instead of working out points; use `paint_stroke` when the marks need variable timing or tilt. |
325
331
  | `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. |
326
332
  | `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. |
327
333
  | `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. |
@@ -339,7 +345,7 @@ Listing, saving, sharing and previewing brushes are the brush library's tools (`
339
345
 
340
346
  | Tool | Use |
341
347
  | --- | --- |
342
- | `get_wet_paint` | Read the wet paint: `wet`, the clock, `paused`, `speed`, `tilt`, the kernel (`backend`, `message` when it runs slower than it could), the GPU accelerator (`engine`) and each wet layer (`wetCells`, `wetBounds`, `activeTiles`, settings, flow map). Call after wet strokes. |
348
+ | `get_wet_paint` | Read the wet paint: `wet`, the clock, `paused`, `speed`, `tilt`, the kernel (`backend`, `message` when it runs slower than it could, e.g. on one thread because the page is not cross-origin isolated (COOP / COEP)), the GPU accelerator (`engine`) and each wet layer (`wetCells`, `wetBounds`, `activeTiles`, settings, flow map). Call after wet strokes. |
343
349
  | `advance_wet_paint` | Let time pass: `steps` (120 = one second at the default speed) or `seconds`. The paint spreads, blooms, runs and dries. Not an undo step. Returns `steps`, `clock`, `stillWet`, `layers`. Call after a group of wet strokes, before `render_preview`. |
344
350
  | `dry_paint` | Dry wet paint at once where it is (one layer with `layerId` or all, optionally inside `rect`). One undo step. Returns `dried` (layer ids and rectangles) and `stillWet`. Dry before glazing over a wash. |
345
351
  | `wet_paper` | Pre-wet the paper with clear water over `rect` (default the canvas) on a layer, `amount` 0..1, for wet-in-wet. One undo step. Returns the rectangle and `wet`. Then paint into it. |
@@ -462,7 +468,8 @@ The host may add the shared document tools (see `pixerate-agent-tools`). A paint
462
468
  keep taking its colours.
463
469
  - **Strokes take real time when the host shows your cursor.** With a director (the demo and
464
470
  editors that set `createPaintEditorToolProvider({ director })`), `paint_stroke` paints live at
465
- the pace of the points' `t` (else about 1.5 px per ms, at most ~1.8 s), and `fill_layer` /
471
+ the pace of the points' `t` (else about 1.5 px per ms, at most ~1.8 s), `stroke_path` paints
472
+ each subpath in turn (paced by its `speed` when given) as one undo step, and `fill_layer` /
466
473
  `replay_strokes` move the cursor first. Results and undo are the same; it is one more reason to
467
474
  paint a few long strokes and replay them rather than many short ones. A stroke fails with "A
468
475
  stroke is in progress" while the user is painting; try it again afterwards.
@@ -470,7 +477,10 @@ The host may add the shared document tools (see `pixerate-agent-tools`). A paint
470
477
  flowed; `advance_wet_paint` first. Paint that should stay exactly where it was laid needs
471
478
  `dry_paint` (or a dry medium).
472
479
  - **Wet strokes on one layer interact**; washes that must not mix go on separate layers, or dry
473
- the first before painting the second (glazing). At most 8 layers hold wet paint at once.
480
+ the first before painting the second (glazing). At most 8 layers hold wet paint at once, and the simulation holds at most 328 wet tiles (256 × 256 px each) across them; water that spreads past
481
+ that limit is lost (`dry_paint` frees tiles). Multi-threaded wet media needs a cross-origin isolated
482
+ page (COOP / COEP, for `SharedArrayBuffer`) and runs on one thread otherwise, more slowly but with
483
+ the same result.
474
484
  - **Undoing far back** past the wet undo memory (512 MB by default) leaves that paint dry as it
475
485
  looked, rather than wet.
476
486
  - **Replays repeat what was recorded**: strokes, fills, clears, flattened relief and wet events.
@@ -8,7 +8,9 @@ more; this is where each one went.
8
8
  | Engine setting | Facet |
9
9
  | --- | --- |
10
10
  | Tip size, hardness, spacing, angle, roundness, opacity, flow, blend | `tip` |
11
- | Smoothing, pressure, tilt, velocity, jitter, scatter, rotation | `dynamics` |
11
+ | Smoothing, pressure, tilt, velocity, jitter, scatter, rotation, dabs per step (`count`), random `flip` | `dynamics` |
12
+ | Image tip | `tip` `{ shape: 'stamp', stamps: ['source:<id>'] }` (an image source from `add_brush_source`) |
13
+ | Texture inside the tip | `tip.grain` `{ source: 'sanded' or 'source:<id>', mode: 'texturised' or 'moving', scale, depth, contrast, invert }` |
12
14
  | Grain, pick-up and mix (behavior), blender and smudge strength | `behaviour` (`grain`, `exchange`, `strength`, `distance`, `length`, `mixing`) |
13
15
  | Mixer Brush reservoir | `behaviour.exchange.reservoir` (`load`, `refill`, `loadsColour`) |
14
16
  | Wet engine (medium, water, pigment, body…) | `behaviour.surface` `wet` and `behaviour.wet` |
@@ -28,6 +28,15 @@
28
28
  dries it first (in the same undo step).
29
29
  - **Time** runs by itself in an editor on screen (`pause_wet_paint` stops it); for an agent with
30
30
  no screen, nothing moves until `advance_wet_paint` or `dry_paint`.
31
+ - **Extent limit (8 wet layers, 328 tiles).** The simulation holds at most 8 wet layers and 328
32
+ live tiles of 256 × 256 px across them (one wasm32 memory, under 4 GB). Water that spreads beyond
33
+ them is lost (`get_wet_paint` reports `activeTiles` per layer, so watch the total); dry
34
+ finished washes with `dry_paint` to free their tiles before painting another large wash.
35
+ - **Multi-threading and COOP / COEP.** Stepping wet tiles across Web Workers needs
36
+ `SharedArrayBuffer`, which browsers only expose on cross-origin isolated pages
37
+ (`Cross-Origin-Opener-Policy: same-origin` and `Cross-Origin-Embedder-Policy: require-corp` or
38
+ `credentialless`). Without cross-origin isolation the simulation runs on one thread, more slowly
39
+ but with identical results, and `get_wet_paint` says why in the kernel's `message`.
31
40
  - **The GPU only draws.** With WebGPU and a large wet area (16 tiles of 256 px by default), Engine B
32
41
  shades the wet paint on the GPU; the CPU engine still computes every step, so the painting,
33
42
  `dry_paint`, saving, export and replay come out the same with it on or off. `get_wet_paint`
@@ -66,6 +66,7 @@ This skill is the method. `pixerate-paint-editor` has the full tool reference.
66
66
  | Clean geometric lines (curves, circles, borders) | `stroke_path` with SVG path data and a `pressure` taper, on the Ink layer |
67
67
  | Flats | `marker` or a flat brush on a layer under the ink |
68
68
  | Patterned or textured flats (sky, gradient, galaxy) | `set_effect_paint` with a provider generator, then a flat dry brush on the Flats layer; `set_color` after |
69
+ | Textured passes (dry brush, sponge, foliage) | A brush with `tip.grain` (`set_brush` `{ tip: { grain: { source: "sanded", depth: 0.6 } } }`) or an image tip from the brush library, with `dynamics.angleJitter` and `flip` so repeats don't show |
69
70
  | Shadows | Clipped layer, `multiply` or `shadow`, a cool colour |
70
71
  | Light, glow | Clipped layer, `highlight` or `screen` |
71
72
  | Warm or cool a whole area | Clipped layer in `tint` |
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pixerate-vector
3
- description: How to draw and edit vector artwork in the Pixerate vector editor through its vector.* commands - adding shapes and SVG paths, text (point, area, on a path, styles, Create Outlines), symbols and instances, CAD constraints, artboards and frames with resizing constraints, connectors, fills, strokes, stacked fills and strokes with blend modes, drop shadows, glows and blurs, clipping and opacity masks, gradients (linear, radial, conic, mesh, freeform), stroke alignment, arrowheads, variable-width and tapered strokes, live corner radii, boolean operations and Pathfinder (unite, subtract, divide, trim), Shape Builder, paint bucket, path effects (trim, repeater, zig zag, offset), envelope warps and free distort, blends and morphing, anchor editing (join, smooth, simplify, knife, eraser), align, distribute, group, and SVG import and export. Use when creating an icon, logo, illustration, glyph, badge or any Bezier path artwork, or editing an SVG in a Pixerate vector editor.
3
+ description: How to draw and edit vector artwork in the Pixerate vector editor through its vector.* commands - adding shapes and SVG paths, text (point, area, on a path, styles, Create Outlines), symbols and instances, CAD constraints, artboards and frames with resizing constraints, connectors, stacked fills and strokes with blend modes, brushstrokes with brush character and paper grain, drop shadows, glows and blurs, clipping and opacity masks, gradients (linear, radial, conic, mesh, freeform), stroke alignment, arrowheads, variable-width and tapered strokes, live corner radii, boolean operations and Pathfinder (unite, subtract, divide, trim), Shape Builder, paint bucket, path effects (trim, repeater, zig zag, offset), envelope warps and free distort, blends and morphing, anchor editing (join, smooth, simplify, knife, eraser), align, distribute, group, and SVG import and export. Use when creating an icon, logo, illustration, glyph, badge or Bezier path artwork, or editing an SVG in a Pixerate vector editor.
4
4
  ---
5
5
 
6
6
  # Pixerate vector editor
@@ -20,7 +20,9 @@ carry a live effect stack. Every command is a `vector.*` command run with `run_c
20
20
  `{ type, stops: [{ offset, color, opacity?, midpoint? }], ... }`.
21
21
  - **Appearance**: a path can stack several fills and several strokes (bottom to top; all fills
22
22
  draw under all strokes), each with its own paint, `opacity`, `blendMode` and `visible`, and
23
- each stroke with its own width, alignment, dashes, caps, joins, arrowheads and profile. Layers
23
+ each stroke with its own width, alignment, dashes, caps, joins, arrowheads and profile, or a
24
+ **brushstroke** (`vector.addBrushstroke`, `vector.setBrushstroke`, `vector.listBrushes`) painted
25
+ along the outline with a dry or wet brush, a Mixer Brush, smudge or blender, gradient paint, seeded jitter and paper grain. Layers
24
26
  are addressed by `index` in their stack, 0 at the bottom. `fill` / `stroke` and the stroke
25
27
  settings of `vector.setStyle` are shorthands for the **top** layer; `null` clears the stack.
26
28
  Any node can also have a `blendMode` and `filters` (drop / inner shadow, outer / inner glow,
@@ -101,7 +103,7 @@ the pitfalls, and uses the commands below.
101
103
  than splitting text into separate nodes, so it still edits as one paragraph. Create Outlines
102
104
  only for final artwork or logos that must not depend on fonts; keep a copy as text if it may
103
105
  change.
104
- - **Paths, strokes, booleans, gradients, stacks, masks, shadows, connectors, constraints and
106
+ - **Paths, strokes, brushstrokes, booleans, gradients, stacks, masks, shadows, connectors, constraints and
105
107
  vertical text:** read [references/best-practices.md](references/best-practices.md) first.
106
108
  - **Non-destructive first.** Live booleans (`vector.boolean` with `live: true`), path effects
107
109
  and envelopes keep the operands editable. A destructive boolean or Pathfinder replaces its
@@ -373,12 +375,20 @@ contents whole while you work, without losing the mask.
373
375
 
374
376
  | Command | Use it to |
375
377
  | --- | --- |
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
- | `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`, default the brush's default colour (ballpoint blue, chalk), else the path's stroke; `keepColour` keeps the path's colour): 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
- | `vector.setBrushstroke` | Change brushstroke layer `index` of path `id`: brush (another brush without `color` takes its default colour, unless `keepColour`; giving the same `brushId` again takes that saved brush as the library has it now), 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`. |
378
+ | `vector.listBrushes` | List the brushes a brushstroke can use: the dry presets (pencils, pastels, charcoal, crayon, chalk, markers, inks, ballpoint, gel pen, fineliner, gradient and multi-colour brushes), wet paint (watercolour, gouache, wet oils), the Mixer Brush, smudge and blender, then saved brushes from the brush library the vector profile admits, with their sizes and `builtin`, plus the editor's `colorScheme` and `colorLocked`. Brushes it refuses (thick paint, particles, stickers, erasers, the wet tools) are refused by `vector.addBrushstroke` with the reason. A saved brush is kept in the layer, so the document renders without the library. |
379
+ | `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`, default the brush's default colour (ballpoint blue, chalk), else the path's stroke; `keepColour`, or a lock from `vector.setColorLocked`, keeps the path's colour): 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. `paint` is gradient paint sampled dab by dab along the path (`{ type: 'gradient', stops, map: { by: 'distance', length, repeat } }`, lengths in document units; also pressure, direction, across-tip, random; not velocity or time). Returns the stroke stacks. |
380
+ | `vector.setBrushstroke` | Change brushstroke layer `index` of path `id`: brush (another brush without `color` takes its default colour, unless `keepColour` or the colour is locked; giving the same `brushId` again takes that saved brush as the library has it now), size, pressure, colour, opacity, `seed` (a new seed is a new take on the same stroke; null: plain dabs), `paper` (replaced whole; null: no grain; {}: the default sanded paper) and `paint` (gradient paint, replaced whole; null: the colour again). Remove it with `vector.removeStroke`. |
381
+ | `vector.setColorLocked` | Lock (`locked: true`) or unlock brushstroke colours ("Keep my colour"): while locked, `vector.addBrushstroke` and `vector.setBrushstroke` with another brush keep the path's or layer's colour instead of the brush's default; an explicit `color` still applies. Strokes already made are unchanged. Respect a lock the person set. Editor state, not an undo step. Returns `{ colorScheme, colorLocked }`. |
382
+ | `vector.setColorScheme` | Set `scheme` `light` or `dark`: the side of light / dark default colours (chalk) that brushstrokes made from now on take. Existing strokes keep their colour. Usually the host's job. Returns `{ colorScheme, colorLocked }`. |
379
383
 
380
384
  Give brushstrokes a `seed` unless you want flat, even dabs: without one a charcoal or pastel line looks like a felt pen. Use different seeds on neighbouring strokes so they do not repeat each other, and `paper: null` for clean ink or marker lines that should not catch grain. The grain is laid in each path's own space, so it moves with the path.
381
385
 
386
+ **Colour along the stroke.** For colour that changes along the path, give the brushstroke `paint` (`by: 'distance'` with `length` about the path's length and `repeat: 'clamp'` runs the gradient once from start to end; `across-tip` loads the brush with several colours side by side; `pressure` follows the pressure profile). A gradient as the layer colour also works: its `strokeMode` `along` runs it from each subpath's start to its end, `across` lays it across the tip, and `within` (the default) lays it over the path's box. Gradient and multi-colour presets (`gradient-round`, `rainbow-marker`) paint their own gradient; their `paint` stops take the layer colour.
387
+
388
+ **Wet paint** (`watercolor-round`, `watercolor-wash`, `gouache`, `oil-round`) bleeds, blooms and granulates in its own small wet area, then dries. That takes a few seconds per stroke, so the canvas shows the paint as laid first and swaps in the settled stroke when it is ready; SVG and PDF exports always wait for it. Prefer a handful of wet strokes to dozens of short ones.
389
+
390
+ **Mixer Brush, smudge and blender** act on what lies beneath the stroke: the shapes below it in the document and the path's own fills and lower strokes. Put the path above the art it should work on (`vector.arrange`), give it no visible stroke of its own if only the effect should show, and draw it after the shapes are final: the stroke re-renders whenever something beneath it changes. They pick up fills (solid and gradient), plain strokes and other brushstrokes; not text, placed images, filters or masks. The result is drawn over the art, so a smudge can drag colour out onto bare paper but can't wipe the art away. The Mixer Brush paints from its reservoir (the layer colour) even with nothing beneath it; a smudge or blender over nothing draws nothing.
391
+
382
392
  ### Editor settings
383
393
 
384
394
  | Command | Use it to |
@@ -19,6 +19,7 @@ Detail for `SKILL.md`'s best practices, by topic.
19
19
  `outside` for outlines around artwork; open paths always stroke centred. Use round caps and
20
20
  joins for friendly line icons, miter for geometric ones. Keep one stroke width across an icon
21
21
  set (for example 1.5 or 2 units on a 24-unit grid).
22
+ - **Brushstrokes and brush character.** Use `vector.addBrushstroke` (after `vector.listBrushes`) when a path should look hand-drawn (pencil, charcoal, pastel, chalk, marker, ink) while staying an editable Bezier path. Pass a `seed` for pencil, pastel, charcoal and chalk (without one the brush paints plain, even dabs with no jitter or grain), and a different seed on neighbouring paths so their strokes do not repeat. Use a `pressure` taper such as `[0.2, 1, 0.2]` for strokes that start and end lightly. For clean ink or marker lines leave out the seed, or pass `paper: null` to keep the jitter without grain. Remove a brushstroke layer with `vector.removeStroke`.
22
23
  - **Icon grid.** Use a square artboard (24, 32 or 48 units), keep a padding margin, place
23
24
  coordinates on whole or half units so strokes render crisp, and align optical centres.
24
25
  - **Gradients.** Two to four stops are usually enough. Use `interpolation: "oklab"` for
@@ -180,3 +180,22 @@ inside the intended face.
180
180
  | transparency | 0–100 % see-through (50) | – | The lens's own fill, see-through. |
181
181
 
182
182
  The lens shows what is drawn before it (below it), follows edits there, and keeps its own bounds. Move it with `vector.transform` like any path. Remove it with `vector.removeLens`.
183
+
184
+ ## Brushstrokes (`vector.listBrushes`, `vector.addBrushstroke`, `vector.setBrushstroke`)
185
+
186
+ A brushstroke is a stroke layer (kind brushstroke) in a path's appearance stack that paints a dry
187
+ brush along the path's outline; the path stays editable and the stroke follows it.
188
+
189
+ | Parameter | Meaning |
190
+ | --- | --- |
191
+ | `ids` / `id` | `vector.addBrushstroke`: paths or groups (`ids`, default the selection; a group paints every path inside). `vector.setBrushstroke`: one path `id` plus the stroke layer `index` (from `vector.getAppearance`, 0 the bottom). |
192
+ | `brushId` | Required on add. A preset or saved brush id from `vector.listBrushes` (dry brushes only: pencils, pastels, charcoal, crayon, chalk, markers, inks, ballpoint, gel pen, fineliner). Wet, thick, particle, sticker and tool brushes are refused. |
193
+ | `size` | Brush diameter in document units, up to 500 (default the brush's own; `null` on set restores it). |
194
+ | `pressure` | Pressure profile 0–1 spread evenly from start to end (`[0.2, 1, 0.2]` tapers both ends; default full pressure; `null` on set). |
195
+ | `color` | CSS colour. Default: the brush's own default colour (e.g. the ballpoint's blue) unless `keepColour`, else the path's stroke colour, else black. |
196
+ | `keepColour` | Use the path's (or, on set with another `brushId`, the layer's) colour instead of the brush's default colour. |
197
+ | `opacity` | Layer opacity 0–1 (default 1). `index` on add places the layer in the stroke stack (default on top). |
198
+ | `seed` | Integer 0–2147483647 that turns on the brush's character: seeded size, angle, opacity and scatter jitter plus paper grain. The same seed renders the same stroke. Without a seed (or `null` on set) the brush paints plain, even dabs. |
199
+ | `paper` | Applies with a `seed`: `{ pattern?, seed?, scale?, depth? }` for the grain (pattern `sanded` by default, `rough`, `cold-pressed`, `canvas`, `linen`; `scale` 0.25–8; `depth` 0–1, default 0.5). Omitted: the sanded paper; `null`: jitter without grain. On set it is replaced whole and `{}` is the default paper. |
200
+
201
+ Remove a brushstroke with `vector.removeStroke`, like any stroke layer.