@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
@@ -571,6 +571,12 @@ export interface VectorSceneRenderOptions {
571
571
  };
572
572
  /** With `viewportSize`: groups, text and instances smaller than this many screen pixels draw as one rectangle in their main colour (default 2; 0 turns it off). */
573
573
  lod?: number;
574
+ /**
575
+ * `live` (default): wet brushstrokes show the paint as laid until they settle (re-render when
576
+ * `onVectorBrushstrokesSettled` fires; the controller notifies then). `settled`: settle them
577
+ * first, as exports must.
578
+ */
579
+ brushstrokes?: 'live' | 'settled';
574
580
  }
575
581
  /**
576
582
  * The document layer. Render the nodes inside `<g transform={transform}>` and `defs` in a
@@ -571,6 +571,12 @@ export interface VectorSceneRenderOptions {
571
571
  };
572
572
  /** With `viewportSize`: groups, text and instances smaller than this many screen pixels draw as one rectangle in their main colour (default 2; 0 turns it off). */
573
573
  lod?: number;
574
+ /**
575
+ * `live` (default): wet brushstrokes show the paint as laid until they settle (re-render when
576
+ * `onVectorBrushstrokesSettled` fires; the controller notifies then). `settled`: settle them
577
+ * first, as exports must.
578
+ */
579
+ brushstrokes?: 'live' | 'settled';
574
580
  }
575
581
  /**
576
582
  * The document layer. Render the nodes inside `<g transform={transform}>` and `defs` in a
@@ -1,5 +1,6 @@
1
1
  import type { GenerationProvenance } from '../generation/provenance.cjs';
2
2
  import type { CanvasGradientDef } from '../canvas/gradient.cjs';
3
+ import type { CanvasBrushPaint } from '../canvas/brushTypes.cjs';
3
4
  import type { WarpMesh, WarpStyle } from '../geom/types.cjs';
4
5
  import type { GrainPattern } from '../paint/grain.cjs';
5
6
  import type { Brush } from '../brushes/types.cjs';
@@ -282,7 +283,20 @@ export interface VectorBrushstroke {
282
283
  seed?: number;
283
284
  /** The paper the grain comes from (with `seed`); `null` for no grain. */
284
285
  paper?: VectorBrushstrokePaper | null;
286
+ /**
287
+ * Gradient paint sampled dab by dab along the path, instead of the layer's colour (which still
288
+ * gives the alpha and fills `paint` stops): by distance travelled (document units), pressure,
289
+ * tilt, direction, across the tip or at random. Without it, a gradient layer paint is laid
290
+ * along, across or within the stroke (its `strokeMode`), and a brush with its own gradient
291
+ * paints that.
292
+ */
293
+ paint?: VectorBrushstrokePaint;
285
294
  }
295
+ /**
296
+ * A brushstroke's gradient paint (the paint engine's `PaintSource`, as canvas brushed strokes take
297
+ * it: `CanvasBrushPaint`). The `velocity` and `time` mappings are not allowed: paths have no timing.
298
+ */
299
+ export type VectorBrushstrokePaint = CanvasBrushPaint;
286
300
  /** One stroke of a path's stroke stack (`VectorStyle.strokes`): a paint plus its own stroke settings. */
287
301
  export interface VectorStrokeLayer extends VectorPaintLayer, VectorStrokeSettings {
288
302
  /** Default `paint`. See `VectorStrokeLayerKind`. */
@@ -1,5 +1,6 @@
1
1
  import type { GenerationProvenance } from '../generation/provenance.js';
2
2
  import type { CanvasGradientDef } from '../canvas/gradient.js';
3
+ import type { CanvasBrushPaint } from '../canvas/brushTypes.js';
3
4
  import type { WarpMesh, WarpStyle } from '../geom/types.js';
4
5
  import type { GrainPattern } from '../paint/grain.js';
5
6
  import type { Brush } from '../brushes/types.js';
@@ -282,7 +283,20 @@ export interface VectorBrushstroke {
282
283
  seed?: number;
283
284
  /** The paper the grain comes from (with `seed`); `null` for no grain. */
284
285
  paper?: VectorBrushstrokePaper | null;
286
+ /**
287
+ * Gradient paint sampled dab by dab along the path, instead of the layer's colour (which still
288
+ * gives the alpha and fills `paint` stops): by distance travelled (document units), pressure,
289
+ * tilt, direction, across the tip or at random. Without it, a gradient layer paint is laid
290
+ * along, across or within the stroke (its `strokeMode`), and a brush with its own gradient
291
+ * paints that.
292
+ */
293
+ paint?: VectorBrushstrokePaint;
285
294
  }
295
+ /**
296
+ * A brushstroke's gradient paint (the paint engine's `PaintSource`, as canvas brushed strokes take
297
+ * it: `CanvasBrushPaint`). The `velocity` and `time` mappings are not allowed: paths have no timing.
298
+ */
299
+ export type VectorBrushstrokePaint = CanvasBrushPaint;
286
300
  /** One stroke of a path's stroke stack (`VectorStyle.strokes`): a paint plus its own stroke settings. */
287
301
  export interface VectorStrokeLayer extends VectorPaintLayer, VectorStrokeSettings {
288
302
  /** Default `paint`. See `VectorStrokeLayerKind`. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pixerate/editor",
3
- "version": "0.266.2",
3
+ "version": "0.268.0",
4
4
  "description": "UI-agnostic rich text and media editing core library",
5
5
  "author": "Pixerate",
6
6
  "license": "MIT",
@@ -162,6 +162,7 @@ refuse.
162
162
  | `update_effect` | Make an applied effect again with new `values`, in the same place (`effect` is its instance id). |
163
163
  | `remove_effect` | Remove an applied effect: its commands, shots it alone filled and a vector effect's group. |
164
164
  | `add_vector_effect` | Add a vector effect's own picture as an artwork group (fitted into `box`) with its motion as new shots. For an effect that draws along a path (`search_effects` `accepts: ["path"]`: a signature, a route), `path: { node }` runs it along a path node and lands it on the node, `path: { text }` writes words. Use it when the effect should trace the path itself; use `follow` or a draw-on to move or reveal your own art. Returns the instance and group ids. |
165
+ | `set_vector_effect_path` | Give a vector effect already added (`instance` from `add_vector_effect` or `list_applied_effects`) a new path in place: `path: { node }` runs it along a path node and lands it on the node, `path: { text }` writes words and keeps the picture where it was. Call it after reshaping the node an effect runs along (the path is a copy), or to switch between a node and words; clear the path with `update_effect` setting the path control to `""`. One undo step. Returns the instance, the `control` set and its new path data `value`. |
165
166
  | `list_camera_moves` | List the provider's camera moves for a `role` (`transition`: how the camera gets into a shot; `hold`: how it moves while the shot holds), with `durationMs` and `cut`. Returns `available: false` without camera moves. Call before `set_shot_camera_move`; `describe_effect` gives a move's controls and presets. |
166
167
  | `set_shot_camera_move` | Give a shot a camera move (`shot`, `role`, `move`, optional `ms` for a transition, `preset`, `values`): it is saved on the shot's camera as `in` or `during`. Returns the shot's camera and the `window` (ms) the move plays in. A wrong-role id is refused naming the moves that fit. |
167
168
  | `clear_shot_camera_move` | Remove a shot's move for a `role`, keeping its framing. Returns the shot's camera (null when nothing is left). |
@@ -1,6 +1,6 @@
1
1
  ---
2
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.
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, preview_brush, add_brush_source and list_brush_sources - brushes made of facets (kind, medium and variety or tool, tip, marks, dynamics, behaviour, light, motion, colour), image tips and tip grain made from image sources, 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, make a brush from an image (a scanned mark, a leaf, a texture), give a brush grain, find a brush for a medium (pencil, ink, watercolour, oil, airbrush, highlighter), or make a brush work in another editor.
4
4
  ---
5
5
 
6
6
  # Brushes
@@ -15,13 +15,13 @@ 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` 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. |
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`, any sticker ref from `find_stickers`, or an image source `source:<id>` from `list_brush_sources`), size in px, hardness (an image tip fades towards its edges below 1), spacing, opacity, flow, blend mode, and `grain`: texture inside each dab (`source`: a pattern such as `rough`, `canvas`, `sanded`, or `source:<id>`; `mode` `texturised` stays put on the canvas, `moving` rolls with the tip; `scale`, `depth`, `contrast`, `invert`). |
19
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
- | Dynamics | Pen feel: pressure to size and opacity, smoothing, tilt, velocity, jitter, rotation. |
20
+ | Dynamics | Pen feel: pressure to size and opacity, smoothing, tilt, velocity, jitter (`sizeJitter`, `angleJitter` for rotation, `opacityJitter`, `scatter`), rotation, `count` (dabs per spacing step, each jittered) and `flip` (mirror dabs at random: `x`, `y`, `xy`). |
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). 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. Canvas strokes and vector brushstrokes take it too, when they are given no colour. |
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. Canvas strokes and vector brushstrokes take it too, when they are given no colour. Canvas and vector have the same lock (`canvas.setColorLocked`, `vector.setColorLocked`), and a canvas colour taken from a light / dark default follows the scheme like the paint editor's; vector strokes already made keep theirs. |
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**
@@ -31,7 +31,16 @@ and **wet oil** are `paint` on `surface: 'wet'`; **impasto** is `surface: 'thick
31
31
  pressure to size); **crayon** and **chalk** are `dry` with high `grain`; a **highlighter** is
32
32
  `ink` / `highlighter`, a multiply chisel tip with a little light, `spill` radius 0, fluorescent
33
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.
34
+ **sticker** brushes are `marks` particles or objects on a `stamp` tip. An **image brush** (a
35
+ scanned charcoal mark, a leaf, a dry-brush texture) is a `stamp` tip whose stamp is an image
36
+ source, usually with `tip.grain`, `angleJitter`, `flip` and a little `scatter` so repeats don't
37
+ show.
38
+
39
+ **Image sources** are the images tips and grain are made from, kept in the library with their
40
+ bytes (a grey mask, at most 512 px) and their licence. `add_brush_source` makes one from a PNG:
41
+ the marks are read from its transparency, its dark parts (a scan on paper) or its light parts
42
+ (white on black). Library files (`export_brushes`) and saved paintings carry the sources their
43
+ brushes name, so they paint and replay the same anywhere.
35
44
 
36
45
  ## Host profiles
37
46
 
@@ -40,10 +49,10 @@ paints without (warnings), each with a fix; lists and writes use the current hos
40
49
 
41
50
  | Host | Renders |
42
51
  | --- | --- |
43
- | Paint editor (`paint`) | Everything. |
44
- | 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. Pick one with `set_brush_preset`. |
45
- | Canvas (`canvas`) | Dry dab brushes along a path (no tools, wet, thick, particles, objects or light); velocity is ignored. Draw with one as `canvas.drawFreehand` `brush`. |
46
- | Vector strokes (`vector`) | As canvas, but light brushes paint as plain colour. Draw with one as `vector.addBrushstroke` `brushId`. |
52
+ | Paint editor (`paint`) | Everything, image tips and tip grain included. |
53
+ | Image editor (`image`) | Dry dabs on round, flat and chisel tips, the Mixer Brush and the eraser; no wet or thick surface, particles, objects, stamp or image tips, light or motion; it paints without tip grain and `count`. Pick one with `set_brush_preset`. |
54
+ | Canvas (`canvas`) | Dry dab brushes along a path (no tools, wet, thick, particles, objects, stamp or image tips, or light); velocity, tip grain and `count` are ignored. Draw with one as `canvas.drawFreehand` `brush`. |
55
+ | Vector strokes (`vector`) | As canvas, plus wet paint (watercolour, gouache, wet oils: simulated in the stroke's own area, then dried), the Mixer Brush, and the smudge and blender tools (acting on the shapes beneath the stroke); light brushes paint as plain colour. The wet tools (water, hair dryer, blow, wet blend, wet drag, blot), thick paint, particles, objects and erasers are refused. Draw with one as `vector.addBrushstroke` `brushId`. |
47
56
 
48
57
  Every host lists the presets its profile admits and the library's saved brushes it admits, and
49
58
  each registers these tools with its own profile, so `list_brush_presets` shows that host's brushes.
@@ -66,7 +75,11 @@ drawn.
66
75
  in the editor (`set_brush` with the new id, then `paint_stroke`).
67
76
  5. **Tune it.** `update_brush` with the facets to change, or `unset` to drop whole facets. It
68
77
  returns the leaf paths `changed`. Built-in presets can't be changed: `duplicate_brush` first.
69
- 6. **Share it.** `export_brushes` writes a library file; `import_brushes` adds one (version 1
78
+ 6. **From an image.** `add_brush_source` with the PNG (`channel` when `auto` picks the wrong
79
+ part; keep the `licence` it came with), then `create_brush` with `tip: { shape: "stamp",
80
+ stamps: ["source:<id>"] }` for an image tip, or `tip.grain.source` for grain.
81
+ `list_brush_sources` lists them and which brushes use each.
82
+ 7. **Share it.** `export_brushes` writes a library file; `import_brushes` adds one (version 1
70
83
  files of the paint editor's older brushes are migrated). `check_brush` with `host` before
71
84
  handing a brush to another editor.
72
85
 
@@ -92,6 +105,13 @@ drawn.
92
105
  to make its preview visible: choosing the brush makes that default the user's colour.
93
106
  - Stamp tips paint the sticker's shape in the paint colour; to keep a sticker's own colours, use
94
107
  `marks` objects instead.
108
+ - Image tips: crop the image to the mark and give it a transparent or plain background, so the
109
+ dab is the mark and not its box. Spacing 0.2–0.4 for a continuous mark, above 1 for separate
110
+ prints; add `angleJitter`, `flip` and `scatter` so the same image doesn't repeat visibly.
111
+ - Grain: `texturised` grain (default) is a texture the stroke reveals, like paper; `moving` grain
112
+ belongs to the tip and rolls with it (a sponge, a roller). Depth 0.4–0.7 reads as texture;
113
+ 1 leaves only the peaks. Use `tip.grain` for the tool's texture and `behaviour.grain` for the
114
+ paper's.
95
115
 
96
116
  ## Pitfalls
97
117
 
@@ -107,6 +127,11 @@ drawn.
107
127
  (`editor:star`…). A ref the host's sticker library can't load is refused with the closest
108
128
  refs. Only the paint editor renders stamp tips and scatter marks; canvas, vector and the image
109
129
  editor refuse them (`check_brush` says so).
130
+ - **Image sources.** A `source:<id>` the library doesn't have is refused, with the closest ids;
131
+ add it first. Replacing a source's image (`overwrite`) changes every brush that names it, but
132
+ not strokes already painted. Objects marks take stickers only, so an image source there is
133
+ dropped (`problems` says so). Brush packs are often licensed for personal use only: keep the
134
+ `licence` text with the source.
110
135
  - **Out-of-range values** are refused by the schema (`tip.size` is 1..2500 px); a stored brush
111
136
  is clamped on import and reported in `problems`.
112
137
  - **The paint editor paints from this library.** Its `set_brush` picks a brush by id here and
@@ -133,3 +158,5 @@ drawn.
133
158
  | `export_brushes` | Library file (pixerate-brushes v2) of the saved brushes or the `ids` given. Returns `file`, for `import_brushes`. |
134
159
  | `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`. |
135
160
  | `preview_brush` | Only when the host renders previews: a sample stroke with a brush (`id`, `width`, `height`, `color`) as an image. |
161
+ | `add_brush_source` | Add an image (`name`, `image` PNG data: URL, optional `id`, `channel` auto / alpha / darkness / lightness, `invert`, `licence`, `origin`, `overwrite`) as a source for image tips and grain. Returns `id`, `ref` (`source:<id>`), `width`, `height`, `problems`, `replaced`. Then `create_brush` / `update_brush` with the ref in `tip.stamps` or `tip.grain.source`. |
162
+ | `list_brush_sources` | The library's image sources (`text`, `offset`, `limit`). Returns `sources` (id, ref, name, width, height, licence, origin, `usedBy` brush ids) and `total`. Call before naming a `source:` ref. |
@@ -26,8 +26,9 @@ This skill is the method. `pixerate-brushes` has the full tool reference.
26
26
 
27
27
  Ask when you cannot tell from the request; say what you assumed when you can.
28
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).
29
+ - **Host:** which editor will use it (paint by default; canvas strokes take only dry dab brushes,
30
+ vector strokes dry and wet dab brushes, the Mixer Brush, smudge and blender; the image editor no
31
+ wet, thick or particle brushes).
31
32
  - **Medium and character:** what it imitates (pencil, ink, chalk, watercolour, marker…), and
32
33
  the size, softness and texture.
33
34
  - **Colour:** a default colour or palette, or the user's own colour (the default).
@@ -53,9 +54,11 @@ Ask when you cannot tell from the request; say what you assumed when you can.
53
54
  | Situation | Approach |
54
55
  | --- | --- |
55
56
  | 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
+ | For canvas strokes | Base on a dry preset (pencil, pastel, charcoal, marker, ink); no wet surface, particles, stickers or tools; no light. |
58
+ | For vector strokes | As canvas, but a wet surface (watercolour, gouache, wet oil), the Mixer Brush and the smudge and blender tools work too; no thick paint, particles, stickers, erasers or wet tools. |
57
59
  | 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. |
60
+ | A texture (chalk, charcoal, dry brush) | Raise `behaviour.grain` (0.8..1) and add `dynamics.sizeJitter`; test on rough paper. For texture that shows on any paper, give the tip its own grain: `tip.grain` `{ source: "sanded", depth: 0.6 }` (paint editor; other hosts paint without it). |
61
+ | A brush from an image (a scanned mark, a leaf, a texture) | `list_brush_sources`, or `add_brush_source` with the PNG (keep its `licence`); then `tip` `{ shape: "stamp", stamps: ["source:<id>"] }`, spacing 0.2–0.4, `dynamics.angleJitter`, `flip` and a little `scatter`. The image as grain instead: `tip.grain.source`. Paint editor only. |
59
62
  | A light brush (neon, glow) | `light.amount`, and paint it on dark paper; a highlighter keeps `light.spill.radius` 0. |
60
63
  | 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
64
  | 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. |
@@ -72,6 +75,11 @@ basedOn: "highlighter", colour: { default: "#ff6ec7" } }`, then `check_brush`.
72
75
  `{ id: "board-chalk", name: "Board chalk", basedOn: "chalk", colour: { default: { light:
73
76
  "#334155", dark: "#f8fafc" } } }`: white chalk in dark mode, slate on light paper.
74
77
 
78
+ **A charcoal brush from a scan.** `add_brush_source` `{ name: "Charcoal scan", image: <PNG>,
79
+ channel: "darkness" }` → `create_brush` `{ id: "scan-charcoal", name: "Scan charcoal", basedOn:
80
+ "charcoal-stick", tip: { shape: "stamp", stamps: ["source:charcoal-scan"], spacing: 0.25, grain:
81
+ { source: "rough", depth: 0.5 } }, dynamics: { angleJitter: 0.3, flip: "xy" } }` → `preview_brush`.
82
+
75
83
  **Port a wet brush to vector.** `check_brush` `{ id, host: "vector" }`; `create_brush` from the
76
84
  `degraded` brush with `id` `<id>-vector`; `check_brush` again.
77
85
 
@@ -80,7 +88,8 @@ basedOn: "highlighter", colour: { default: "#ff6ec7" } }`, then `check_brush`.
80
88
  - **Starting from nothing.** A brush without `basedOn` gets engine defaults (a hard round dab);
81
89
  it rarely looks like the medium asked for.
82
90
  - **A host that refuses the base.** A wet or particle preset can't be the base of a canvas
83
- brush; set `behaviour.surface` to `dry` or start from a dry preset.
91
+ brush, nor a thick (impasto) preset of a vector one; set `behaviour.surface` to `dry` (vector
92
+ also takes `wet`) or start from a dry preset.
84
93
  - **Lists replace.** Giving `colour.palette` or `tip.stamps` replaces the whole list; include
85
94
  every entry you want.
86
95
  - **Changing a built-in.** `update_brush` refuses built-ins; duplicate first.
@@ -91,3 +100,4 @@ basedOn: "highlighter", colour: { default: "#ff6ec7" } }`, then `check_brush`.
91
100
  - `create_brush`, `update_brush`, `duplicate_brush`: make and tune the brush.
92
101
  - `check_brush`: prove it works for the host, and get a degraded spec when it doesn't.
93
102
  - `preview_brush`: look at a sample stroke when the host renders previews.
103
+ - `add_brush_source`, `list_brush_sources`: images for image tips and grain.
@@ -1,17 +1,17 @@
1
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.",
2
+ "prompt": "A teammate's brush team-knife is in the library, but it won't work for vector brushstrokes. Make a copy called team-knife-vector that does, and leave theirs as it is.",
3
3
  "fixture": {
4
4
  "editor": "brushes",
5
- "setup": [{ "tool": "create_brush", "args": { "id": "team-wash", "name": "Team wash", "basedOn": "watercolor-wash", "tags": ["team"] } }]
5
+ "setup": [{ "tool": "create_brush", "args": { "id": "team-knife", "name": "Team knife", "basedOn": "palette-knife", "tags": ["team"] } }]
6
6
  },
7
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" } } } }
8
+ { "why": "the copy works in the vector editor", "tool": "check_brush", "args": { "id": "team-knife-vector", "host": "vector" }, "expect": { "ok": true } },
9
+ { "why": "the copy keeps the knife's tip", "tool": "describe_brush", "args": { "id": "team-knife-vector" }, "expect": { "brush": { "tip": { "size": 48, "roundness": 0.18 } } } },
10
+ { "why": "the teammate's brush still lays thick paint", "tool": "describe_brush", "args": { "id": "team-knife" }, "expect": { "brush": { "behaviour": { "surface": "thick" } } } }
11
11
  ],
12
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" } }
13
+ { "tool": "check_brush", "args": { "id": "team-knife", "host": "vector" } },
14
+ { "tool": "create_brush", "args": { "id": "team-knife-vector", "name": "Team knife (vector)", "basedOn": "team-knife", "behaviour": { "surface": "wet" } } },
15
+ { "tool": "check_brush", "args": { "id": "team-knife-vector", "host": "vector" } }
16
16
  ]
17
17
  }
@@ -100,7 +100,14 @@ you to. Withdraw stale ones with `reject_proposal`: when the user changes what a
100
100
  stroke always looks the same, and neighbouring strokes share one sheet of paper. Pick the brush
101
101
  for the look, then the colour; for a sketch, use one or two brushes rather than all of them.
102
102
  A brush with a default colour (ballpoint blue, highlighter yellow, chalk) paints in it when
103
- you pass no `color`; pass `color` to use another.
103
+ you pass no `color`; pass `color` to use another. Light / dark defaults (chalk) resolve for
104
+ the canvas's colour scheme (`canvas.setColorScheme`; the host usually matches its theme).
105
+ - **Setting up the pen for the person, not drawing.** `canvas.selectBrush` chooses the brush the
106
+ person draws with by hand, as the brush picker does: it takes the brush's default colour unless
107
+ they locked theirs (`canvas.setColorLocked`, "Keep my colour") or you pass `keepColour`. A
108
+ default taken this way follows the scheme until another colour is set. Respect a lock the
109
+ person set; don't unlock it to change their colour. These are editor state, not undo steps,
110
+ and don't affect `canvas.drawFreehand`, which takes its own `brush` and `color`.
104
111
  - **Annotate with gestures, not points.** To mark up work the way a designer marks up a print,
105
112
  use the gesture commands (`canvas.circle`, `canvas.underline`, `canvas.arrow`,
106
113
  `canvas.bracket`, `canvas.strike`, `canvas.cross`, `canvas.check`) with node ids
@@ -254,6 +261,9 @@ character: seeded jitter and the paper grain they catch) and, when a width was c
254
261
  | Command | Use it to |
255
262
  | --- | --- |
256
263
  | `canvas.drawFreehand` | Draw a pen or highlighter stroke through `points` (`[x, y]` pairs in canvas units, in drawing order; one undo step). `tool` picks the defaults (`pen`: blue, width 3; `highlighter`: yellow, width 24, opacity 0.35); `color`, `width`, `opacity` override them (with a `brush` and no `color`, the brush's default colour if it has one; with a `brush`, `width` is the brush diameter and defaults to the preset's own size). Pass `brush` (a preset or saved brush id from `canvas.listBrushes`, or tip settings) to paint it with a dry brush, plus `pressure` / `tiltX` / `tiltY` profiles spread along its length (`[0.2, 1, 0.3]` swells in the middle). Add `paint` (only with `brush`) for gradient paint that changes colour along the stroke: `{ "type": "gradient", "stops": [...], "map": { "by": "distance", "length": 200 } }` (length in canvas units), or `by` `pressure`, `tilt`, `direction`, `across-tip`, `random`. Returns the node `id`, its `bounds` and the stored `brush` (with `paint` normalised: defaults filled in, colours lowercased). |
264
+ | `canvas.selectBrush` | Choose the brush the person draws with by hand (`brushId` from `canvas.listBrushes`, or `null` for a plain vector stroke), at its own size, taking its default colour unless the colour is locked or `keepColour`. Draws nothing. Returns `{ brush, color, colorScheme, colorLocked, colorFollowsScheme }`. |
265
+ | `canvas.setColorLocked` | Lock (`locked: true`) or unlock the hand-drawing colour ("Keep my colour"): while locked, `canvas.selectBrush` keeps it. Returns the same settings as `canvas.selectBrush`; call it (or `canvas.selectBrush`) to read whether the person locked it. |
266
+ | `canvas.setColorScheme` | Set `scheme` `light` or `dark`: picks the side of light / dark default colours for `canvas.selectBrush` and `canvas.drawFreehand`; a colour taken from such a default follows, strokes already drawn keep theirs. Usually the host's job. Returns the same settings as `canvas.selectBrush`. |
257
267
  | `canvas.listBrushes` | Read-only: the brushes `canvas.drawFreehand` and the gestures accept as `brush`: the dry presets, then saved brushes from the brush library the canvas can paint (`id`, `name`, `category`, the brush's own `size`, which a stroke uses unless you pass `width`, and `builtin`). Call it before the first brushed stroke instead of guessing ids. |
258
268
 
259
269
  Gestures draw one hand-drawn stroke node each (one undo step) through the same path as
@@ -1,17 +1,19 @@
1
1
  ---
2
2
  name: pixerate-image-editor
3
- description: How to colour-correct, composite and retouch a photo in the Pixerate image editor through its agent tools - layers (pixel, adjustment, group, shape, type, annotation and smart object layers; masks, clipping, blend modes, merge, filters), smart objects and smart filters, shape and type layers, layer effects, adjustments, selections and masks, Free Transform, crop, rotate, flip, painting and the Mixer Brush, retouching (Clone Stamp, Healing Brush, Spot Healing, Smudge, Dodge, Burn, Sponge), undo, PSD, histogram and previews. Use when asked to edit, grade, correct, crop, straighten or mask an image, remove a blemish or spot, clone, dodge or burn, add a shadow, glow or outline, transform or warp a layer, draw a shape or arrow, add or edit text, make a smart object, or open or save a PSD, or when list_layers, set_adjustments, add_layer_effect, transform_layer, add_shape_layer, add_text_layer, paint_stroke, clone_stamp, spot_heal, import_psd or render_preview are available.
3
+ description: How to colour-correct, composite and retouch a photo in the Pixerate image editor through its agent tools - layers (pixel, adjustment, group, shape, type, annotation, smart object; masks, clipping, blend modes, filters), 16/32-bit depth, colour management (profiles, proofing, CMYK, Lab), smart objects and smart filters, provider effects, layer effects, adjustments, selections and masks, Free Transform, crop, rotate, flip, painting and Mixer Brush, retouching (Clone Stamp, Healing, Spot Healing, Smudge, Dodge, Burn, Sponge, Blur, Sharpen, History Brush), undo, PSD, histogram and previews. Use when editing, grading, cropping, masking or retouching an image, adding effects, shapes or text, converting profiles or bit depth, transforming layers, or when list_layers, set_adjustments, add_layer_effect, apply_effect, transform_layer, add_text_layer, paint_stroke, clone_stamp, spot_heal, blur_stroke, sharpen_stroke, history_brush, set_bit_depth, convert_to_profile or import_psd are available.
4
4
  ---
5
5
 
6
6
  # Image editor
7
7
 
8
- The image editor holds a layer stack over a canvas the size of the source image. Opening an
8
+ The image editor holds a layer stack over a canvas the size of the source image (8-, 16- or
9
+ 32-bit depth, with RGB, CMYK and Lab colour management, ICC profiles and soft proofing). Opening an
9
10
  image gives one pixel layer, **Background**, whose pixels are the source and whose `develop`
10
11
  filter holds the base adjustments (the base controls edit the develop filter of the active pixel
11
12
  layer). Adjustment layers sit above it, each through its mask, opacity and blend mode, and
12
13
  annotations are shape and type layers on top. Edits never touch the source pixels; `list_layers`
13
14
  shows the whole stack. Pixel, group and smart object layers can carry live effects; a smart
14
- object places contents through a transform and smart filters; shape layers hold vector paths and
15
+ object places contents through a transform and smart filters (including provider effects, which
16
+ also add generator and overlay effect layers); shape layers hold vector paths and
15
17
  type layers live text, re-rendered on every change. There is also an optional selection and a
16
18
  crop, rotation and flip. See `pixerate-agent-tools` for errors and batching.
17
19
 
@@ -95,9 +97,9 @@ Boxes, badges, arrows and callouts are shape layers (`add_shape_layer`), text is
95
97
  (`add_text_layer`); both stay sharp and take effects, masks and blends. Read
96
98
  [references/shapes-and-type.md](references/shapes-and-type.md) before drawing or editing either.
97
99
 
98
- ### Layer effects
100
+ ### Layer and provider effects
99
101
 
100
- Live per-layer effects (shadows, glows, bevel, overlays, strokes): see `references/layer-effects.md`.
102
+ Live per-layer styles (shadows, glows, bevel, overlays, strokes): see [references/layer-effects.md](references/layer-effects.md). Provider filter smart filters and generator / overlay effect layers (`search_effects`, `describe_effect`, `apply_effect`, `add_effect_layer`): see [references/provider-effects.md](references/provider-effects.md).
101
103
 
102
104
  ### Paths and cutout edges
103
105
 
@@ -110,13 +112,15 @@ Keep outlines as paths (`make_work_path`, `add_path`) and edit their anchors: se
110
112
  Paint on its own layer (`add_pixel_layer`), one stroke per call and one undo step, limited by a
111
113
  selection or locked transparency. Read [references/painting.md](references/painting.md) first.
112
114
 
113
- ### Colour management
115
+ ### Bit depth and colour management
114
116
 
115
- Working space, profiles, soft proofing and print conversions: read [references/color-management.md](references/color-management.md).
117
+ Switch between 8-, 16- and 32-bit (float, linear light) with `set_bit_depth` (`references/photoshop-files.md`). For working space profiles (`assign_profile`, `convert_to_profile`), soft proofing (`set_proof_setup`) and CMYK / Lab print modes (`set_color_mode`, `convert_colors`), read [references/color-management.md](references/color-management.md).
116
118
 
117
119
  ### Retouching
118
120
 
119
- Heal, clone, dodge and burn on layers above the photo (`sample: "current-below"`): load
121
+ Heal and clone on layers above the photo (`sample: "current-below"`); dodge, burn, sponge, blur
122
+ (`blur_stroke`), sharpen (`sharpen_stroke`) and History Brush (`history_brush`) change the pixels
123
+ they touch, so run them on a duplicate. Load
120
124
  `pixerate-image-portrait-retouch`, and read [references/retouching.md](references/retouching.md) first.
121
125
 
122
126
  ### Photoshop files
@@ -136,11 +140,11 @@ in the source frame at the document's bit depth, colour mode and profile. Read
136
140
  - **Blend modes for tone-only or colour-only changes:** `luminosity` applies a curves or
137
141
  levels layer without shifting hue or saturation; `color` applies a tint without changing
138
142
  brightness; `soft-light` / `overlay` for gentle contrast.
139
- - **`pigment` mixes like paint.** On a layer, a fill or a brush, `pigment` mixes the colour into
143
+ - **`pigment` and painterly modes mix like paint.** On a layer, a fill or a brush, `pigment` mixes the colour into
140
144
  what is below as real paint would (Kubelka-Munk): blue into yellow gives green, white gives
141
145
  tints, and opacity is the share of new paint (0.5 is half and half). Use it for glazes,
142
- natural colour mixing and painterly strokes; it is not a Photoshop mode, so keep it off
143
- layers that must survive a PSD round trip. It costs about ten times a normal blend.
146
+ natural colour mixing and painterly strokes; neither `pigment` nor the painterly modes (`watercolor`, `tint`, `highlight`, `shadow`) are Photoshop modes (`export_psd` writes them as Normal), so keep them off
147
+ layers that must stay editable in a PSD round trip. `pigment` costs about ten times a normal blend.
144
148
  - **Name layers** (`name`) by intent ("Sky darken", "Warm skin"), so the stack stays readable.
145
149
  - **Effects, transforms and smart objects** have their own best practices in
146
150
  `references/layer-effects.md`, `references/free-transform.md` and `references/smart-objects.md`.
@@ -174,7 +178,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
174
178
  | `measure_region` | Measure a rectangle of the composited image (`x`, `y`, `width`, `height` in natural pixels; default the crop, else the whole image) without a render: the `get_histogram` summary plus `meanColor` (hex) and `cast` (`redBlue`, `greenMagenta` in levels; within about ±4 is neutral). Check a neutral reference, skin, a background or clipping after each correction. |
175
179
  | `render_preview` | See the edited image (crop, adjustments, layers, annotations) as a PNG, or with `layerId` one layer alone. `maxSide` 16..2048 px, default 512. Images cost tokens: render after a group of edits, use 512 for overall look and 1024+ to judge clipping, edges and masks. Fails in environments without rendering. |
176
180
  | `inspect_psd` | Read a PSD / PSB (`data` base64) without opening it: size, `depth`, `colorMode`, `resolution`, every layer bottom first with `psdKind`, the `type` it would become (`smart`, `shape` and `type` for Photoshop smart objects, shapes and text) and `status` (`mapped`, `approximated`, `dropped`) with `notes`, and the import `warnings`. Call before `import_psd`. |
177
- | `export_psd` | Save the document as an RGB PSD at its bit depth (PSB above 30000 px) with layers, groups, adjustment layers and masks. Returns `data` (base64), `size` in bytes, `psb` and `warnings` (crop or rotation not applied, filters and depth masks baked, annotations rasterized or left out, Pigment blends written as Normal). Shape and type layers are written as real Photoshop shape and type layers. Changes nothing. |
181
+ | `export_psd` | Save the document as a PSD in its colour mode (RGB, or CMYK / Lab channels after `set_color_mode`) with its profile, at its bit depth (RGB 8, 16 or 32; CMYK and Lab 8 or 16, so a 32-bit CMYK / Lab document is written at 16 with a warning; PSB above 30000 px), with layers, groups, adjustment layers and masks. Returns `data` (base64), `size` in bytes, `psb` and `warnings` (crop or rotation not applied, filters and depth masks baked, annotations rasterized or left out, Pigment blends written as Normal; painterly blends are written as Normal too, without a warning). Shape and type layers are written as real Photoshop shape and type layers. Changes nothing. |
178
182
  | `get_transform_frame` | Before a transform: what it would move (`content`, `moved`: pixels, mask, selection), its `frame` (document bounds of the content), `corners` (top-left, top-right, bottom-right, bottom-left) and `center`. `meshColumns` / `meshRows` also return an identity warp `mesh` over the frame to edit for `warp_layer`. Changes nothing. |
179
183
  | `get_smart_object` | Read a smart object: `contents` (`id` shared by linked instances, `name`, `kind` raster or document, `width` / `height`, `decoded`, `fileType`, `linkedPath`, `instances`), `placement` (`corners`, `warp` in contents px, `interpolation`, and when `affine` the `scaleX` / `scaleY` in percent of the contents, `rotate`, `skewX`, `center`, `bounds`) and `smartFilters` (`enabled`, `filters` bottom to top with ids, settings, `blend`, `opacity`; kind `psd` is a Photoshop filter kept but not drawn; the filter `mask`). Call before changing a smart object. |
180
184
  | `list_fonts` | Read the font families type layers can use here (generic families always work) and which shaper draws them. Call before choosing a font. |
@@ -381,13 +385,7 @@ The host may add the shared document tools (see `pixerate-agent-tools`). An imag
381
385
  ## Pitfalls
382
386
 
383
387
  - Agent playback (if on) opens each write's panel, then traces `points`, drags boxes or visits the layers and sliders named; results are unchanged.
384
- - Effects need a shape: on an empty layer they draw nothing. They cannot go on adjustment layers. A clipped layer's effects stay inside the layer it is clipped to.
385
- - `bevel-emboss` has no `blend` / `opacity`: it uses `highlightBlend`, `highlightOpacity`,
386
- `shadowBlend` and `shadowOpacity`. Inner shadows and inner glows use `choke`, not `spread`.
387
- - Glows paint a colour or a gradient, never a pattern; strokes take all three. Pattern effects
388
- need a pattern id from `list_layer_effects.patterns`; an unknown id paints nothing.
389
- - Effect angles are where the light comes *from*: 120° (top left) casts a shadow down and to
390
- the right. With `useGlobalLight` true an effect's own `angle` is ignored.
388
+ - Domain-specific pitfalls live in their references: layer styles in [references/layer-effects.md](references/layer-effects.md), provider effects in [references/provider-effects.md](references/provider-effects.md), painting and the Mixer Brush in [references/painting.md](references/painting.md), retouching in [references/retouching.md](references/retouching.md), shapes and text in [references/shapes-and-type.md](references/shapes-and-type.md), smart objects in [references/smart-objects.md](references/smart-objects.md), Free Transform in [references/free-transform.md](references/free-transform.md), colour management in [references/color-management.md](references/color-management.md), and PSD in [references/photoshop-files.md](references/photoshop-files.md).
391
389
  - A layer's `kind` cannot change; to switch kinds add a new layer and remove the old one.
392
390
  Curves points, `colorize` and colours are only set when the layer is added, so get them
393
391
  right in `add_adjustment_layer` (or replace the layer).
@@ -395,41 +393,7 @@ The host may add the shared document tools (see `pixerate-agent-tools`). An imag
395
393
  `locks` only when the user wants that layer changed.
396
394
  - `merge_down`, `merge_visible` and `flatten_image` merge annotation layers only in a browser
397
395
  (they need a canvas); elsewhere they fail and say so.
398
- - Retouching pitfalls (empty layers, sources, what ignores opacity): see references/retouching.md.
399
- - `paint_stroke`, `fill_pixels` and `clear_pixels` need a visible pixel layer (or a layer with a
400
- raster mask and `channel: "mask"`); adjustment, group, shape, type, annotation and smart object layers can
401
- only be painted through their masks (and a smart object through `channel: "filter-mask"`).
402
- To paint a smart object's pixels, `edit_smart_object_contents` or rasterize it. A layer with
403
- `locks.pixels` refuses paint.
404
- - Text ranges count UTF-16 code units (an emoji is two). Point text grows from its origin: centred
405
- text is centred on `x`. Type cannot be distorted, put in perspective or warped; it can be scaled,
406
- rotated and skewed. Where no font shaper exists (a server without one), type layers keep their
407
- stored pixels and edits are refused with a reason.
408
- - Shape strokes are not layer-effect strokes: `stroke` on `add_shape_layer` / `update_shape`
409
- follows the path (and its dashes and caps); `add_layer_effect` `stroke` outlines the rendered
410
- pixels. Arrowheads belong to `line` live shapes (their `width` and `length` are pixels), not to
411
- strokes. A shape drawn over an annotation migrated from an old document (a shape layer with an
412
- `annotation` in `list_layers`) stops being that annotation once you edit it as a shape.
413
- - Shapes render up to 512 px beyond the canvas; moving one in from far outside draws it again
414
- from its paths, so nothing is lost.
415
- - While contents are open (`edit_smart_object_contents`), `list_layers` and every tool see the
416
- contents, not the document, and `export_psd` / saving write the document without the open
417
- edits. Close with `close_smart_object_contents` before anything else.
418
- - Smart filters run over the canvas, like layer filters: a blur clamps at the canvas edge, and a
419
- smart object is drawn only up to 512 px beyond the canvas. `get_transform_frame` on a smart
420
- object returns the contents' rectangle (contents px) as the `frame`, and a warp `mesh` in
421
- contents px.
422
- - `mix_colors` predicts a mix; it does not paint. To paint the mix, pass `blend: "pigment"` to
423
- `paint_stroke` (in `brush`) or `fill_pixels`, or set it on a layer.
424
- - `pigment` has no PSD equivalent: `export_psd` writes those layers as Normal (the merged image
425
- keeps the look) and says so in `warnings`, naming the layers. `import_psd` never produces it.
426
- Over transparent pixels there is nothing to mix with, so it lays the colour down as Normal.
427
- - The Mixer Brush ignores `brush.blend` (including `pigment`; use its own `mixer.mixing` instead) and the opacity ceiling: each dab lays paint at
428
- `flow × opacity` over the pixels as they are now, so overlapping dabs build up. At `flow` 1
429
- each dab covers what is under it, the pickup then sees mostly the brush's own paint, and the
430
- stroke drifts towards the loaded colour; lower the flow for more blending.
431
- - An empty, dry mixer (no reservoir, `wet` 0) paints nothing; the call succeeds with
432
- `bounds: null`. A mixer stroke over transparent pixels has nothing to pick up.
396
+ - `pigment` and the painterly blend modes (`watercolor`, `tint`, `highlight`, `shadow`) have no Photoshop mode in the image editor's PSD exporter: `export_psd` writes those layers as Normal (the merged image keeps the composited look; only `pigment` adds a warning, naming the layers). `import_psd` never produces them.
433
397
  - `export_psd` is in the source frame: a crop or rotation is not in the file (the warnings say
434
398
  so). Crop with `set_crop` only for PNG / JPEG output, or flatten the look you need first.
435
399
  - PSD opacity, fill and mask density come in as whole percents (Photoshop stores bytes), so a
@@ -444,19 +408,10 @@ The host may add the shared document tools (see `pixerate-agent-tools`). An imag
444
408
  Do not keep raising it; correct the green-magenta cast with a `colorBalance` layer instead.
445
409
  - Base `opacity` fades the whole image, not an adjustment. Base `blur` blurs a whole layer (the
446
410
  Background by default, before the adjustment layers; in older documents with blur and
447
- adjustment layers, the output after them). It is not a local softening tool.
411
+ adjustment layers, the output after them). For local softening use `blur_stroke`.
448
412
  - New adjustment layers go above the other adjustment layers and below annotations, so they
449
413
  never tint arrows, labels or watermarks. `list_layers` shows where each one went.
450
414
  - `invert` and `threshold` are destructive-looking creative effects; never use them for a
451
415
  correction the user did not ask for. Posterize `levels` below ~8 bands visibly.
452
- - Transforms refuse hidden layers, layers with `locks.position` or `locks.pixels`, and adjustment,
453
- annotation and group layers (transform a layer's mask with `channel: "mask"` after unlinking
454
- it). A result larger than about 11 600 × 11 600 px is refused.
455
- - `transform_layer` scale fields are percent (100 = unchanged), not factors; `rotate` is degrees
456
- clockwise. `corners` that cross or fold through the horizon are refused: keep the quad convex.
457
- - With a selection, `content: "auto"` (the default) moves only the selected pixels, not the
458
- whole layer. Pass `content: "layer"` to move everything regardless of the selection.
459
- - A Background transformed smaller leaves transparent canvas around it; fill it with a layer
460
- below or crop afterwards.
461
416
  - Opacity values are 0..1, not percent. Curves and levels points are 0..255 levels; levels
462
417
  `gamma` is 0.1..9.99 (1 = neutral); hue is degrees -180..180.
@@ -38,6 +38,7 @@ means sRGB. The viewport assumes an sRGB display and converts other working spac
38
38
  `unconvertedLayerIds`: convert inside them (`edit_smart_object_contents`) if the look matters.
39
39
  - Images opened from a URL or file in the browser arrive already converted to sRGB by the
40
40
  browser, so their own profile is not kept; PSDs keep theirs (resource 1039).
41
- - `export_psd` always embeds the profile; PNG and JPEG exports embed it when it is not sRGB.
41
+ - `export_psd` embeds the profile (the working space for RGB, the press profile for CMYK; Lab
42
+ files carry none); PNG and JPEG exports embed it when it is not sRGB.
42
43
  - The first proof or conversion with `coated-cmyk` builds that profile, which takes a second or
43
44
  two.
@@ -31,3 +31,15 @@ How to move, scale, rotate, skew, distort and warp layers, selected pixels and m
31
31
  - **Scale proportionally** unless asked to stretch: give `scaleX` and `scaleY` the same value.
32
32
  Keep the subject in frame: after a scale about `center` the layer can run past the canvas,
33
33
  which `bounds` in the result shows.
34
+
35
+ ## Pitfalls
36
+
37
+ - Transforms refuse hidden layers, layers with `locks.position` or `locks.pixels`, and adjustment,
38
+ annotation and group layers (transform a layer's mask with `channel: "mask"` after unlinking
39
+ it). A result larger than about 11 600 × 11 600 px is refused.
40
+ - `transform_layer` scale fields are percent (100 = unchanged), not factors; `rotate` is degrees
41
+ clockwise. `corners` that cross or fold through the horizon are refused: keep the quad convex.
42
+ - With a selection, `content: "auto"` (the default) moves only the selected pixels, not the
43
+ whole layer. Pass `content: "layer"` to move everything regardless of the selection.
44
+ - A Background transformed smaller leaves transparent canvas around it; fill it with a layer
45
+ below or crop afterwards.
@@ -34,3 +34,13 @@ Read this before `add_layer_effect`, `update_layer_effect`, `set_global_light` o
34
34
  layer `opacity` fades the effects too.
35
35
  - **Prefer an effect over painted pixels** for shadows, glows, outlines and colour fills of a
36
36
  shape: it follows later edits and stays adjustable.
37
+
38
+ ## Pitfalls
39
+
40
+ - Effects need a shape: on an empty layer they draw nothing. They cannot go on adjustment layers. A clipped layer's effects stay inside the layer it is clipped to.
41
+ - `bevel-emboss` has no `blend` / `opacity`: it uses `highlightBlend`, `highlightOpacity`,
42
+ `shadowBlend` and `shadowOpacity`. Inner shadows and inner glows use `choke`, not `spread`.
43
+ - Glows paint a colour or a gradient, never a pattern; strokes take all three. Pattern effects
44
+ need a pattern id from `list_layer_effects.patterns`; an unknown id paints nothing.
45
+ - Effect angles are where the light comes *from*: 120° (top left) casts a shadow down and to
46
+ the right. With `useGlobalLight` true an effect's own `angle` is ignored.
@@ -43,3 +43,20 @@ Read this before `mix_colors`, a `paint_stroke` with `tool: "mixer"`, or gradien
43
43
  "gradient", stops, map }`, or `paint_stroke` `paint` for one stroke (brush only). `map.by`
44
44
  `distance` (default 300 px, mirrored) for drag-it-out colour, `pressure`, `direction`,
45
45
  `across-tip`, `random`; stops may be `"paint"` / `"secondary"` (foreground / background).
46
+
47
+ ## Pitfalls
48
+
49
+ - `paint_stroke`, `fill_pixels` and `clear_pixels` need a visible pixel layer (or a layer with a
50
+ raster mask and `channel: "mask"`); adjustment, group, shape, type, annotation and smart object layers can
51
+ only be painted through their masks (and a smart object through `channel: "filter-mask"`).
52
+ To paint a smart object's pixels, `edit_smart_object_contents` or rasterize it. A layer with
53
+ `locks.pixels` refuses paint.
54
+ - `mix_colors` predicts a mix; it does not paint. To paint the mix, pass `blend: "pigment"` to
55
+ `paint_stroke` (in `brush`) or `fill_pixels`, or set it on a layer.
56
+ - `pigment` and the painterly blend modes (`watercolor`, `tint`, `highlight`, `shadow`) have no Photoshop mode in the image editor's PSD exporter: `export_psd` writes those layers as Normal (the merged image keeps the look; only `pigment` adds a warning, naming the layers). `import_psd` never produces them. Over transparent pixels there is nothing for `pigment` to mix with, so it lays the colour down as Normal.
57
+ - The Mixer Brush ignores `brush.blend` (including `pigment`; use its own `mixer.mixing` instead) and the opacity ceiling: each dab lays paint at
58
+ `flow × opacity` over the pixels as they are now, so overlapping dabs build up. At `flow` 1
59
+ each dab covers what is under it, the pickup then sees mostly the brush's own paint, and the
60
+ stroke drifts towards the loaded colour; lower the flow for more blending.
61
+ - An empty, dry mixer (no reservoir, `wet` 0) paints nothing; the call succeeds with
62
+ `bounds: null`. A mixer stroke over transparent pixels has nothing to pick up.
@@ -40,7 +40,7 @@
40
40
  (kind `psd`): hide, reorder, re-blend or remove them, and `export_psd` writes them back.
41
41
  5. **`export_psd` writes the stack as it is**, in the source frame: crop and rotation are not
42
42
  applied, layer filters and depth masks are baked, annotation layers are rasterized (only in
43
- a browser). Effects are written as Photoshop layer styles: an effects block opened from a PSD
43
+ a browser), and `pigment` or painterly blend modes (`watercolor`, `tint`, `highlight`, `shadow`) are written as Normal (the merged image keeps their composited look; only `pigment` adds a warning, naming the layers). Effects are written as Photoshop layer styles: an effects block opened from a PSD
44
44
  goes back byte for byte while its effects are unchanged, and the patterns effects use (the
45
45
  built-in ones too) are embedded. Only the first outer glow, inner glow, bevel, satin and
46
46
  pattern overlay of a layer fit in a PSD. Smart objects are written as smart objects (an
@@ -72,3 +72,17 @@ upright-Latin runs) and goes back out as vertical type. `get_text_layer` reports
72
72
  and `missingFonts` for those: if a font is missing, edits draw with a fallback, so tell the user
73
73
  before editing such text, or pick an available font with `set_text_style`. `export_psd` writes both
74
74
  kinds back as Photoshop layers; unchanged ones are written byte for byte as they were read.
75
+
76
+ ## Pitfalls
77
+
78
+ - Text ranges count UTF-16 code units (an emoji is two). Point text grows from its origin: centred
79
+ text is centred on `x`. Type cannot be distorted, put in perspective or warped; it can be scaled,
80
+ rotated and skewed. Where no font shaper exists (a server without one), type layers keep their
81
+ stored pixels and edits are refused with a reason.
82
+ - Shape strokes are not layer-effect strokes: `stroke` on `add_shape_layer` / `update_shape`
83
+ follows the path (and its dashes and caps); `add_layer_effect` `stroke` outlines the rendered
84
+ pixels. Arrowheads belong to `line` live shapes (their `width` and `length` are pixels), not to
85
+ strokes. A shape drawn over an annotation migrated from an old document (a shape layer with an
86
+ `annotation` in `list_layers`) stops being that annotation once you edit it as a shape.
87
+ - Shapes render up to 512 px beyond the canvas; moving one in from far outside draws it again
88
+ from its paths, so nothing is lost.