@pixerate/editor 0.221.1 → 0.222.1

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 (36) hide show
  1. package/dist/image-editor/edit-tools.d.cts +1 -1
  2. package/dist/image-editor/edit-tools.d.ts +1 -1
  3. package/dist/image-editor/index.cjs +35 -2
  4. package/dist/image-editor/index.js +35 -2
  5. package/package.json +1 -1
  6. package/skills/README.md +10 -3
  7. package/skills/pixerate-image-editor/SKILL.md +25 -76
  8. package/skills/pixerate-image-editor/references/free-transform.md +11 -0
  9. package/skills/pixerate-image-editor/references/layer-effects.md +12 -0
  10. package/skills/pixerate-image-editor/references/smart-objects.md +9 -0
  11. package/skills/pixerate-image-photo-correction/SKILL.md +129 -0
  12. package/skills/pixerate-image-photo-correction/evals/crop-for-instagram.json +19 -0
  13. package/skills/pixerate-image-photo-correction/evals/lift-shadows-keep-sky.json +20 -0
  14. package/skills/pixerate-image-photo-correction/evals/neutralise-cast.json +25 -0
  15. package/skills/pixerate-image-portrait-retouch/SKILL.md +115 -0
  16. package/skills/pixerate-image-portrait-retouch/evals/brighten-eyes.json +25 -0
  17. package/skills/pixerate-image-portrait-retouch/evals/remove-blemishes.json +23 -0
  18. package/skills/pixerate-image-portrait-retouch/evals/remove-stray-hair.json +18 -0
  19. package/skills/pixerate-image-product-photo/SKILL.md +126 -0
  20. package/skills/pixerate-image-product-photo/evals/grounding-shadow.json +19 -0
  21. package/skills/pixerate-image-product-photo/evals/square-crop.json +19 -0
  22. package/skills/pixerate-image-product-photo/evals/white-background.json +23 -0
  23. package/skills/pixerate-vector/SKILL.md +11 -61
  24. package/skills/pixerate-vector/references/best-practices.md +62 -0
  25. package/skills/pixerate-vector-icon-set/SKILL.md +111 -0
  26. package/skills/pixerate-vector-icon-set/evals/consistent-strokes.json +22 -0
  27. package/skills/pixerate-vector-icon-set/evals/plus-icon.json +15 -0
  28. package/skills/pixerate-vector-icon-set/evals/repeats-as-symbol.json +192 -0
  29. package/skills/pixerate-vector-illustration/SKILL.md +112 -0
  30. package/skills/pixerate-vector-illustration/evals/clip-stripes.json +21 -0
  31. package/skills/pixerate-vector-illustration/evals/radial-sun.json +16 -0
  32. package/skills/pixerate-vector-illustration/evals/subtle-shadow.json +16 -0
  33. package/skills/pixerate-vector-logo/SKILL.md +107 -0
  34. package/skills/pixerate-vector-logo/evals/centre-lockup.json +20 -0
  35. package/skills/pixerate-vector-logo/evals/knockout-ring.json +21 -0
  36. package/skills/pixerate-vector-logo/evals/live-union.json +19 -0
@@ -0,0 +1,115 @@
1
+ ---
2
+ name: pixerate-image-portrait-retouch
3
+ description: The portrait retouching workflow in the Pixerate image editor - removing blemishes, spots and stray hairs, healing larger flaws, evening skin while keeping its texture, dodging and burning, brightening eyes and teeth, and keeping the result natural and reversible on separate layers. Use when asked to retouch, clean up, airbrush or touch up a portrait, headshot or face, remove spots, pimples, acne, wrinkles, stray hairs or dust, smooth or even skin, brighten eyes or whiten teeth, or when spot_heal, heal_brush, clone_stamp, dodge or burn are available. Load with pixerate-image-editor, which lists every tool.
4
+ metadata:
5
+ kind: workflow
6
+ reference: pixerate-image-editor
7
+ ---
8
+
9
+ # Retouching a portrait
10
+
11
+ Good retouching removes what is temporary (a spot, a stray hair, a shine) and keeps what makes
12
+ the person look like themselves (moles they keep, freckles, pores, laughter lines). Work from
13
+ the global to the small, on layers above the photo, so every step can be faded or taken back.
14
+ The healing tools paint onto an empty layer when they sample `current-below`; tonal tools
15
+ (dodge, burn, sponge, smudge) change the pixels they touch, so they work on a duplicate.
16
+
17
+ This skill is the method. `pixerate-image-editor` has the tool reference, and its
18
+ `references/retouching.md` the detail of each retouching tool.
19
+
20
+ ## Goal and quality bar
21
+
22
+ - Temporary flaws are gone: a blemish area measures like the skin around it (`measure_region`
23
+ on a small box around it: luminance `p1` close to the surrounding skin).
24
+ - Skin keeps its texture: no flat, plastic patches. Heal and clone copy real texture; blur only
25
+ locally and lightly.
26
+ - Tone and colour are natural: skin has red above green above blue, eyes and teeth brightened
27
+ only a little (they stay off-white).
28
+ - The photo layer is unchanged: retouching lives on named layers ("Retouch", "Dodge & burn",
29
+ "Eyes") the user can fade or switch off.
30
+
31
+ ## Inputs to confirm
32
+
33
+ - **How much:** a light clean-up (temporary flaws only, the default), a polished commercial
34
+ retouch, or something specific.
35
+ - **What to keep:** moles, freckles, scars and features that are part of the person stay unless
36
+ the user asks.
37
+ - **Where:** the flaws to fix when the user has named them (positions or a description).
38
+
39
+ ## Working method
40
+
41
+ 1. **Global correction first.** Exposure and white balance as in `pixerate-image-photo-correction`,
42
+ so retouching is judged on the final tones.
43
+ **Check:** skin `measure_region` has a plausible colour (red > green > blue, no strong cast).
44
+ 2. **Find the flaws.** `capture_view` the face at 2-4× scale in parts; list spots, hairs and
45
+ flaws with positions. `measure_region` on a small box around a suspect spot compared with a
46
+ box of clean skin beside it tells a spot from a shadow.
47
+ **Check:** a list of flaws with coordinates and sizes.
48
+ 3. **Make a retouch layer.** `add_pixel_layer` named "Retouch" above the photo; all healing goes
49
+ on it with `sample: "current-below"`.
50
+ **Check:** `list_layers` shows it active above the photo.
51
+ 4. **Heal small to large.** `spot_heal` for spots (one dab, `brush.size` a little larger than the
52
+ spot, `hardness` 0.2-0.5); a short stroke of dabs along a stray hair. `heal_brush` from a clean
53
+ source of the same texture for larger flaws; `clone_stamp` near edges (lips, eyes, hairline)
54
+ where healing would pull colour across.
55
+ **Check:** `measure_region` around each flaw: its `p1` is near the clean skin's; `capture_view`
56
+ shows texture, not a smear.
57
+ 5. **Even tone (dodge and burn)**, only if asked for more than a clean-up. Duplicate the photo
58
+ (`duplicate_layer`) and `dodge` / `burn` at `exposure` 0.1-0.2, `range: "midtones"`, along
59
+ shadows under the eyes or uneven patches; or paint on a layer in `soft-light` blend.
60
+ **Check:** the area's luminance moved by a few levels, not tens.
61
+ 6. **Eyes and teeth.** Select them (`select_ellipse`, `select_polygon`), feather 1-2 px, and add a
62
+ `levels` or `curves` layer while the selection is active (`master.gamma` 1.2-1.6). For yellow
63
+ teeth, a `hueSaturation` layer lowering the yellows' saturation, masked the same way.
64
+ **Check:** `measure_region` inside rises; just outside (skin, lips) does not change.
65
+ 7. **Review the whole face** at normal scale (`render_preview` or `capture_view` at 1×). Lower a
66
+ layer's `opacity` where it went too far (`set_layer_properties`). Report what you removed.
67
+
68
+ ## Decision table
69
+
70
+ | Flaw | Tool |
71
+ | --- | --- |
72
+ | Spot, pimple, dust (smaller than about 15 px) | `spot_heal`, `proximity-match` or `content-aware` |
73
+ | Stray hair across skin | `spot_heal` along it, short strokes; `clone_stamp` where it crosses an edge |
74
+ | Larger blemish, scar the user wants gone | `heal_brush` from clean skin of the same texture and light |
75
+ | Flaw next to an edge (lip line, eyelid, hairline) | `clone_stamp`, with a source along the same edge |
76
+ | Shine on forehead or nose | `heal_brush` at reduced brush opacity from matte skin nearby |
77
+ | Dark circles, uneven patches | `dodge` (midtones, exposure 0.1-0.2) on a duplicate, or a masked curves layer |
78
+ | Dull eyes | Masked `levels` gamma 1.2-1.6; `sharpen_stroke` lightly on the iris |
79
+ | Yellow teeth | Masked `hueSaturation` (yellows, saturation -20..-40), then a little brightness |
80
+ | Redness | Masked `hueSaturation` (reds, saturation down a little) |
81
+
82
+ ## Recipes
83
+
84
+ **Clean-up pass.** `add_pixel_layer` "Retouch" → one `spot_heal` per spot (`points: [{x, y}]`,
85
+ `sample: "current-below"`, `brush: { size, hardness: 0.3 }`) → `measure_region` around each.
86
+
87
+ **Stray hair.** `spot_heal` with `points` every 8-10 px along the hair and a brush 1.5-2× its
88
+ width; check the whole length with one `measure_region` box over it.
89
+
90
+ **Brighter eyes.** `select_ellipse` each eye (`mode: "add"` for the second) → `modify_selection`
91
+ `feather` 1 → `add_adjustment_layer` `levels` with `params: { master: { gamma: 1.4 } }` →
92
+ `measure_region` on an eye and on the skin beside it.
93
+
94
+ ## Pitfalls
95
+
96
+ - **Healing on the photo layer.** Mistakes become permanent; heal onto an empty layer with
97
+ `current-below`.
98
+ - **An empty layer with `sample: "current"`** has nothing to copy, so the stroke does nothing.
99
+ - **Over-smoothing.** Blurring skin removes pores and looks fake; heal flaws instead.
100
+ - **Big brushes.** A heal brush much larger than the flaw pulls in colour from around it; keep it
101
+ just over the flaw.
102
+ - **Healing across an edge** smears lips or lashes into skin; clone along edges instead.
103
+ - **Removing features.** Moles, freckles and scars are part of a face; ask before removing them.
104
+ - **Bright white eyes and teeth** look unnatural; small gamma moves, and measure.
105
+
106
+ ## Tools used
107
+
108
+ - `list_layers`, `capture_view`, `render_preview`, `measure_region`: find flaws, check each fix, review
109
+ - `add_pixel_layer`, `duplicate_layer`, `set_layer_properties`: retouch and dodge-and-burn layers; fade them
110
+ - `spot_heal`, `heal_brush`, `clone_stamp`, `set_clone_source`: remove spots, hairs and flaws
111
+ - `dodge`, `burn`, `sponge`: even tone and colour on a duplicate
112
+ - `sharpen_stroke`, `blur_stroke`: crisp eyes, soften a harsh edge, locally
113
+ - `select_ellipse`, `select_polygon`, `modify_selection`: select eyes and teeth
114
+ - `add_adjustment_layer`: masked brightening or colour change of eyes and teeth
115
+ - `undo_image`: take back a step
@@ -0,0 +1,25 @@
1
+ {
2
+ "prompt": "Brighten the eyes a little (the two dark ovals at x 35-45 and x 55-65, y 15-21) without changing the skin.",
3
+ "fixture": {
4
+ "editor": "image",
5
+ "width": 100,
6
+ "height": 60,
7
+ "background": [200, 160, 140],
8
+ "shapes": [
9
+ { "shape": "ellipse", "x": 35, "y": 15, "width": 10, "height": 6, "color": [70, 55, 50] },
10
+ { "shape": "ellipse", "x": 55, "y": 15, "width": 10, "height": 6, "color": [70, 55, 50] }
11
+ ]
12
+ },
13
+ "checks": [
14
+ { "why": "the eyes are brighter", "tool": "measure_region", "args": { "x": 38, "y": 17, "width": 4, "height": 2 }, "expect": { "stats": { "luminance": { "mean": { "$min": 75, "$max": 140 } } } } },
15
+ { "why": "the other eye too", "tool": "measure_region", "args": { "x": 58, "y": 17, "width": 4, "height": 2 }, "expect": { "stats": { "luminance": { "mean": { "$min": 75, "$max": 140 } } } } },
16
+ { "why": "the skin is unchanged", "tool": "measure_region", "args": { "x": 10, "y": 35, "width": 80, "height": 20 }, "expect": { "meanColor": "#c8a08c" } },
17
+ { "why": "it is an adjustable, masked layer", "tool": "list_layers", "expect": { "layers": { "$some": { "type": "adjustment", "mask": { "kind": "raster" } } } } }
18
+ ],
19
+ "solution": [
20
+ { "tool": "select_ellipse", "args": { "x": 35, "y": 15, "width": 10, "height": 6 } },
21
+ { "tool": "select_ellipse", "args": { "x": 55, "y": 15, "width": 10, "height": 6, "mode": "add" } },
22
+ { "tool": "modify_selection", "args": { "op": "feather", "radius": 1 } },
23
+ { "tool": "add_adjustment_layer", "args": { "kind": "levels", "name": "Eyes", "params": { "master": { "gamma": 1.6 } } } }
24
+ ]
25
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "prompt": "Remove the two dark blemishes on this skin (around x 30, y 30 and x 70, y 22). Don't change the original photo layer.",
3
+ "fixture": {
4
+ "editor": "image",
5
+ "width": 100,
6
+ "height": 60,
7
+ "background": [200, 160, 140],
8
+ "shapes": [
9
+ { "shape": "rect", "x": 29, "y": 29, "width": 3, "height": 3, "color": [120, 80, 70] },
10
+ { "shape": "rect", "x": 69, "y": 21, "width": 3, "height": 3, "color": [120, 80, 70] }
11
+ ]
12
+ },
13
+ "checks": [
14
+ { "why": "the first blemish is gone", "tool": "measure_region", "args": { "x": 26, "y": 26, "width": 9, "height": 9 }, "expect": { "stats": { "luminance": { "p1": { "$min": 155 } } } } },
15
+ { "why": "the second blemish is gone", "tool": "measure_region", "args": { "x": 66, "y": 18, "width": 9, "height": 9 }, "expect": { "stats": { "luminance": { "p1": { "$min": 155 } } } } },
16
+ { "why": "the retouching is on its own layer above the photo", "tool": "list_layers", "expect": { "layers": { "$length": { "$min": 2 }, "$some": { "id": "background", "mask": null } } } }
17
+ ],
18
+ "solution": [
19
+ { "tool": "add_pixel_layer", "args": { "name": "Retouch" } },
20
+ { "tool": "spot_heal", "args": { "points": [{ "x": 30, "y": 30 }], "sample": "current-below", "brush": { "size": 10, "hardness": 0.3 } } },
21
+ { "tool": "spot_heal", "args": { "points": [{ "x": 70, "y": 22 }], "sample": "current-below", "brush": { "size": 10, "hardness": 0.3 } } }
22
+ ]
23
+ }
@@ -0,0 +1,18 @@
1
+ {
2
+ "prompt": "There's a stray dark hair across the cheek (a thin line at y 45, from x 20 to 60). Remove it without touching the original layer.",
3
+ "fixture": {
4
+ "editor": "image",
5
+ "width": 100,
6
+ "height": 60,
7
+ "background": [200, 160, 140],
8
+ "shapes": [{ "shape": "rect", "x": 20, "y": 45, "width": 41, "height": 1, "color": [90, 70, 60] }]
9
+ },
10
+ "checks": [
11
+ { "why": "the hair is gone along its whole length", "tool": "measure_region", "args": { "x": 18, "y": 43, "width": 45, "height": 5 }, "expect": { "stats": { "luminance": { "p1": { "$min": 155 } } } } },
12
+ { "why": "on its own layer", "tool": "list_layers", "expect": { "layers": { "$length": { "$min": 2 } } } }
13
+ ],
14
+ "solution": [
15
+ { "tool": "add_pixel_layer", "args": { "name": "Retouch" } },
16
+ { "tool": "spot_heal", "args": { "points": [{ "x": 20, "y": 45.5 }, { "x": 30, "y": 45.5 }, { "x": 40, "y": 45.5 }, { "x": 50, "y": 45.5 }, { "x": 60, "y": 45.5 }], "sample": "current-below", "brush": { "size": 8, "hardness": 0.3 } } }
17
+ ]
18
+ }
@@ -0,0 +1,126 @@
1
+ ---
2
+ name: pixerate-image-product-photo
3
+ description: The product photo workflow in the Pixerate image editor - cutting a product out of its backdrop, putting it on pure white or a brand colour, grounding it with a soft shadow, keeping its colours accurate, cropping and centring it to marketplace ratios with consistent margins, and checking the result before export. Use when asked to prepare product shots, packshots or e-commerce, marketplace, Amazon, Etsy or catalogue images, put a product on white, remove or replace a product background, add a product shadow, or crop and centre products consistently, or when select_subject, add_layer_mask, add_layer_effect or set_crop are available. Load with pixerate-image-editor, which lists every tool.
4
+ metadata:
5
+ kind: workflow
6
+ reference: pixerate-image-editor
7
+ ---
8
+
9
+ # Preparing a product photo
10
+
11
+ A product shot is a cutout on a controlled background: the product's edge and colour must be
12
+ exact, the background uniform, and every image in a set framed the same way. Build it as layers
13
+ so each part stays adjustable: the photo with a mask hiding its backdrop, a background fill
14
+ layer below it, a shadow (an effect, or its own layer) between, and the crop on top.
15
+
16
+ This skill is the method. `pixerate-image-editor` has the full tool reference, and
17
+ `pixerate-image-selection-masking` the detail of selecting and refining the edge.
18
+
19
+ ## Goal and quality bar
20
+
21
+ - The background is uniform: pure white is 255 in every channel (`measure_region` `stats`
22
+ luminance `p1` ≥ 250 and a cast within ±2), or exactly the brand colour asked for.
23
+ - The product's colour is unchanged by the cutout (measure it before and after) and its edge
24
+ has no halo of the old backdrop (`inspect_mask_edge` `haloScore` under 0.35).
25
+ - The product sits on something: a soft shadow below it, unless the user wants it floating.
26
+ - The frame matches the destination: ratio, product centred (or as the set requires), and the
27
+ product filling a consistent share of the frame (about 75-85 % of the longer side for most
28
+ marketplaces).
29
+ - Everything stays editable: masked, not erased; the shadow an effect or a layer.
30
+
31
+ ## Inputs to confirm
32
+
33
+ - **Background:** pure white (the default for marketplaces), a colour (hex), or transparent.
34
+ - **Destination ratio and margin:** 1:1 (most marketplaces), 4:5, 3:2, or a size in pixels; how
35
+ much of the frame the product should fill. For a set, the same for every image.
36
+ - **Shadow:** a soft contact or drop shadow (the default), a reflection, or none.
37
+ - **Colour reference:** a swatch or hex the product must match, when colour accuracy matters.
38
+
39
+ ## Working method
40
+
41
+ 1. **Read and measure the product.** `list_layers`, `get_image_info`, then `measure_region` on a
42
+ flat part of the product: its mean colour is the reference you will protect.
43
+ **Check:** you have the product's bounds and colour.
44
+ 2. **Correct the photo first** if it needs it (white balance from the backdrop, exposure): the
45
+ cutout and the measure should be on corrected colours (`pixerate-image-photo-correction`).
46
+ **Check:** the backdrop is neutral before you remove it.
47
+ 3. **Select the product.** On a plain backdrop, `magic_wand` on the product (`contiguous: true`)
48
+ or on the backdrop and `invert_selection`; on a busy one, `select_subject` or `select_object`
49
+ with a box, then `refine_selection`. Hard-edged products want a crisp edge (refine radius
50
+ 1-3, no feather).
51
+ **Check:** `get_selection` bounds hug the product; `inspect_mask_edge` `issues` is empty.
52
+ 4. **Hide the backdrop, do not delete it.** `add_layer_mask` `from: "selection"` on the photo
53
+ layer (or `add_vector_mask` for a geometric product that must stay razor sharp). Then
54
+ `add_pixel_layer` with the background `color` placed `belowId` the photo layer.
55
+ **Check:** `measure_region` on a corner: `p1` ≥ 250 (white) or the brand colour; on the
56
+ product: the colour from step 1.
57
+ 5. **Fix the edge colour** if the old backdrop tints it: `set_edge_decontamination`, or
58
+ `modify_selection` `contract` 1 px before masking.
59
+ **Check:** `inspect_mask_edge` with `target: "layer-mask"` shows no halo.
60
+ 6. **Ground it.** With the product on its own layer (`layer_via_copy`, or the masked photo),
61
+ `add_layer_effect` `drop-shadow` with `angle` 90 (light from above), a small `distance`
62
+ (3-10 px at web sizes), a `size` larger than the distance and `opacity` 0.25-0.45.
63
+ **Check:** `list_layers` shows the effect; the shadow reads as contact, not as a cutout.
64
+ 7. **Frame it.** `set_crop` at the ratio, centred on the product's bounds, with the margin asked.
65
+ **Check:** `get_image_info` `crop`: ratio right, centre within a pixel or two of the
66
+ product's centre, product share of the side as intended.
67
+ 8. **Final check.** `measure_region` on the background corners and the product; `render_preview`
68
+ where available. Report the crop, the background colour and the shadow settings so the rest
69
+ of the set can match.
70
+
71
+ ## Decision table
72
+
73
+ | Backdrop | Selection |
74
+ | --- | --- |
75
+ | Plain, contrasting | `magic_wand` on the product or backdrop (tolerance 10-30) |
76
+ | Plain but close to the product colour | `select_color_range` on the backdrop with low fuzziness, then refine |
77
+ | Busy or textured | `select_subject`, or `select_object` with the product's box |
78
+ | Glass, transparent or reflective product | Select the outline, then lower the mask's `density` where it should show through; ask how it should look |
79
+ | Product with straight edges (a box, a screen) | `add_vector_mask` from the selection, or a path |
80
+
81
+ | Destination | Frame |
82
+ | --- | --- |
83
+ | Marketplace main image | 1:1, white, product about 85 % of the frame |
84
+ | Catalogue or site grid | The grid's ratio, the same margin on every image |
85
+ | Social post | 4:5 or 1:1, room for text if the user asks |
86
+
87
+ ## Recipes
88
+
89
+ **On white.** `magic_wand` on the product → `inspect_mask_edge` → `add_layer_mask` (`layerId`
90
+ of the photo, `from: "selection"`) → `add_pixel_layer` (`color: "#ffffff"`, `belowId` the photo)
91
+ → `measure_region` on a corner and on the product.
92
+
93
+ **Centred square crop.** Product bounds `x, y, w, h`; side `S = max(w, h) / share` (share 0.7-0.85,
94
+ no larger than the image allows); `set_crop` with `x + w/2 - S/2`, `y + h/2 - S/2`, `S`, `S`.
95
+
96
+ **Contact shadow.** `layer_via_copy` the selected product (or use the masked layer), then
97
+ `add_layer_effect` `kind: "drop-shadow"` with `settings: { angle: 90, distance: 6, size: 10,
98
+ opacity: 0.4 }`; adjust with `update_layer_effect`.
99
+
100
+ ## Pitfalls
101
+
102
+ - **Off-white that looks white.** 245 grey reads as white on screen and fails a marketplace
103
+ check; measure `p1`, not the mean.
104
+ - **Erasing the backdrop.** Deleted pixels cannot be fixed later; mask instead.
105
+ - **Colour shift from the cutout.** A decontamination or a colour adjustment above the product
106
+ changes it; measure the product before and after, and keep corrections below the product.
107
+ - **A halo of the old backdrop** shows on white more than anywhere; inspect the edge and
108
+ decontaminate or contract by a pixel.
109
+ - **A shadow that floats.** A large distance or a sideways angle looks pasted on; keep it short
110
+ and below.
111
+ - **Inconsistent sets.** Each image cropped by eye drifts; compute the crop from the product's
112
+ bounds with the same share for every image.
113
+
114
+ ## Tools used
115
+
116
+ - `list_layers`, `get_image_info`, `measure_region`: read the stack, the frame and the colours
117
+ - `magic_wand`, `select_color_range`, `select_subject`, `select_object`, `invert_selection`: select the product
118
+ - `refine_selection`, `modify_selection`, `get_selection`, `inspect_mask_edge`: refine and check the edge
119
+ - `add_layer_mask`, `add_vector_mask`, `set_layer_mask_properties`: hide the backdrop, editably
120
+ - `set_edge_decontamination`: remove the backdrop's colour from the edge
121
+ - `add_pixel_layer`: the background fill below the product
122
+ - `layer_via_copy`: the product on its own layer
123
+ - `add_layer_effect`, `update_layer_effect`: the shadow
124
+ - `set_crop`, `reset_crop`: the frame
125
+ - `render_preview`: look at the result
126
+ - `undo_image`: take back a step
@@ -0,0 +1,19 @@
1
+ {
2
+ "prompt": "The product looks like it's floating. Put it on its own layer and add a soft shadow under it that I can still adjust.",
3
+ "fixture": {
4
+ "editor": "image",
5
+ "width": 120,
6
+ "height": 90,
7
+ "background": [200, 198, 195],
8
+ "shapes": [{ "shape": "rect", "x": 40, "y": 25, "width": 40, "height": 40, "color": [30, 90, 160] }]
9
+ },
10
+ "checks": [
11
+ { "why": "the product is on its own layer, cut to its shape", "tool": "list_layers", "expect": { "layers": { "$some": { "type": "pixel", "bounds": { "x": { "$min": 39, "$max": 41 }, "y": { "$min": 24, "$max": 26 }, "width": { "$min": 39, "$max": 42 }, "height": { "$min": 39, "$max": 42 } } } } } },
12
+ { "why": "a live drop shadow on that layer", "tool": "list_layers", "expect": { "layers": { "$some": { "effects": { "$expr": "v && JSON.stringify(v).includes('drop-shadow')" } } } } }
13
+ ],
14
+ "solution": [
15
+ { "tool": "magic_wand", "args": { "x": 60, "y": 45, "tolerance": 20, "contiguous": true } },
16
+ { "tool": "layer_via_copy", "args": {} },
17
+ { "tool": "add_layer_effect", "args": { "layerId": { "$read": "list_layers", "path": "activeLayerId" }, "kind": "drop-shadow", "settings": { "distance": 6, "size": 10, "opacity": 0.4, "angle": 90 } } }
18
+ ]
19
+ }
@@ -0,0 +1,19 @@
1
+ {
2
+ "prompt": "Crop to a square for the marketplace listing with the product centred, leaving some breathing room around it (the product should fill roughly two thirds of the frame).",
3
+ "fixture": {
4
+ "editor": "image",
5
+ "width": 120,
6
+ "height": 90,
7
+ "background": [200, 198, 195],
8
+ "shapes": [{ "shape": "rect", "x": 40, "y": 25, "width": 40, "height": 40, "color": [30, 90, 160] }]
9
+ },
10
+ "checks": [
11
+ { "why": "a square crop", "tool": "get_image_info", "expect": { "crop": { "$expr": "v && Math.abs(v.width - v.height) <= 1" } } },
12
+ { "why": "the product is centred (within 2 px)", "tool": "get_image_info", "expect": { "crop": { "$expr": "v && Math.abs(v.x + v.width / 2 - 60) <= 2 && Math.abs(v.y + v.height / 2 - 45) <= 2" } } },
13
+ { "why": "the product fills 60-85 % of the side", "tool": "get_image_info", "expect": { "crop": { "$expr": "v && 40 / v.width >= 0.6 && 40 / v.width <= 0.85" } } }
14
+ ],
15
+ "solution": [
16
+ { "tool": "get_image_info" },
17
+ { "tool": "set_crop", "args": { "x": 32, "y": 17, "width": 56, "height": 56 } }
18
+ ]
19
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "prompt": "Put this product on a pure white background for the shop, without changing the product's colour. Keep it editable.",
3
+ "fixture": {
4
+ "editor": "image",
5
+ "width": 120,
6
+ "height": 90,
7
+ "background": [200, 198, 195],
8
+ "shapes": [{ "shape": "rect", "x": 40, "y": 25, "width": 40, "height": 40, "color": [30, 90, 160] }]
9
+ },
10
+ "checks": [
11
+ { "why": "the background is pure, neutral white", "tool": "measure_region", "args": { "x": 0, "y": 0, "width": 30, "height": 20 }, "expect": { "stats": { "luminance": { "p1": { "$min": 250 } } }, "cast": { "redBlue": { "$min": -2, "$max": 2 } } } },
12
+ { "why": "the area under the product is white too", "tool": "measure_region", "args": { "x": 0, "y": 72, "width": 120, "height": 18 }, "expect": { "stats": { "luminance": { "p1": { "$min": 250 } } } } },
13
+ { "why": "the product keeps its colour", "tool": "measure_region", "args": { "x": 45, "y": 30, "width": 30, "height": 30 }, "expect": { "stats": { "red": { "mean": { "$min": 26, "$max": 34 } }, "green": { "mean": { "$min": 86, "$max": 94 } }, "blue": { "mean": { "$min": 156, "$max": 164 } } } } },
14
+ { "why": "the backdrop is hidden by a mask, not deleted", "tool": "list_layers", "expect": { "layers": { "$some": { "type": "pixel", "mask": { "kind": "raster" } } } } }
15
+ ],
16
+ "solution": [
17
+ { "tool": "magic_wand", "args": { "x": 60, "y": 45, "tolerance": 20, "contiguous": true } },
18
+ { "tool": "inspect_mask_edge" },
19
+ { "tool": "add_layer_mask", "args": { "layerId": "background", "from": "selection" } },
20
+ { "tool": "add_pixel_layer", "args": { "name": "White", "color": "#ffffff", "belowId": "background" } },
21
+ { "tool": "measure_region", "args": { "x": 0, "y": 0, "width": 30, "height": 20 } }
22
+ ]
23
+ }
@@ -54,6 +54,15 @@ carry a live effect stack. Every command is a `vector.*` command run with `run_c
54
54
  Parameter details (gradient fields, effect parameters, width profiles, envelopes) are in
55
55
  [references/commands.md](references/commands.md).
56
56
 
57
+ ## Workflow skills
58
+
59
+ For these jobs, load the workflow skill too: it has the method, the checks between steps and
60
+ the pitfalls, and uses the commands below.
61
+
62
+ - `pixerate-vector-icon-set`: icons on one grid, one stroke and style, crisp, as symbols and SVG.
63
+ - `pixerate-vector-logo`: marks from booleans, a lockup, colour variants, outlined delivery files.
64
+ - `pixerate-vector-illustration`: block in, gradients and stacked fills, masks, blends, shadows.
65
+
57
66
  ## Working method
58
67
 
59
68
  1. **Read.** `vector.list` gives the artboard size, every node depth first (id, type, name,
@@ -74,30 +83,16 @@ Parameter details (gradient fields, effect parameters, width profiles, envelopes
74
83
 
75
84
  ## Best practices
76
85
 
77
- - **Diagrams with connectors.** Lay the boxes out first, then `vector.connect` them (in one
78
- `run_commands` batch) rather than drawing lines: connectors stay attached when boxes move, and
79
- `route: "elbow"` with a small `radius` reads well in flowcharts. Leave sides `auto` unless a
80
- connector must leave a particular side.
81
-
82
86
  - **Frames for anything that resizes.** Build screens, cards, buttons and banners inside a frame
83
87
  (`vector.addFrame` or `vector.frameSelection`), set each child's constraints with
84
88
  `vector.setResizing` (backgrounds and dividers `leftRight`, icons pinned to their corner, titles
85
89
  `center`), then resize with `vector.setFrame` to produce other sizes from one design. Export each
86
90
  artboard with `vector.exportFrameSvg`.
87
-
88
- - **Constraints for precise, editable geometry.** For technical drawings, diagrams, floor plans or
89
- any shape that must keep exact relations, draw it roughly, run `vector.autoConstrain`, then add
90
- the dimensions that matter (`vector.addConstraint` distance / angle / radius) and name them so
91
- related sizes follow one another (`vector.setConstraintValue` with expressions like `w / 2`).
92
- Check `vector.listConstraints` `dof`: aim for `fully` on parts that must not drift; `over`
93
- means some constraints repeat others. Prefer few, meaningful constraints over many.
94
-
95
91
  - **Symbols for repeats.** When the same art appears several times (icons in a set, trees in a
96
92
  scene, bullets, a logo mark), make it once and place instances (`vector.makeSymbol`, then
97
93
  `vector.placeSymbol` with `x` / `y` or a `transform`), so one edit in isolation changes them all.
98
94
  Give each a clear name. Use `vector.setInstanceOptions` `fill` / `stroke` for colour variants and
99
95
  `hidden` to drop a part, rather than detaching; detach only when one copy must diverge for good.
100
-
101
96
  - **Text.** Use point text for labels and headlines, area text (`width`, `height`) for anything
102
97
  that should wrap, text on a path for badges and seals. Check `overflow` in the summary every
103
98
  text write returns: hidden lines or characters mean the box or path is too small. Keep one or
@@ -106,56 +101,11 @@ Parameter details (gradient fields, effect parameters, width profiles, envelopes
106
101
  than splitting text into separate nodes, so it still edits as one paragraph. Create Outlines
107
102
  only for final artwork or logos that must not depend on fonts; keep a copy as text if it may
108
103
  change.
109
- For Japanese, Chinese or Korean set in columns (signage, book spines, posters), use vertical
110
- text (`textOrientation: "vertical"` or `vector.setTextOrientation`): CJK stands upright, Latin
111
- turns sideways; set a short Latin range `upright: true` to stack it letter by letter. In an
112
- area, columns that do not fit past the left edge are `overflow`. Vertical text cannot follow a
113
- path.
114
-
115
- - **Clean paths.** Use the fewest anchors that describe the shape: anchors at extrema (top,
116
- bottom, left, right of a curve) and at true corners, handles horizontal or vertical at extrema,
117
- each handle about a third of its segment. Run `vector.removeRedundantPoints` after
118
- booleans, knife and pencil work; `vector.smooth` for lumpy curves; `vector.deleteAnchors`
119
- keeps the outline by refitting neighbours.
120
- - **Live corners over drawn arcs.** Give rectangles, polygons and stars a `cornerRadius`
121
- (`vector.addShape`) or set it with `vector.setCorners`; the anchors stay sharp and editable and
122
- radii shrink to fit short sides. Keep one radius scale across a design. For app icons and UI
123
- cards use `smoothing` (about 0.6) on `vector.setCorners` for continuous, squircle corners.
104
+ - **Paths, strokes, booleans, gradients, stacks, masks, shadows, connectors, constraints and
105
+ vertical text:** read [references/best-practices.md](references/best-practices.md) first.
124
106
  - **Non-destructive first.** Live booleans (`vector.boolean` with `live: true`), path effects
125
107
  and envelopes keep the operands editable. A destructive boolean or Pathfinder replaces its
126
108
  inputs; do that last, and only when asked for flat output.
127
- - **Boolean vs Pathfinder vs Shape Builder.** `vector.boolean` for a single result shape (union
128
- for silhouettes, subtract for cut-outs, intersect, exclude). `vector.pathfinder` when you need
129
- every region separately (divide), hidden parts removed (trim, merge) or a crop. Shape Builder
130
- when you want to merge or delete particular regions picked by point.
131
- - **Strokes.** Use `strokeAlign: "inside"` for icons and UI shapes so the outer size stays exact,
132
- `outside` for outlines around artwork; open paths always stroke centred. Use round caps and
133
- joins for friendly line icons, miter for geometric ones. Keep one stroke width across an icon
134
- set (for example 1.5 or 2 units on a 24-unit grid).
135
- - **Icon grid.** Use a square artboard (24, 32 or 48 units), keep a padding margin, place
136
- coordinates on whole or half units so strokes render crisp, and align optical centres.
137
- - **Gradients.** Two to four stops are usually enough. Use `interpolation: "oklab"` for
138
- perceptually even blends without the grey or muddy midpoint sRGB gives between saturated hues;
139
- `midpoint` to shift the half-way point instead of adding stops. Prefer linear or radial for
140
- portability; conic and diamond export as SVG patterns.
141
- - **Appearance stacks over duplicate shapes.** For a gradient over a texture, a tinted overlay,
142
- an outline around an outline or a stroke inside and another outside, stack layers on one path
143
- (`vector.addFill`, `vector.addStroke`) instead of copying the shape: the copies would drift
144
- apart when edited. Give each stroke its own `strokeAlign` (outside under, inside or center on
145
- top) and widths that step down towards the top. Use per-layer `opacity` and `blendMode`
146
- (multiply to darken, screen to lighten, overlay for texture) rather than node opacity when only
147
- one paint should fade.
148
- - **Masks over cut-outs.** To show artwork inside a shape (a photo-like fill in a circle, a
149
- pattern inside text-like outlines, a scene seen through a window), use
150
- `vector.makeClippingMask` rather than a destructive intersect: the contents stay editable and
151
- movable inside the frame. Draw the mask last (on top) and make the mask from it; edit the mask
152
- later by its `maskId`. Use `vector.makeOpacityMask` with a white-to-black gradient for fades
153
- and reflections (luminance: white shows). A clip's own paint is never drawn, so its fill
154
- colour does not matter. Effects and shadows on the mask group follow the masked silhouette.
155
- - **Shadows and glows.** Keep drop shadows subtle: black at 0.15 to 0.3 opacity, `offsetY` 2 to 8,
156
- `blur` about twice the offset. Use one light direction for every shadow in a design. Filters
157
- export as SVG filters, which some tools (and older renderers) draw slightly differently or not
158
- at all; a design that must look identical everywhere should avoid them or rasterize.
159
109
  - **Grouping and naming.** Group the parts of one object (`vector.group` with a `name`), name
160
110
  paths that matter (`name` on add), and keep the tree shallow.
161
111
  - **SVG hygiene.** Export with `precision` 2 or 3 (default 3); higher only for tiny artboards.
@@ -0,0 +1,62 @@
1
+ # Drawing best practices
2
+
3
+ Detail for `SKILL.md`'s best practices, by topic.
4
+
5
+ - **Clean paths.** Use the fewest anchors that describe the shape: anchors at extrema (top,
6
+ bottom, left, right of a curve) and at true corners, handles horizontal or vertical at extrema,
7
+ each handle about a third of its segment. Run `vector.removeRedundantPoints` after
8
+ booleans, knife and pencil work; `vector.smooth` for lumpy curves; `vector.deleteAnchors`
9
+ keeps the outline by refitting neighbours.
10
+ - **Live corners over drawn arcs.** Give rectangles, polygons and stars a `cornerRadius`
11
+ (`vector.addShape`) or set it with `vector.setCorners`; the anchors stay sharp and editable and
12
+ radii shrink to fit short sides. Keep one radius scale across a design. For app icons and UI
13
+ cards use `smoothing` (about 0.6) on `vector.setCorners` for continuous, squircle corners.
14
+ - **Boolean vs Pathfinder vs Shape Builder.** `vector.boolean` for a single result shape (union
15
+ for silhouettes, subtract for cut-outs, intersect, exclude). `vector.pathfinder` when you need
16
+ every region separately (divide), hidden parts removed (trim, merge) or a crop. Shape Builder
17
+ when you want to merge or delete particular regions picked by point.
18
+ - **Strokes.** Use `strokeAlign: "inside"` for icons and UI shapes so the outer size stays exact,
19
+ `outside` for outlines around artwork; open paths always stroke centred. Use round caps and
20
+ joins for friendly line icons, miter for geometric ones. Keep one stroke width across an icon
21
+ set (for example 1.5 or 2 units on a 24-unit grid).
22
+ - **Icon grid.** Use a square artboard (24, 32 or 48 units), keep a padding margin, place
23
+ coordinates on whole or half units so strokes render crisp, and align optical centres.
24
+ - **Gradients.** Two to four stops are usually enough. Use `interpolation: "oklab"` for
25
+ perceptually even blends without the grey or muddy midpoint sRGB gives between saturated hues;
26
+ `midpoint` to shift the half-way point instead of adding stops. Prefer linear or radial for
27
+ portability; conic and diamond export as SVG patterns.
28
+ - **Appearance stacks over duplicate shapes.** For a gradient over a texture, a tinted overlay,
29
+ an outline around an outline or a stroke inside and another outside, stack layers on one path
30
+ (`vector.addFill`, `vector.addStroke`) instead of copying the shape: the copies would drift
31
+ apart when edited. Give each stroke its own `strokeAlign` (outside under, inside or center on
32
+ top) and widths that step down towards the top. Use per-layer `opacity` and `blendMode`
33
+ (multiply to darken, screen to lighten, overlay for texture) rather than node opacity when only
34
+ one paint should fade.
35
+ - **Masks over cut-outs.** To show artwork inside a shape (a photo-like fill in a circle, a
36
+ pattern inside text-like outlines, a scene seen through a window), use
37
+ `vector.makeClippingMask` rather than a destructive intersect: the contents stay editable and
38
+ movable inside the frame. Draw the mask last (on top) and make the mask from it; edit the mask
39
+ later by its `maskId`. Use `vector.makeOpacityMask` with a white-to-black gradient for fades
40
+ and reflections (luminance: white shows). A clip's own paint is never drawn, so its fill
41
+ colour does not matter. Effects and shadows on the mask group follow the masked silhouette.
42
+ - **Shadows and glows.** Keep drop shadows subtle: black at 0.15 to 0.3 opacity, `offsetY` 2 to 8,
43
+ `blur` about twice the offset. Use one light direction for every shadow in a design. Filters
44
+ export as SVG filters, which some tools (and older renderers) draw slightly differently or not
45
+ at all; a design that must look identical everywhere should avoid them or rasterize.
46
+ - **Diagrams with connectors.** Lay the boxes out first, then `vector.connect` them (in one
47
+ `run_commands` batch) rather than drawing lines: connectors stay attached when boxes move, and
48
+ `route: "elbow"` with a small `radius` reads well in flowcharts. Leave sides `auto` unless a
49
+ connector must leave a particular side.
50
+
51
+ - **Constraints for precise, editable geometry.** For technical drawings, diagrams, floor plans or
52
+ any shape that must keep exact relations, draw it roughly, run `vector.autoConstrain`, then add
53
+ the dimensions that matter (`vector.addConstraint` distance / angle / radius) and name them so
54
+ related sizes follow one another (`vector.setConstraintValue` with expressions like `w / 2`).
55
+ Check `vector.listConstraints` `dof`: aim for `fully` on parts that must not drift; `over`
56
+ means some constraints repeat others. Prefer few, meaningful constraints over many.
57
+
58
+ - **Vertical text.** For Japanese, Chinese or Korean set in columns (signage, book spines, posters), use vertical
59
+ text (`textOrientation: "vertical"` or `vector.setTextOrientation`): CJK stands upright, Latin
60
+ turns sideways; set a short Latin range `upright: true` to stack it letter by letter. In an
61
+ area, columns that do not fit past the left edge are `overflow`. Vertical text cannot follow a
62
+ path.