@pixerate/editor 0.266.2 → 0.267.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 (111) 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 +172 -105
  8. package/dist/animation-editor/index.js +91 -24
  9. package/dist/brushes/index.cjs +59 -59
  10. package/dist/brushes/index.js +5 -5
  11. package/dist/brushes/profiles.d.cts +6 -1
  12. package/dist/brushes/profiles.d.ts +6 -1
  13. package/dist/{brushstrokeRender-FSUWXUZY.cjs → brushstrokeRender-L7JYRR2R.cjs} +16 -16
  14. package/dist/{brushstrokeRender-KD453OBX.js → brushstrokeRender-NLFPEBGI.js} +4 -4
  15. package/dist/canvas/brushTypes.d.cts +12 -0
  16. package/dist/canvas/brushTypes.d.ts +12 -0
  17. package/dist/canvas/brushstroke.d.cts +1 -1
  18. package/dist/canvas/brushstroke.d.ts +1 -1
  19. package/dist/canvas/drawController.d.cts +19 -4
  20. package/dist/canvas/drawController.d.ts +19 -4
  21. package/dist/canvas/index.cjs +144 -105
  22. package/dist/canvas/index.js +46 -7
  23. package/dist/{chunk-XORJ7UZO.cjs → chunk-3VHH6V67.cjs} +13 -12
  24. package/dist/{chunk-T2LDTEXK.js → chunk-4IDUYB45.js} +2217 -56
  25. package/dist/chunk-7UKFXXWL.js +802 -0
  26. package/dist/chunk-ADMRNWR7.js +340 -0
  27. package/dist/{chunk-CIJBSUM5.js → chunk-AGNUD5BI.js} +2 -2
  28. package/dist/{chunk-WLUVZKVD.cjs → chunk-DT4YQUGT.cjs} +32 -32
  29. package/dist/{chunk-DXYHQ2VN.js → chunk-EF7LZAXQ.js} +1 -1
  30. package/dist/chunk-EXB2EGBE.cjs +802 -0
  31. package/dist/{chunk-4ODRXQRW.js → chunk-EYSHTYLG.js} +1 -1
  32. package/dist/{chunk-R44Y3EFS.js → chunk-FGJHJIMN.js} +8 -336
  33. package/dist/{chunk-AQZQWXEU.cjs → chunk-G7NHE2VD.cjs} +35 -35
  34. package/dist/{chunk-XC6DPRYI.js → chunk-GSTSZPSR.js} +154 -949
  35. package/dist/{chunk-VRK322ZC.cjs → chunk-HIO6E7LC.cjs} +2 -2
  36. package/dist/{chunk-TOID45I2.cjs → chunk-J2TNIZ55.cjs} +1 -1
  37. package/dist/{chunk-YHDHFJUC.cjs → chunk-J5VQYK7T.cjs} +217 -1012
  38. package/dist/{chunk-3C33YTNN.js → chunk-JGWVZPT6.js} +2 -2
  39. package/dist/{chunk-ACFSFUQH.js → chunk-JL6YH5WD.js} +4 -0
  40. package/dist/{chunk-3SBLVGOM.cjs → chunk-LA5Y4ETA.cjs} +23 -23
  41. package/dist/{chunk-MVUQSK5J.cjs → chunk-LHPEG5AU.cjs} +107 -435
  42. package/dist/{chunk-DOZ6F2K2.js → chunk-NSETJI74.js} +3 -2
  43. package/dist/{chunk-XRXFVRGY.cjs → chunk-OKHKA43L.cjs} +2364 -203
  44. package/dist/{chunk-LK4SXL3V.js → chunk-OSEXT7JL.js} +1 -1
  45. package/dist/{chunk-PE6YJMQT.cjs → chunk-PFHJS4HE.cjs} +20 -19
  46. package/dist/{chunk-7KNW3WXZ.cjs → chunk-QEVLRLUN.cjs} +63 -0
  47. package/dist/{chunk-OTN6VB5S.cjs → chunk-RDFHWAHL.cjs} +5 -1
  48. package/dist/{chunk-6JL2D5SG.js → chunk-VE6UKYQ2.js} +3 -2
  49. package/dist/{chunk-RFJZGTHA.cjs → chunk-XVOP3CGQ.cjs} +9 -9
  50. package/dist/{chunk-EZOJBP4D.js → chunk-YIC6MAJ2.js} +1 -1
  51. package/dist/chunk-YZWM6ZIP.cjs +340 -0
  52. package/dist/{chunk-472INMWH.js → chunk-ZA7L5YJB.js} +63 -0
  53. package/dist/commands/builtins.d.cts +12 -1
  54. package/dist/commands/builtins.d.ts +12 -1
  55. package/dist/commands/index.cjs +2 -2
  56. package/dist/commands/index.js +1 -1
  57. package/dist/image-editor/controller.d.cts +1 -1
  58. package/dist/image-editor/controller.d.ts +1 -1
  59. package/dist/image-editor/index.cjs +837 -833
  60. package/dist/image-editor/index.js +105 -101
  61. package/dist/paint/index.cjs +5 -3
  62. package/dist/paint/index.js +46 -44
  63. package/dist/paint-editor/controller.d.cts +5 -3
  64. package/dist/paint-editor/controller.d.ts +5 -3
  65. package/dist/paint-editor/index.cjs +832 -2295
  66. package/dist/paint-editor/index.js +285 -1748
  67. package/dist/paint-editor/path-samples.d.cts +2 -0
  68. package/dist/paint-editor/path-samples.d.ts +2 -0
  69. package/dist/paint-editor/presence.d.cts +20 -1
  70. package/dist/paint-editor/presence.d.ts +20 -1
  71. package/dist/stickers/helpers.d.cts +2 -0
  72. package/dist/stickers/helpers.d.ts +2 -0
  73. package/dist/stickers/index.cjs +41 -35
  74. package/dist/stickers/index.js +22 -16
  75. package/dist/stickers/types.d.cts +4 -2
  76. package/dist/stickers/types.d.ts +4 -2
  77. package/dist/vector/backdrop.d.cts +13 -0
  78. package/dist/vector/backdrop.d.ts +13 -0
  79. package/dist/vector/brushstroke.d.cts +54 -7
  80. package/dist/vector/brushstroke.d.ts +54 -7
  81. package/dist/vector/brushstrokeMedia.d.cts +96 -0
  82. package/dist/vector/brushstrokeMedia.d.ts +96 -0
  83. package/dist/vector/controller.d.cts +17 -3
  84. package/dist/vector/controller.d.ts +17 -3
  85. package/dist/vector/index.cjs +1846 -1386
  86. package/dist/vector/index.d.cts +2 -0
  87. package/dist/vector/index.d.ts +2 -0
  88. package/dist/vector/index.js +517 -57
  89. package/dist/vector/render.d.cts +6 -0
  90. package/dist/vector/render.d.ts +6 -0
  91. package/dist/vector/types.d.cts +14 -0
  92. package/dist/vector/types.d.ts +14 -0
  93. package/package.json +1 -1
  94. package/skills/pixerate-animation-editor/SKILL.md +1 -0
  95. package/skills/pixerate-brushes/SKILL.md +2 -2
  96. package/skills/pixerate-brushes-design/SKILL.md +7 -4
  97. package/skills/pixerate-brushes-design/evals/port-to-vector.json +8 -8
  98. package/skills/pixerate-canvas/SKILL.md +11 -1
  99. package/skills/pixerate-image-editor/SKILL.md +19 -64
  100. package/skills/pixerate-image-editor/references/color-management.md +2 -1
  101. package/skills/pixerate-image-editor/references/free-transform.md +12 -0
  102. package/skills/pixerate-image-editor/references/layer-effects.md +10 -0
  103. package/skills/pixerate-image-editor/references/painting.md +17 -0
  104. package/skills/pixerate-image-editor/references/photoshop-files.md +1 -1
  105. package/skills/pixerate-image-editor/references/shapes-and-type.md +14 -0
  106. package/skills/pixerate-image-editor/references/smart-objects.md +10 -0
  107. package/skills/pixerate-paint-editor/SKILL.md +8 -4
  108. package/skills/pixerate-paint-editor/references/wet-media.md +9 -0
  109. package/skills/pixerate-vector/SKILL.md +16 -6
  110. package/skills/pixerate-vector/references/best-practices.md +1 -0
  111. 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.267.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). |
@@ -21,7 +21,7 @@ built-in presets (read-only) and the user's brushes, shared by every editor that
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**
@@ -43,7 +43,7 @@ paints without (warnings), each with a fix; lists and writes use the current hos
43
43
  | Paint editor (`paint`) | Everything. |
44
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
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`. |
46
+ | 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
47
 
48
48
  Every host lists the presets its profile admits and the library's saved brushes it admits, and
49
49
  each registers these tools with its own profile, so `list_brush_presets` shows that host's brushes.
@@ -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,7 +54,8 @@ 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
60
  | A texture (chalk, charcoal, dry brush) | Raise `behaviour.grain` (0.8..1) and add `dynamics.sizeJitter`; test on rough paper. |
59
61
  | A light brush (neon, glow) | `light.amount`, and paint it on dark paper; a highlighter keeps `light.spill.radius` 0. |
@@ -80,7 +82,8 @@ basedOn: "highlighter", colour: { default: "#ff6ec7" } }`, then `check_brush`.
80
82
  - **Starting from nothing.** A brush without `basedOn` gets engine defaults (a hard round dab);
81
83
  it rarely looks like the medium asked for.
82
84
  - **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.
85
+ brush, nor a thick (impasto) preset of a vector one; set `behaviour.surface` to `dry` (vector
86
+ also takes `wet`) or start from a dry preset.
84
87
  - **Lists replace.** Giving `colour.palette` or `tip.stamps` replaces the whole list; include
85
88
  every entry you want.
86
89
  - **Changing a built-in.** `update_brush` refuses built-ins; duplicate first.
@@ -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.
@@ -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.
@@ -321,7 +321,7 @@ Listing, saving, sharing and previewing brushes are the brush library's tools (`
321
321
  | Tool | Use |
322
322
  | --- | --- |
323
323
  | `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. |
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) 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
325
  | `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
326
  | `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
327
  | `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 +339,7 @@ Listing, saving, sharing and previewing brushes are the brush library's tools (`
339
339
 
340
340
  | Tool | Use |
341
341
  | --- | --- |
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. |
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, 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
343
  | `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
344
  | `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
345
  | `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 +462,8 @@ The host may add the shared document tools (see `pixerate-agent-tools`). A paint
462
462
  keep taking its colours.
463
463
  - **Strokes take real time when the host shows your cursor.** With a director (the demo and
464
464
  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` /
465
+ the pace of the points' `t` (else about 1.5 px per ms, at most ~1.8 s), `stroke_path` paints
466
+ each subpath in turn (paced by its `speed` when given) as one undo step, and `fill_layer` /
466
467
  `replay_strokes` move the cursor first. Results and undo are the same; it is one more reason to
467
468
  paint a few long strokes and replay them rather than many short ones. A stroke fails with "A
468
469
  stroke is in progress" while the user is painting; try it again afterwards.
@@ -470,7 +471,10 @@ The host may add the shared document tools (see `pixerate-agent-tools`). A paint
470
471
  flowed; `advance_wet_paint` first. Paint that should stay exactly where it was laid needs
471
472
  `dry_paint` (or a dry medium).
472
473
  - **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.
474
+ 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
475
+ that limit is lost (`dry_paint` frees tiles). Multi-threaded wet media needs a cross-origin isolated
476
+ page (COOP / COEP, for `SharedArrayBuffer`) and runs on one thread otherwise, more slowly but with
477
+ the same result.
474
478
  - **Undoing far back** past the wet undo memory (512 MB by default) leaves that paint dry as it
475
479
  looked, rather than wet.
476
480
  - **Replays repeat what was recorded**: strokes, fills, clears, flattened relief and wet events.
@@ -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`