@pixerate/editor 0.220.0 → 0.221.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.
- package/dist/image-editor/controller.d.cts +27 -4
- package/dist/image-editor/controller.d.ts +27 -4
- package/dist/image-editor/index.cjs +2276 -1864
- package/dist/image-editor/index.js +1240 -828
- package/dist/image-editor/layers/serialize.d.cts +2 -0
- package/dist/image-editor/layers/serialize.d.ts +2 -0
- package/dist/image-editor/selection/index.d.cts +1 -0
- package/dist/image-editor/selection/index.d.ts +1 -0
- package/dist/image-editor/selection/inspect.d.cts +1 -1
- package/dist/image-editor/selection/inspect.d.ts +1 -1
- package/dist/image-editor/selection/recipe.d.cts +89 -0
- package/dist/image-editor/selection/recipe.d.ts +89 -0
- package/dist/image-editor/types.d.cts +9 -0
- package/dist/image-editor/types.d.ts +9 -0
- package/package.json +1 -1
- package/skills/README.md +40 -9
- package/skills/WORKFLOW_TEMPLATE.md +50 -0
- package/skills/pixerate-image-editor/SKILL.md +14 -11
- package/skills/pixerate-image-editor/references/painting.md +17 -1
- package/skills/pixerate-image-selection-masking/SKILL.md +3 -3
- package/skills/pixerate-image-selection-masking/evals/local-adjustment.json +22 -0
- package/skills/pixerate-image-selection-masking/evals/mask-out-background.json +24 -0
- package/skills/pixerate-image-selection-masking/evals/select-one-of-two.json +22 -0
- package/skills/pixerate-spreadsheet/SKILL.md +7 -0
- package/skills/pixerate-spreadsheet-clean-import/SKILL.md +154 -0
- package/skills/pixerate-spreadsheet-clean-import/evals/clean-in-place.json +43 -0
- package/skills/pixerate-spreadsheet-clean-import/evals/dedupe-keep-latest.json +25 -0
- package/skills/pixerate-spreadsheet-clean-import/evals/fix-types-only.json +24 -0
- package/skills/pixerate-video-editor/SKILL.md +8 -0
|
@@ -242,6 +242,8 @@ export interface SerializedLayeredImageEditorState {
|
|
|
242
242
|
/** Blur migrated from a v1 / v2 document with adjustment layers (runs after the layers). */
|
|
243
243
|
legacyOutputBlur?: number;
|
|
244
244
|
selection?: SerializedSelection;
|
|
245
|
+
/** The selection's recipe (live selections): the steps that made it, as JSON. */
|
|
246
|
+
selectionRecipe?: unknown[];
|
|
245
247
|
/** Alpha channels (saved selections), run-length encoded. */
|
|
246
248
|
channels?: SerializedAlphaChannel[];
|
|
247
249
|
/** Document paths (the Paths panel). */
|
|
@@ -242,6 +242,8 @@ export interface SerializedLayeredImageEditorState {
|
|
|
242
242
|
/** Blur migrated from a v1 / v2 document with adjustment layers (runs after the layers). */
|
|
243
243
|
legacyOutputBlur?: number;
|
|
244
244
|
selection?: SerializedSelection;
|
|
245
|
+
/** The selection's recipe (live selections): the steps that made it, as JSON. */
|
|
246
|
+
selectionRecipe?: unknown[];
|
|
245
247
|
/** Alpha channels (saved selections), run-length encoded. */
|
|
246
248
|
channels?: SerializedAlphaChannel[];
|
|
247
249
|
/** Document paths (the Paths panel). */
|
|
@@ -31,7 +31,7 @@ export interface MaskEdgeReport {
|
|
|
31
31
|
edgeAlignment: number;
|
|
32
32
|
/** Turning of the outline beyond what closing it needs, in radians per 100 px of outline (0: smooth). */
|
|
33
33
|
jaggedness: number;
|
|
34
|
-
/** 0–1: the share of edge pixels
|
|
34
|
+
/** 0–1: the share of edge pixels in a transition wider than anti-aliasing (partly covered, no fully selected and unselected pixel within 2 px). */
|
|
35
35
|
softFraction: number;
|
|
36
36
|
/** Unselected areas enclosed by the selection, largest first (at most 10 listed). */
|
|
37
37
|
holes: {
|
|
@@ -31,7 +31,7 @@ export interface MaskEdgeReport {
|
|
|
31
31
|
edgeAlignment: number;
|
|
32
32
|
/** Turning of the outline beyond what closing it needs, in radians per 100 px of outline (0: smooth). */
|
|
33
33
|
jaggedness: number;
|
|
34
|
-
/** 0–1: the share of edge pixels
|
|
34
|
+
/** 0–1: the share of edge pixels in a transition wider than anti-aliasing (partly covered, no fully selected and unselected pixel within 2 px). */
|
|
35
35
|
softFraction: number;
|
|
36
36
|
/** Unselected areas enclosed by the selection, largest first (at most 10 listed). */
|
|
37
37
|
holes: {
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live selections: a selection kept as the steps that made it (its recipe) beside its bitmap, so a
|
|
3
|
+
* step can be edited, inserted or removed afterwards and the selection replayed. The bitmap stays
|
|
4
|
+
* what everything else uses; a change no step can describe drops the recipe (the selection is then
|
|
5
|
+
* frozen). Steps are plain JSON: the arguments the selection commands took.
|
|
6
|
+
*/
|
|
7
|
+
import type { CropBox, ImagePoint } from '../types.cjs';
|
|
8
|
+
import { type PixelBuffer, type SelectionMask, type SelectionMode } from './mask.cjs';
|
|
9
|
+
import { type ColorRangeSample } from './wand.cjs';
|
|
10
|
+
import { type SelectionModifyOp } from './modify.cjs';
|
|
11
|
+
import { type RefineEdgeParams } from './refine.cjs';
|
|
12
|
+
/** Where pixel steps sample (as the selection commands' `SampleOptions`). */
|
|
13
|
+
export interface RecipeSampling {
|
|
14
|
+
sample?: 'composite' | 'active-layer';
|
|
15
|
+
sampleMerged?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** How a new shape combines with what the earlier steps selected. */
|
|
18
|
+
export interface RecipeCombine {
|
|
19
|
+
mode?: SelectionMode;
|
|
20
|
+
/** Feather the new shape by this many pixels before combining. */
|
|
21
|
+
feather?: number;
|
|
22
|
+
/** Invert the new shape before combining. */
|
|
23
|
+
invert?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** One step of a selection's recipe. */
|
|
26
|
+
export type SelectionRecipeStep = ({
|
|
27
|
+
op: 'rect' | 'ellipse';
|
|
28
|
+
rect: CropBox;
|
|
29
|
+
antiAlias?: boolean;
|
|
30
|
+
} & RecipeCombine) | ({
|
|
31
|
+
op: 'polygon';
|
|
32
|
+
points: ImagePoint[];
|
|
33
|
+
antiAlias?: boolean;
|
|
34
|
+
} & RecipeCombine) | ({
|
|
35
|
+
op: 'magic-wand';
|
|
36
|
+
point: ImagePoint;
|
|
37
|
+
options?: Record<string, unknown>;
|
|
38
|
+
} & RecipeCombine & RecipeSampling) | ({
|
|
39
|
+
op: 'color-range';
|
|
40
|
+
samples: ColorRangeSample[];
|
|
41
|
+
options?: Record<string, unknown>;
|
|
42
|
+
} & RecipeCombine & RecipeSampling) | ({
|
|
43
|
+
op: 'color-range-preset';
|
|
44
|
+
preset: string;
|
|
45
|
+
falloff?: number;
|
|
46
|
+
} & RecipeCombine & RecipeSampling) | {
|
|
47
|
+
op: 'all';
|
|
48
|
+
} | {
|
|
49
|
+
op: 'invert';
|
|
50
|
+
} | {
|
|
51
|
+
op: 'modify';
|
|
52
|
+
modify: SelectionModifyOp;
|
|
53
|
+
radius: number;
|
|
54
|
+
} | ({
|
|
55
|
+
op: 'refine';
|
|
56
|
+
params: RefineEdgeParams;
|
|
57
|
+
} & RecipeSampling);
|
|
58
|
+
export type SelectionRecipeOp = SelectionRecipeStep['op'];
|
|
59
|
+
export declare const SELECTION_RECIPE_OPS: readonly SelectionRecipeOp[];
|
|
60
|
+
/** Steps that make a new shape (combined by `mode`); the rest change what is already selected. */
|
|
61
|
+
export declare function isShapeStep(step: SelectionRecipeStep): boolean;
|
|
62
|
+
/** Whether a step reads the image's pixels. */
|
|
63
|
+
export declare function stepNeedsPixels(step: SelectionRecipeStep): boolean;
|
|
64
|
+
/** A key for a pixel step's sampling (steps with the same key read the same pixels). */
|
|
65
|
+
export declare function samplingKey(step: SelectionRecipeStep): string;
|
|
66
|
+
/** Checks and copies one step (as JSON from an agent or a document); throws a useful error. */
|
|
67
|
+
export declare function readRecipeStep(raw: unknown, at?: string): SelectionRecipeStep;
|
|
68
|
+
/**
|
|
69
|
+
* The selection `steps` make on a `width × height` canvas, from nothing (null when nothing ends
|
|
70
|
+
* up selected). `pixelsFor` gives a pixel step its image (see `samplingKey`).
|
|
71
|
+
*/
|
|
72
|
+
export declare function replaySelectionRecipe(steps: readonly SelectionRecipeStep[], width: number, height: number, pixelsFor: (step: SelectionRecipeStep) => PixelBuffer): SelectionMask | null;
|
|
73
|
+
/** A recipe as an agent reads it: each step with its index. */
|
|
74
|
+
export declare function describeRecipe(steps: readonly SelectionRecipeStep[]): Array<{
|
|
75
|
+
index: number;
|
|
76
|
+
} & SelectionRecipeStep>;
|
|
77
|
+
/** A step in a few words, for a recipe list ("Rectangle 30 × 20 at 10, 10", "Smooth 2 px"). */
|
|
78
|
+
export declare function describeRecipeStep(step: SelectionRecipeStep): string;
|
|
79
|
+
/**
|
|
80
|
+
* A step's main setting for a recipe list (feather, tolerance, fuzziness, falloff or radius) and
|
|
81
|
+
* the change that sets it, or null for steps with none.
|
|
82
|
+
*/
|
|
83
|
+
export declare function recipeStepSetting(step: SelectionRecipeStep): {
|
|
84
|
+
label: string;
|
|
85
|
+
value: number;
|
|
86
|
+
min: number;
|
|
87
|
+
max: number;
|
|
88
|
+
change: (v: number) => Record<string, unknown>;
|
|
89
|
+
} | null;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Live selections: a selection kept as the steps that made it (its recipe) beside its bitmap, so a
|
|
3
|
+
* step can be edited, inserted or removed afterwards and the selection replayed. The bitmap stays
|
|
4
|
+
* what everything else uses; a change no step can describe drops the recipe (the selection is then
|
|
5
|
+
* frozen). Steps are plain JSON: the arguments the selection commands took.
|
|
6
|
+
*/
|
|
7
|
+
import type { CropBox, ImagePoint } from '../types.js';
|
|
8
|
+
import { type PixelBuffer, type SelectionMask, type SelectionMode } from './mask.js';
|
|
9
|
+
import { type ColorRangeSample } from './wand.js';
|
|
10
|
+
import { type SelectionModifyOp } from './modify.js';
|
|
11
|
+
import { type RefineEdgeParams } from './refine.js';
|
|
12
|
+
/** Where pixel steps sample (as the selection commands' `SampleOptions`). */
|
|
13
|
+
export interface RecipeSampling {
|
|
14
|
+
sample?: 'composite' | 'active-layer';
|
|
15
|
+
sampleMerged?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/** How a new shape combines with what the earlier steps selected. */
|
|
18
|
+
export interface RecipeCombine {
|
|
19
|
+
mode?: SelectionMode;
|
|
20
|
+
/** Feather the new shape by this many pixels before combining. */
|
|
21
|
+
feather?: number;
|
|
22
|
+
/** Invert the new shape before combining. */
|
|
23
|
+
invert?: boolean;
|
|
24
|
+
}
|
|
25
|
+
/** One step of a selection's recipe. */
|
|
26
|
+
export type SelectionRecipeStep = ({
|
|
27
|
+
op: 'rect' | 'ellipse';
|
|
28
|
+
rect: CropBox;
|
|
29
|
+
antiAlias?: boolean;
|
|
30
|
+
} & RecipeCombine) | ({
|
|
31
|
+
op: 'polygon';
|
|
32
|
+
points: ImagePoint[];
|
|
33
|
+
antiAlias?: boolean;
|
|
34
|
+
} & RecipeCombine) | ({
|
|
35
|
+
op: 'magic-wand';
|
|
36
|
+
point: ImagePoint;
|
|
37
|
+
options?: Record<string, unknown>;
|
|
38
|
+
} & RecipeCombine & RecipeSampling) | ({
|
|
39
|
+
op: 'color-range';
|
|
40
|
+
samples: ColorRangeSample[];
|
|
41
|
+
options?: Record<string, unknown>;
|
|
42
|
+
} & RecipeCombine & RecipeSampling) | ({
|
|
43
|
+
op: 'color-range-preset';
|
|
44
|
+
preset: string;
|
|
45
|
+
falloff?: number;
|
|
46
|
+
} & RecipeCombine & RecipeSampling) | {
|
|
47
|
+
op: 'all';
|
|
48
|
+
} | {
|
|
49
|
+
op: 'invert';
|
|
50
|
+
} | {
|
|
51
|
+
op: 'modify';
|
|
52
|
+
modify: SelectionModifyOp;
|
|
53
|
+
radius: number;
|
|
54
|
+
} | ({
|
|
55
|
+
op: 'refine';
|
|
56
|
+
params: RefineEdgeParams;
|
|
57
|
+
} & RecipeSampling);
|
|
58
|
+
export type SelectionRecipeOp = SelectionRecipeStep['op'];
|
|
59
|
+
export declare const SELECTION_RECIPE_OPS: readonly SelectionRecipeOp[];
|
|
60
|
+
/** Steps that make a new shape (combined by `mode`); the rest change what is already selected. */
|
|
61
|
+
export declare function isShapeStep(step: SelectionRecipeStep): boolean;
|
|
62
|
+
/** Whether a step reads the image's pixels. */
|
|
63
|
+
export declare function stepNeedsPixels(step: SelectionRecipeStep): boolean;
|
|
64
|
+
/** A key for a pixel step's sampling (steps with the same key read the same pixels). */
|
|
65
|
+
export declare function samplingKey(step: SelectionRecipeStep): string;
|
|
66
|
+
/** Checks and copies one step (as JSON from an agent or a document); throws a useful error. */
|
|
67
|
+
export declare function readRecipeStep(raw: unknown, at?: string): SelectionRecipeStep;
|
|
68
|
+
/**
|
|
69
|
+
* The selection `steps` make on a `width × height` canvas, from nothing (null when nothing ends
|
|
70
|
+
* up selected). `pixelsFor` gives a pixel step its image (see `samplingKey`).
|
|
71
|
+
*/
|
|
72
|
+
export declare function replaySelectionRecipe(steps: readonly SelectionRecipeStep[], width: number, height: number, pixelsFor: (step: SelectionRecipeStep) => PixelBuffer): SelectionMask | null;
|
|
73
|
+
/** A recipe as an agent reads it: each step with its index. */
|
|
74
|
+
export declare function describeRecipe(steps: readonly SelectionRecipeStep[]): Array<{
|
|
75
|
+
index: number;
|
|
76
|
+
} & SelectionRecipeStep>;
|
|
77
|
+
/** A step in a few words, for a recipe list ("Rectangle 30 × 20 at 10, 10", "Smooth 2 px"). */
|
|
78
|
+
export declare function describeRecipeStep(step: SelectionRecipeStep): string;
|
|
79
|
+
/**
|
|
80
|
+
* A step's main setting for a recipe list (feather, tolerance, fuzziness, falloff or radius) and
|
|
81
|
+
* the change that sets it, or null for steps with none.
|
|
82
|
+
*/
|
|
83
|
+
export declare function recipeStepSetting(step: SelectionRecipeStep): {
|
|
84
|
+
label: string;
|
|
85
|
+
value: number;
|
|
86
|
+
min: number;
|
|
87
|
+
max: number;
|
|
88
|
+
change: (v: number) => Record<string, unknown>;
|
|
89
|
+
} | null;
|
|
@@ -285,6 +285,7 @@ export type ImageEditorTool = 'select' | 'crop' | 'pen' | 'rect' | 'circle' | 'a
|
|
|
285
285
|
| 'path-select' | 'direct-select' | 'path-pen';
|
|
286
286
|
export type ImageEditorFit = 'contain' | 'margin';
|
|
287
287
|
import type { DocumentColorMode, DocumentColorProfile, ProofSetup } from './color-management.cjs';
|
|
288
|
+
import type { SelectionRecipeStep } from './selection/recipe.cjs';
|
|
288
289
|
import type { BitDepth } from '../raster/deep.cjs';
|
|
289
290
|
export interface ImageEditorState {
|
|
290
291
|
sourceUrl: string | null;
|
|
@@ -319,6 +320,12 @@ export interface ImageEditorState {
|
|
|
319
320
|
* growing as the brush moves). View state: not an edit, not in history, not saved.
|
|
320
321
|
*/
|
|
321
322
|
selectionPreview?: SelectionMask | null;
|
|
323
|
+
/**
|
|
324
|
+
* Live selections: the steps that made `selection` (see `selection/recipe`), so they can be
|
|
325
|
+
* edited and replayed; null or missing when it is frozen (made or changed by something no step
|
|
326
|
+
* describes). Recorded in history and saved.
|
|
327
|
+
*/
|
|
328
|
+
selectionRecipe?: SelectionRecipeStep[] | null;
|
|
322
329
|
/**
|
|
323
330
|
* `compare_selections`' overlay: the added area tinted green, the removed red. Shown while the
|
|
324
331
|
* selection is still `selection` (any change hides it). View state: not in history, not saved.
|
|
@@ -418,6 +425,8 @@ export interface LegacySerializedImageEditorState {
|
|
|
418
425
|
annotations: Annotation[];
|
|
419
426
|
/** The active selection, run-length encoded. Missing when nothing is selected. */
|
|
420
427
|
selection?: SerializedSelection;
|
|
428
|
+
/** The selection's recipe (live selections): the steps that made it, as JSON. */
|
|
429
|
+
selectionRecipe?: unknown[];
|
|
421
430
|
/** Alpha channels (saved selections), run-length encoded. */
|
|
422
431
|
channels?: SerializedAlphaChannel[];
|
|
423
432
|
/** Document paths (the Paths panel). */
|
|
@@ -285,6 +285,7 @@ export type ImageEditorTool = 'select' | 'crop' | 'pen' | 'rect' | 'circle' | 'a
|
|
|
285
285
|
| 'path-select' | 'direct-select' | 'path-pen';
|
|
286
286
|
export type ImageEditorFit = 'contain' | 'margin';
|
|
287
287
|
import type { DocumentColorMode, DocumentColorProfile, ProofSetup } from './color-management.js';
|
|
288
|
+
import type { SelectionRecipeStep } from './selection/recipe.js';
|
|
288
289
|
import type { BitDepth } from '../raster/deep.js';
|
|
289
290
|
export interface ImageEditorState {
|
|
290
291
|
sourceUrl: string | null;
|
|
@@ -319,6 +320,12 @@ export interface ImageEditorState {
|
|
|
319
320
|
* growing as the brush moves). View state: not an edit, not in history, not saved.
|
|
320
321
|
*/
|
|
321
322
|
selectionPreview?: SelectionMask | null;
|
|
323
|
+
/**
|
|
324
|
+
* Live selections: the steps that made `selection` (see `selection/recipe`), so they can be
|
|
325
|
+
* edited and replayed; null or missing when it is frozen (made or changed by something no step
|
|
326
|
+
* describes). Recorded in history and saved.
|
|
327
|
+
*/
|
|
328
|
+
selectionRecipe?: SelectionRecipeStep[] | null;
|
|
322
329
|
/**
|
|
323
330
|
* `compare_selections`' overlay: the added area tinted green, the removed red. Shown while the
|
|
324
331
|
* selection is still `selection` (any change hides it). View state: not in history, not saved.
|
|
@@ -418,6 +425,8 @@ export interface LegacySerializedImageEditorState {
|
|
|
418
425
|
annotations: Annotation[];
|
|
419
426
|
/** The active selection, run-length encoded. Missing when nothing is selected. */
|
|
420
427
|
selection?: SerializedSelection;
|
|
428
|
+
/** The selection's recipe (live selections): the steps that made it, as JSON. */
|
|
429
|
+
selectionRecipe?: unknown[];
|
|
421
430
|
/** Alpha channels (saved selections), run-length encoded. */
|
|
422
431
|
channels?: SerializedAlphaChannel[];
|
|
423
432
|
/** Document paths (the Paths panel). */
|
package/package.json
CHANGED
package/skills/README.md
CHANGED
|
@@ -5,6 +5,11 @@ Each editor domain in `@pixerate/editor` ships an [Agent Skill](https://agentski
|
|
|
5
5
|
expert in the domain would follow. They are published with the package, so an app or agent
|
|
6
6
|
that uses the tools can load the matching skill.
|
|
7
7
|
|
|
8
|
+
## Reference skills
|
|
9
|
+
|
|
10
|
+
One per editor domain. A reference skill owns its domain's tool and command tables, its mental
|
|
11
|
+
model and its general practice.
|
|
12
|
+
|
|
8
13
|
| Skill | Covers |
|
|
9
14
|
| --- | --- |
|
|
10
15
|
| [`pixerate-agent-tools`](pixerate-agent-tools/SKILL.md) | What every editor has in common: the MCP bridge, toolsets and tool naming, finding tools with `list_toolsets` / `search_tools` / `enable_tools` / `get_skill`, `list_commands` / `run_command` / `run_commands`, errors, undo. Load it with any domain skill. |
|
|
@@ -15,12 +20,9 @@ that uses the tools can load the matching skill.
|
|
|
15
20
|
| [`pixerate-chart`](pixerate-chart/SKILL.md) | Chart specs, data, series, presets, trendlines, Office and Mermaid charts. |
|
|
16
21
|
| [`pixerate-diagram`](pixerate-diagram/SKILL.md) | Mermaid diagrams: nodes, edges, groups, layout. |
|
|
17
22
|
| [`pixerate-image-editor`](pixerate-image-editor/SKILL.md) | Adjustments, adjustment layers, selections and masks. |
|
|
18
|
-
| [`pixerate-image-selection-masking`](pixerate-image-selection-masking/SKILL.md) | Workflow: cutouts, background and sky replacement, local adjustments through masks; choosing a selection method, checking and refining edges, and outputting an editable mask. Built on `pixerate-image-editor`'s tools. |
|
|
19
23
|
| [`pixerate-paint-editor`](pixerate-paint-editor/SKILL.md) | Natural-media painting: paper and grain, dry-media brushes, the brush library, strokes as data, pigment mixing. |
|
|
20
24
|
| [`pixerate-animation-editor`](pixerate-animation-editor/SKILL.md) | Vector animation: shots, commands, timing, Flair and Lottie. |
|
|
21
25
|
| [`pixerate-video-editor`](pixerate-video-editor/SKILL.md) | Non-linear video editing: tracks, clips, transitions, export. |
|
|
22
|
-
| [`pixerate-talking-head-edit`](pixerate-talking-head-edit/SKILL.md) | Talking-head cleanup by transcript: fillers, long pauses and stutters found in one pass and cut as one plan reviewed before it applies. |
|
|
23
|
-
| [`pixerate-bgm-alignment`](pixerate-bgm-alignment/SKILL.md) | Background music: beats and cuts on the beat, ducking under speech, loudness in LUFS. |
|
|
24
26
|
| [`pixerate-generation`](pixerate-generation/SKILL.md) | Planning, estimating and running paid media generation. |
|
|
25
27
|
| [`pixerate-parameters`](pixerate-parameters/SKILL.md) | Effect parameters, presets and keyframes. |
|
|
26
28
|
| [`pixerate-tokens`](pixerate-tokens/SKILL.md) | Design tokens: collections, modes, variables and aliases, through the whole-set document tools. |
|
|
@@ -28,6 +30,20 @@ that uses the tools can load the matching skill.
|
|
|
28
30
|
| [`pixerate-color`](pixerate-color/SKILL.md) | Colour inspection, WCAG accessibility and contrast optimization, CVD simulation, palette audits, harmonies, and OKLab/OKLCH blends. |
|
|
29
31
|
| [`pixerate-typography`](pixerate-typography/SKILL.md) | Fonts and fallback stacks, font loading, modular type scales and hierarchies, fluid sizes, readability and type-aware contrast audits, text segmentation, the font picker. |
|
|
30
32
|
|
|
33
|
+
## Workflow skills
|
|
34
|
+
|
|
35
|
+
A workflow skill covers one job from start to finish: what to confirm first, the steps in
|
|
36
|
+
order with a check after each, a decision table, recipes and the job's pitfalls. It is built on
|
|
37
|
+
its reference skill's tools (load both), and its description is written about the job, so a
|
|
38
|
+
request like "clean up this export" finds it.
|
|
39
|
+
|
|
40
|
+
| Skill | Editor | Covers |
|
|
41
|
+
| --- | --- | --- |
|
|
42
|
+
| [`pixerate-image-selection-masking`](pixerate-image-selection-masking/SKILL.md) | Image (`pixerate-image-editor`) | Cutouts, background and sky replacement, local adjustments through masks; choosing a selection method, checking and refining edges, and outputting an editable mask. |
|
|
43
|
+
| [`pixerate-spreadsheet-clean-import`](pixerate-spreadsheet-clean-import/SKILL.md) | Spreadsheet (`pixerate-spreadsheet`) | Imported or pasted data made tidy: structure, headers, types (currency, comma decimals, day-first dates, leading zeros, missing values), text, duplicates and validation. |
|
|
44
|
+
| [`pixerate-talking-head-edit`](pixerate-talking-head-edit/SKILL.md) | Video (`pixerate-video-editor`) | Talking-head cleanup by transcript: fillers, long pauses and stutters found in one pass and cut as one plan reviewed before it applies. Owns the dialogue tools. |
|
|
45
|
+
| [`pixerate-bgm-alignment`](pixerate-bgm-alignment/SKILL.md) | Video (`pixerate-video-editor`) | Background music: beats and cuts on the beat, ducking under speech, loudness in LUFS. Owns the music tools. |
|
|
46
|
+
|
|
31
47
|
## Using them
|
|
32
48
|
|
|
33
49
|
Copy or symlink the directories into your agent's skills folder, for example for Claude Code:
|
|
@@ -75,12 +91,27 @@ same change.
|
|
|
75
91
|
`create*ToolProvider` / `create*Commands` factory is not mapped to a skill. A new domain
|
|
76
92
|
gets a new skill directory, an entry in `expectedBySkill()` and `FACTORIES`, and a row in
|
|
77
93
|
the table above.
|
|
78
|
-
- **Workflow skills.** A skill that covers one job end to end on another skill's tools
|
|
79
|
-
|
|
80
|
-
Its frontmatter adds `metadata` with
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
reference skill
|
|
94
|
+
- **Workflow skills.** A skill that covers one job end to end on another skill's tools has no
|
|
95
|
+
tool table (unless the job has its own provider, as the two video ones do). Start from
|
|
96
|
+
[`WORKFLOW_TEMPLATE.md`](WORKFLOW_TEMPLATE.md). Its frontmatter adds `metadata` with
|
|
97
|
+
`kind: workflow` and `reference: <skill>`; it lists the tools it uses as bullets under
|
|
98
|
+
`## Tools used`; and it is mapped in `WORKFLOWS` in the test (and to `[]` in
|
|
99
|
+
`expectedBySkill()`). The test checks that every tool it uses exists in the reference skill,
|
|
100
|
+
that every backticked tool or command name in its prose is real, that the reference skill
|
|
101
|
+
names it under `## Workflow skills`, that it stays under 250 body lines (aim for 200) and that
|
|
102
|
+
it ships evals. Add its row to the workflow table above.
|
|
103
|
+
- **Evals.** Each workflow skill ships at least three scenarios in `evals/*.json`, run by
|
|
104
|
+
`packages/core/tests/agent-skill-evals.test.ts`. An eval has a `prompt` (the task, in the
|
|
105
|
+
user's words), a `fixture` (the starting document: `{ "editor": "spreadsheet", "rows" }` or
|
|
106
|
+
`{ "editor": "image", "width", "height", "background", "shapes" }`), `checks` (a read-only
|
|
107
|
+
tool, its `args`, what its result must match in `expect`, and `why`) and a `solution` (the
|
|
108
|
+
tool calls an expert would make). The test proves each eval: some check fails on the fixture,
|
|
109
|
+
and all pass after the solution. A model harness runs the same file with the model in place
|
|
110
|
+
of the solution. In `expect`, objects match key by key and arrays element by element, with
|
|
111
|
+
operators for ranges and lists (`$min`, `$max`, `$match`, `$every`, `$some`, `$length`,
|
|
112
|
+
`$unique`, `$distinct`, `$sum`, `$column`, `$nonEmpty`, `$not`, `$in`, `$type`); a check's
|
|
113
|
+
argument can read a value the job created with `{ "$read": "list_layers", "path":
|
|
114
|
+
"layers.-1.id" }`. Checks test the outcome the user asked for, not one way of getting it.
|
|
84
115
|
- **Workflows and practice.** The test cannot check advice. When a feature changes the best
|
|
85
116
|
way to do something (a new batch tool, a new check, a better default), update the working
|
|
86
117
|
method and best practices too.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: pixerate-<editor>-<job>
|
|
3
|
+
description: The <job> workflow in the Pixerate <editor> - <what it produces, in the user's words, and the main steps>. Use when asked to <trigger phrases: the verbs and nouns people use for this job>, or when <a tool or two that signal the job> are available. Load with pixerate-<editor>, which lists every tool.
|
|
4
|
+
metadata:
|
|
5
|
+
kind: workflow
|
|
6
|
+
reference: pixerate-<editor>
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# <The job, as a short title>
|
|
10
|
+
|
|
11
|
+
<Two to four sentences: the model of the job a person needs before starting. What the
|
|
12
|
+
document is made of here, and the one or two facts that make this job go right or wrong.>
|
|
13
|
+
|
|
14
|
+
This skill is the method. `pixerate-<editor>` has the full tool reference.
|
|
15
|
+
|
|
16
|
+
## Goal and quality bar
|
|
17
|
+
|
|
18
|
+
- <What the finished result is, stated so it can be checked: numbers, ranges, absences.>
|
|
19
|
+
- <What must stay editable, reversible or untouched.>
|
|
20
|
+
|
|
21
|
+
## Inputs to confirm
|
|
22
|
+
|
|
23
|
+
Ask when you cannot tell from the document; say what you assumed when you can.
|
|
24
|
+
|
|
25
|
+
- **<Decision>:** <the options, and the default when the user does not say>.
|
|
26
|
+
|
|
27
|
+
## Working method
|
|
28
|
+
|
|
29
|
+
1. **<Step>.** <What to do, with which tool and which arguments matter (units, defaults).>
|
|
30
|
+
**Check:** <a read tool and what its result must show before going on>.
|
|
31
|
+
2. **<Step>.** ...
|
|
32
|
+
**Check:** ...
|
|
33
|
+
|
|
34
|
+
## Decision table
|
|
35
|
+
|
|
36
|
+
| Situation | Approach |
|
|
37
|
+
| --- | --- |
|
|
38
|
+
| <case> | <method, tool, setting> |
|
|
39
|
+
|
|
40
|
+
## Recipes
|
|
41
|
+
|
|
42
|
+
**<Common request>.** <The tool calls in order, with arguments, as short prose.>
|
|
43
|
+
|
|
44
|
+
## Pitfalls
|
|
45
|
+
|
|
46
|
+
- **<Failure>.** <Why it happens and how to avoid or detect it.>
|
|
47
|
+
|
|
48
|
+
## Tools used
|
|
49
|
+
|
|
50
|
+
- `<tool_name>`, `<tool_name>`: <one-line purpose in this job>
|
|
@@ -33,6 +33,13 @@ Also an optional selection and crop/rotate/flip. See `pixerate-agent-tools` for
|
|
|
33
33
|
- **Coordinates** for selections are natural pixels of the source image (before crop, rotate
|
|
34
34
|
or flip). Read `get_selection` bounds or the image size, never screen pixels.
|
|
35
35
|
|
|
36
|
+
## Workflow skills
|
|
37
|
+
|
|
38
|
+
For these jobs, load the workflow skill too: it has the method, the checks between steps and
|
|
39
|
+
the pitfalls, and uses the tools below.
|
|
40
|
+
|
|
41
|
+
- `pixerate-image-selection-masking`: cutouts, background and sky replacement, local adjustments through masks.
|
|
42
|
+
|
|
36
43
|
## Working method
|
|
37
44
|
|
|
38
45
|
1. **Read.** `get_adjustments` for the current values, process version and layers;
|
|
@@ -104,17 +111,8 @@ Keep outlines as paths (`make_work_path`, `add_path`) and edit their anchors: se
|
|
|
104
111
|
|
|
105
112
|
### Painting
|
|
106
113
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
2. **Retouch through masks**, not the eraser (see `pixerate-image-selection-masking`).
|
|
110
|
-
3. **One stroke is one call and one undo step.** Send the path's points in order (a few points
|
|
111
|
-
per brush diameter is enough; dabs are spaced by `brush.spacing`). Use `opacity` for the
|
|
112
|
-
overall strength and `flow` for gradual build-up within the stroke.
|
|
113
|
-
4. **Limit paint with a selection**, or lock transparency (`set_layer_properties`
|
|
114
|
-
`locks.transparency`) to paint only where the layer already has pixels.
|
|
115
|
-
5. **Mixing paint, the Mixer Brush and gradient paint**: `mix_colors` mixes named paints like
|
|
116
|
-
pigment; `paint_stroke` `tool: "mixer"` picks up and smears the layer's colours; gradient paint
|
|
117
|
-
changes colour along a stroke. Read [references/painting.md](references/painting.md) first.
|
|
114
|
+
Paint on its own layer (`add_pixel_layer`), one stroke per call and one undo step, limited by a
|
|
115
|
+
selection or locked transparency. Read [references/painting.md](references/painting.md) first.
|
|
118
116
|
|
|
119
117
|
### Colour management
|
|
120
118
|
|
|
@@ -207,6 +205,11 @@ in the source frame at the document's bit depth, colour mode and profile. Read
|
|
|
207
205
|
| `get_selection` | Read the active selection: `empty`, `bounds` (natural pixels), `area` in pixels, `percent` of the image and a description. Check it after every select or modify step and before masking: a wand that covered 90% of the image is the wrong selection. |
|
|
208
206
|
| `inspect_mask_edge` | Measure an edge without looking: the selection (default) or a layer's `layer-mask` / `vector-mask` (`layerId`). Returns `haloScore`, `edgeAlignment`, `jaggedness`, `softFraction`, `holes`, `islands`, `worst` regions for `capture_view` and `issues` with fixes. Inspect, fix, inspect again (see `pixerate-image-selection-masking`). |
|
|
209
207
|
| `compare_selections` | What changed between two selections: `a` (default the one before the last undo step) and `b` (default current), each `selection`, `history` (`steps`), `channel` or a layer's `layer-mask` / `vector-mask`. Returns `added` / `removed` (area, largest pieces with bounds), `iou` and `identical`; `show` tints the change on the canvas. Check every modify or refine step. |
|
|
208
|
+
| `get_selection_recipe` | Read-only. Live selections: the steps that made the selection (marquees, polygons, magic wand, Color Range, modify, invert, refine) with indexes; `live: false` when frozen (made by a command steps cannot describe, e.g. `quick_select`). |
|
|
209
|
+
| `edit_selection_step` | Change a recipe step (`index`, `changes`: e.g. `options.tolerance`, `rect`, `radius`, `mode`; a new `op` replaces it) and replay. One undo step. Returns the selection and recipe. |
|
|
210
|
+
| `insert_selection_step` | Insert a step at `index` (length appends), e.g. a subtract rect before a refine, and replay. One undo step. |
|
|
211
|
+
| `remove_selection_step` | Remove a recipe step and replay (not the only one: deselect instead). One undo step. |
|
|
212
|
+
| `freeze_selection` | Keep the selection as it is and drop its recipe. One undo step. |
|
|
210
213
|
| `list_channels` | Read the alpha channels (saved selections): id, name, bounds, percent, overlay and visibility, and whether Quick Mask mode is on. |
|
|
211
214
|
| `select_subject` | Select › Subject. With a host segmentation model, `target` describes what to select ("the dog", "the sky") and the model's outlines are selected (`maxSegments` 1 for one). Without one, the subject that stands out most is found classically; `target` can't choose it (`method: "saliency"`, `note`). Combined by `mode`. Returns the selection, `found` (label, bounds) and `method`; nothing found leaves the selection unchanged. |
|
|
212
215
|
| `list_layers` | Read the layer stack bottom to top as a tree: id, type (`pixel`, `adjustment`, `group`, `shape`, `annotation`, `smart`; smart objects add their `contents`, `smartFilters` and `filterMask`; shapes a `shape` summary of component ops and kinds and paint types, and `annotation` when migrated from one), name, document `bounds` of pixel layers, visibility, opacity, `fillOpacity`, blend, `clipped`, locks, a `mask` summary, an `effects` summary (master switch, scale, effect ids and kinds), pixel-layer `filters` (the Background's `develop` filter is the base adjustments), adjustment `kind` and `params`, group `children`, and which layer is `active`. Call it to understand a document with more than the Background before changing it; adjustment layer ids work with every adjustment-layer tool. |
|
|
@@ -1,4 +1,20 @@
|
|
|
1
|
-
# Painting: mixing paint, the Mixer Brush and gradient paint
|
|
1
|
+
# Painting: strokes, mixing paint, the Mixer Brush and gradient paint
|
|
2
|
+
|
|
3
|
+
## Strokes
|
|
4
|
+
|
|
5
|
+
1. **Paint on its own layer** (`add_pixel_layer`), not on the Background (active on a new image, so
|
|
6
|
+
strokes without `layerId` land there) unless asked: a layer can be faded, masked or deleted.
|
|
7
|
+
2. **Retouch through masks**, not the eraser (see `pixerate-image-selection-masking`).
|
|
8
|
+
3. **One stroke is one call and one undo step.** Send the path's points in order (a few points
|
|
9
|
+
per brush diameter is enough; dabs are spaced by `brush.spacing`). Use `opacity` for the
|
|
10
|
+
overall strength and `flow` for gradual build-up within the stroke.
|
|
11
|
+
4. **Limit paint with a selection**, or lock transparency (`set_layer_properties`
|
|
12
|
+
`locks.transparency`) to paint only where the layer already has pixels.
|
|
13
|
+
5. **Mixing paint, the Mixer Brush and gradient paint**: `mix_colors` mixes named paints like
|
|
14
|
+
pigment; `paint_stroke` `tool: "mixer"` picks up and smears the layer's colours; gradient paint
|
|
15
|
+
changes colour along a stroke. See below.
|
|
16
|
+
|
|
17
|
+
## Mixing paint, the Mixer Brush and gradient paint
|
|
2
18
|
|
|
3
19
|
Read this before `mix_colors`, a `paint_stroke` with `tool: "mixer"`, or gradient paint
|
|
4
20
|
(`set_paint` / `paint_stroke` `paint`).
|
|
@@ -40,9 +40,9 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
|
|
|
40
40
|
**Check:** `get_selection` → `empty: false` and a plausible `percent` (for a person in a
|
|
41
41
|
portrait, typically 15–50 %). A wand or range above about 80 % has flooded: undo, then lower
|
|
42
42
|
`tolerance` / `fuzziness`.
|
|
43
|
-
2. **Build it in steps** with `mode`:
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
2. **Build it in steps** with `mode`: `replace`, then `add` what was missed, `subtract` what
|
|
44
|
+
leaked, `intersect` to limit it. The steps are kept (`get_selection_recipe`): fix one with
|
|
45
|
+
`edit_selection_step` (a wand's tolerance, a box) rather than starting over.
|
|
46
46
|
**Check:** `bounds` hug the subject. A bounds as large as the image means a leak. Smart tools
|
|
47
47
|
return `uncertainRegions` (edge with no image edge under it): look there, refine with `regions`.
|
|
48
48
|
3. **Measure the edge, then look.** `inspect_mask_edge` scores it: `haloScore` ≥ 0.35 is old
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Make just the red ball black and white, and keep it adjustable so I can tweak it later. The sign in the corner should stay red.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "image",
|
|
5
|
+
"width": 120,
|
|
6
|
+
"height": 80,
|
|
7
|
+
"background": [40, 90, 200],
|
|
8
|
+
"shapes": [
|
|
9
|
+
{ "shape": "ellipse", "x": 40, "y": 20, "width": 40, "height": 40, "color": [210, 40, 40] },
|
|
10
|
+
{ "shape": "rect", "x": 100, "y": 6, "width": 12, "height": 8, "color": [210, 40, 40] }
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"checks": [
|
|
14
|
+
{ "why": "a Black & White adjustment layer, masked to the ball and not the sign", "tool": "list_layers", "expect": { "layers": { "$some": { "type": "adjustment", "kind": "blackWhite", "mask": { "kind": "raster", "defaultValue": 0, "paintedBounds": { "x": { "$min": 38, "$max": 41 }, "y": { "$min": 18, "$max": 21 }, "width": { "$min": 39, "$max": 44 }, "height": { "$min": 39, "$max": 44 } } } } } } },
|
|
15
|
+
{ "why": "the photo's own pixels are not changed", "tool": "list_layers", "expect": { "layers": { "$some": { "id": "background", "type": "pixel", "mask": null } } } }
|
|
16
|
+
],
|
|
17
|
+
"solution": [
|
|
18
|
+
{ "tool": "magic_wand", "args": { "x": 60, "y": 40, "tolerance": 20, "contiguous": true } },
|
|
19
|
+
{ "tool": "get_selection" },
|
|
20
|
+
{ "tool": "add_adjustment_layer", "args": { "kind": "blackWhite" } }
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Hide the blue background so only the red ball shows. Don't delete any pixels; I may want the background back later.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "image",
|
|
5
|
+
"width": 120,
|
|
6
|
+
"height": 80,
|
|
7
|
+
"background": [40, 90, 200],
|
|
8
|
+
"shapes": [
|
|
9
|
+
{ "shape": "ellipse", "x": 40, "y": 20, "width": 40, "height": 40, "color": [210, 40, 40] },
|
|
10
|
+
{ "shape": "rect", "x": 100, "y": 6, "width": 12, "height": 8, "color": [210, 40, 40] }
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"checks": [
|
|
14
|
+
{ "why": "the top layer has a layer mask that reveals only the ball", "tool": "list_layers", "expect": { "layers": { "$some": { "type": "pixel", "mask": { "kind": "raster", "enabled": true, "defaultValue": 0, "paintedBounds": { "x": { "$min": 38, "$max": 41 }, "y": { "$min": 18, "$max": 21 }, "width": { "$min": 39, "$max": 44 }, "height": { "$min": 39, "$max": 44 } } } } } } },
|
|
15
|
+
{ "why": "the mask edge is clean", "tool": "inspect_mask_edge", "args": { "target": "layer-mask", "layerId": { "$read": "list_layers", "path": "layers.-1.id" } }, "expect": { "issues": [], "haloScore": { "$max": 0.35 } } }
|
|
16
|
+
],
|
|
17
|
+
"solution": [
|
|
18
|
+
{ "tool": "list_layers" },
|
|
19
|
+
{ "tool": "magic_wand", "args": { "x": 60, "y": 40, "tolerance": 20, "contiguous": true } },
|
|
20
|
+
{ "tool": "inspect_mask_edge" },
|
|
21
|
+
{ "tool": "add_layer_mask", "args": { "layerId": "background", "from": "selection" } },
|
|
22
|
+
{ "tool": "inspect_mask_edge", "args": { "target": "layer-mask", "layerId": "background" } }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"prompt": "Select the red ball in the middle of the photo, not the red sign in the top-right corner. Just the selection for now; I'll decide what to do with it.",
|
|
3
|
+
"fixture": {
|
|
4
|
+
"editor": "image",
|
|
5
|
+
"width": 120,
|
|
6
|
+
"height": 80,
|
|
7
|
+
"background": [40, 90, 200],
|
|
8
|
+
"shapes": [
|
|
9
|
+
{ "shape": "ellipse", "x": 40, "y": 20, "width": 40, "height": 40, "color": [210, 40, 40] },
|
|
10
|
+
{ "shape": "rect", "x": 100, "y": 6, "width": 12, "height": 8, "color": [210, 40, 40] }
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"checks": [
|
|
14
|
+
{ "why": "the selection hugs the ball and leaves the sign out", "tool": "get_selection", "expect": { "empty": false, "bounds": { "x": { "$min": 38, "$max": 41 }, "y": { "$min": 18, "$max": 21 }, "width": { "$min": 39, "$max": 44 }, "height": { "$min": 39, "$max": 44 } }, "percent": { "$min": 12, "$max": 14.5 } } },
|
|
15
|
+
{ "why": "a clean edge: no halo, holes or islands", "tool": "inspect_mask_edge", "expect": { "issues": [], "holes": { "count": 0 }, "islands": { "count": 0 } } }
|
|
16
|
+
],
|
|
17
|
+
"solution": [
|
|
18
|
+
{ "tool": "magic_wand", "args": { "x": 60, "y": 40, "tolerance": 20, "contiguous": true } },
|
|
19
|
+
{ "tool": "get_selection" },
|
|
20
|
+
{ "tool": "inspect_mask_edge" }
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -15,6 +15,13 @@ leading `'` forces text.
|
|
|
15
15
|
|
|
16
16
|
Errors, namespacing and `run_commands` batching are in the `pixerate-agent-tools` skill.
|
|
17
17
|
|
|
18
|
+
## Workflow skills
|
|
19
|
+
|
|
20
|
+
For these jobs, load the workflow skill too: it has the method, the checks between steps and
|
|
21
|
+
the pitfalls, and uses the tools below.
|
|
22
|
+
|
|
23
|
+
- `pixerate-spreadsheet-clean-import`: tidying imported or pasted data: structure, headers, types, dates, duplicates.
|
|
24
|
+
|
|
18
25
|
## Working method
|
|
19
26
|
|
|
20
27
|
1. **Read.** `get_range` on the area you will touch (and its header row). The result gives
|