@pixerate/editor 0.200.0 → 0.202.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.
- package/dist/agent/index.cjs +2 -2
- package/dist/agent/index.js +1 -1
- package/dist/{chunk-GLQQ2VIL.cjs → chunk-53GOGBTM.cjs} +23 -1
- package/dist/{chunk-3TNA7JKI.js → chunk-ZPJEH456.js} +22 -0
- package/dist/commands/builtins.d.cts +6 -0
- package/dist/commands/builtins.d.ts +6 -0
- package/dist/commands/index.cjs +4 -2
- package/dist/commands/index.js +4 -2
- package/dist/image-editor/controller.d.cts +18 -0
- package/dist/image-editor/controller.d.ts +18 -0
- package/dist/image-editor/index.cjs +180 -19
- package/dist/image-editor/index.js +179 -18
- package/dist/image-editor/selection/segment/index.d.cts +1 -0
- package/dist/image-editor/selection/segment/index.d.ts +1 -0
- package/dist/image-editor/selection/segment/sky.d.cts +21 -0
- package/dist/image-editor/selection/segment/sky.d.ts +21 -0
- package/dist/paint-editor/guide-tools.d.cts +2 -2
- package/dist/paint-editor/guide-tools.d.ts +2 -2
- package/dist/paint-editor/hosted.d.cts +34 -0
- package/dist/paint-editor/hosted.d.ts +34 -0
- package/dist/paint-editor/index.cjs +101 -10
- package/dist/paint-editor/index.d.cts +1 -0
- package/dist/paint-editor/index.d.ts +1 -0
- package/dist/paint-editor/index.js +96 -5
- package/dist/paint-editor/sticker-tools.d.cts +2 -2
- package/dist/paint-editor/sticker-tools.d.ts +2 -2
- package/dist/paint-editor/thick-tools.d.cts +2 -2
- package/dist/paint-editor/thick-tools.d.ts +2 -2
- package/dist/paint-editor/tools.d.cts +19 -2
- package/dist/paint-editor/tools.d.ts +19 -2
- package/dist/paint-editor/wet-tools.d.cts +2 -2
- package/dist/paint-editor/wet-tools.d.ts +2 -2
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +1 -0
- package/skills/pixerate-image-selection-masking/SKILL.md +4 -2
- package/skills/pixerate-paint-editor/SKILL.md +9 -0
|
@@ -6213,6 +6213,49 @@ function withPaintPlayback(provider, options) {
|
|
|
6213
6213
|
};
|
|
6214
6214
|
}
|
|
6215
6215
|
|
|
6216
|
+
// src/paint-editor/hosted.ts
|
|
6217
|
+
function shiftAt(value, keys, dx, dy) {
|
|
6218
|
+
if (value === null || typeof value !== "object") return value;
|
|
6219
|
+
if (keys.length === 0) {
|
|
6220
|
+
if (Array.isArray(value)) return value;
|
|
6221
|
+
const o = { ...value };
|
|
6222
|
+
if (typeof o.x === "number") o.x += dx;
|
|
6223
|
+
if (typeof o.y === "number") o.y += dy;
|
|
6224
|
+
return o;
|
|
6225
|
+
}
|
|
6226
|
+
const [head, ...rest] = keys;
|
|
6227
|
+
const each = head.endsWith("[]");
|
|
6228
|
+
const key = each ? head.slice(0, -2) : head;
|
|
6229
|
+
const inner = key === "" ? value : value[key];
|
|
6230
|
+
if (inner === void 0) return value;
|
|
6231
|
+
const next = each ? Array.isArray(inner) ? inner.map((v) => shiftAt(v, rest, dx, dy)) : inner : shiftAt(inner, rest, dx, dy);
|
|
6232
|
+
if (key === "") return next;
|
|
6233
|
+
return Array.isArray(value) ? value : { ...value, [key]: next };
|
|
6234
|
+
}
|
|
6235
|
+
var shiftAll = (value, paths, dx, dy) => paths.reduce((v, p) => shiftAt(v, p === "" ? [] : p.split("."), dx, dy), value);
|
|
6236
|
+
var toDocument = (value, paths, origin) => shiftAll(value, paths, -origin.x, -origin.y);
|
|
6237
|
+
var fromDocument = (value, paths, origin) => shiftAll(value, paths, origin.x, origin.y);
|
|
6238
|
+
function hostedText(text) {
|
|
6239
|
+
return text.replace(/\ba document pixel\b/g, "a canvas point").replace(/\bdocument pixels\b/g, "canvas coordinates").replace(/\bDocument ([xy])\b/g, "Canvas $1").replace(/\bdocument ([xy])\b/g, "canvas $1");
|
|
6240
|
+
}
|
|
6241
|
+
function hostedSchema(schema) {
|
|
6242
|
+
if (Array.isArray(schema)) return schema.map(hostedSchema);
|
|
6243
|
+
if (schema === null || typeof schema !== "object") return schema;
|
|
6244
|
+
const out = {};
|
|
6245
|
+
for (const [k, v] of Object.entries(schema)) out[k] = k === "description" && typeof v === "string" ? hostedText(v) : hostedSchema(v);
|
|
6246
|
+
return out;
|
|
6247
|
+
}
|
|
6248
|
+
function lazyController(get) {
|
|
6249
|
+
return new Proxy({}, {
|
|
6250
|
+
get(_, key) {
|
|
6251
|
+
const target = get();
|
|
6252
|
+
const value = Reflect.get(target, key, target);
|
|
6253
|
+
return typeof value === "function" ? value.bind(target) : value;
|
|
6254
|
+
},
|
|
6255
|
+
has: (_, key) => Reflect.has(get(), key)
|
|
6256
|
+
});
|
|
6257
|
+
}
|
|
6258
|
+
|
|
6216
6259
|
// src/paint-editor/sticker-tools.ts
|
|
6217
6260
|
var READ = { readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false };
|
|
6218
6261
|
var WRITE = { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false };
|
|
@@ -6226,6 +6269,7 @@ function createPaintStickerTools(c) {
|
|
|
6226
6269
|
description: "Lists a layer\u2019s stickers bottom first: id, stamp, x, y (centre, document pixels), size (diameter px), rotation (degrees clockwise), color, opacity. Empty for a layer without stickers. Changes nothing.",
|
|
6227
6270
|
inputSchema: { type: "object", properties: { layerId: LAYER_ID }, additionalProperties: false },
|
|
6228
6271
|
annotations: { title: "List stickers", ...READ },
|
|
6272
|
+
coordinates: { out: ["stickers[]"] },
|
|
6229
6273
|
handler: ({ layerId }) => ({ layerId: layerId ?? c.document.activeLayerId, stickers: c.listStickers(layerId) })
|
|
6230
6274
|
},
|
|
6231
6275
|
{
|
|
@@ -6249,6 +6293,7 @@ function createPaintStickerTools(c) {
|
|
|
6249
6293
|
additionalProperties: false
|
|
6250
6294
|
},
|
|
6251
6295
|
annotations: { title: "Change a sticker", ...WRITE },
|
|
6296
|
+
coordinates: { in: [""], out: [""] },
|
|
6252
6297
|
handler: ({ layerId, id, ...update }) => c.updateSticker(layerId, id, update)
|
|
6253
6298
|
},
|
|
6254
6299
|
{
|
|
@@ -6304,6 +6349,7 @@ var RECT = {
|
|
|
6304
6349
|
required: ["x", "y", "width", "height"],
|
|
6305
6350
|
additionalProperties: false
|
|
6306
6351
|
};
|
|
6352
|
+
var STENCIL_POSITIONS = ["from", "to", "center", "rect", "points[]"];
|
|
6307
6353
|
var PNG = { type: "string", minLength: 1, description: "The image as a PNG, base64 or a data:image/png URL." };
|
|
6308
6354
|
var tracingInfo = (t) => t ? { width: t.width, height: t.height, rect: t.rect, opacity: t.opacity, visible: t.visible, sampleColors: t.sampleColors } : null;
|
|
6309
6355
|
var refInfo = ({ src: _s, ...r }) => r;
|
|
@@ -6332,6 +6378,7 @@ function createPaintGuideTools(c) {
|
|
|
6332
6378
|
description: "Reads the painting guides: symmetry (mode, centre, rotation, segments, mirror, and how many copies each stroke paints), the perspective guide (vanishing points, snap), the tracing image (size, placement, opacity, visibility, colour sampling), the reference images and the stencils (id, kind, mode, geometry).",
|
|
6333
6379
|
inputSchema: EMPTY,
|
|
6334
6380
|
annotations: { title: "Get the painting guides", ...READ2 },
|
|
6381
|
+
coordinates: { out: ["symmetry.center", "perspective.points[]", "tracing.rect", ...STENCIL_POSITIONS.map((p) => `stencils[].${p}`)] },
|
|
6335
6382
|
handler: () => {
|
|
6336
6383
|
const g = guides();
|
|
6337
6384
|
return { symmetry: { ...g.symmetry, copies: symmetryCopies(g.symmetry).length }, perspective: g.perspective, tracing: tracingInfo(g.tracing), references: g.references.map(refInfo), stencils: g.stencils };
|
|
@@ -6353,6 +6400,7 @@ function createPaintGuideTools(c) {
|
|
|
6353
6400
|
additionalProperties: false
|
|
6354
6401
|
},
|
|
6355
6402
|
annotations: { title: "Set mirror or kaleidoscope painting", ...SET },
|
|
6403
|
+
coordinates: { in: ["center"], out: ["center"] },
|
|
6356
6404
|
handler: (a) => {
|
|
6357
6405
|
const s = c.setSymmetry(a);
|
|
6358
6406
|
return { ...s, copies: symmetryCopies(s).length };
|
|
@@ -6372,6 +6420,7 @@ function createPaintGuideTools(c) {
|
|
|
6372
6420
|
additionalProperties: false
|
|
6373
6421
|
},
|
|
6374
6422
|
annotations: { title: "Set the perspective guide", ...SET },
|
|
6423
|
+
coordinates: { in: ["points[]"], out: ["points[]"] },
|
|
6375
6424
|
handler: (a) => c.setPerspective(a)
|
|
6376
6425
|
},
|
|
6377
6426
|
{
|
|
@@ -6390,6 +6439,7 @@ function createPaintGuideTools(c) {
|
|
|
6390
6439
|
additionalProperties: false
|
|
6391
6440
|
},
|
|
6392
6441
|
annotations: { title: "Set a tracing image", ...WRITE2 },
|
|
6442
|
+
coordinates: { in: ["rect"], out: ["rect"] },
|
|
6393
6443
|
handler: (a) => tracingInfo(c.setTracingImage(a))
|
|
6394
6444
|
},
|
|
6395
6445
|
{
|
|
@@ -6407,6 +6457,7 @@ function createPaintGuideTools(c) {
|
|
|
6407
6457
|
additionalProperties: false
|
|
6408
6458
|
},
|
|
6409
6459
|
annotations: { title: "Change the tracing image", ...SET },
|
|
6460
|
+
coordinates: { in: ["rect"], out: ["rect"] },
|
|
6410
6461
|
handler: (a) => tracingInfo(c.updateTracing(a))
|
|
6411
6462
|
},
|
|
6412
6463
|
{
|
|
@@ -6700,6 +6751,7 @@ function createPaintGuideTools(c) {
|
|
|
6700
6751
|
description: "Lays a stencil on the canvas: a ruler (straight edge to draw along) or an arc (circle or part of one) that pointer and agent strokes starting near it follow exactly, or a line (straight masking edge) or a shape (rect, ellipse or polygon template) that paint lands only inside of (outside with invert). Editor state saved with the document, not an undo step; replays keep the mask they were painted through. Returns the stencil with its id.",
|
|
6701
6752
|
inputSchema: { type: "object", properties: { kind: { type: "string", enum: ["ruler", "arc", "line", "shape"], description: "ruler, arc, line or shape." }, ...STENCIL_GEOMETRY }, required: ["kind"], additionalProperties: false },
|
|
6702
6753
|
annotations: { title: "Lay a stencil", ...WRITE2 },
|
|
6754
|
+
coordinates: { in: STENCIL_POSITIONS, out: STENCIL_POSITIONS },
|
|
6703
6755
|
handler: (a) => c.addStencil(a)
|
|
6704
6756
|
},
|
|
6705
6757
|
{
|
|
@@ -6708,6 +6760,7 @@ function createPaintGuideTools(c) {
|
|
|
6708
6760
|
description: "Moves, reshapes, switches (guide or mask, invert) or hides (enabled false) a stencil by id from get_guides. Returns the stencil.",
|
|
6709
6761
|
inputSchema: { type: "object", properties: { id: { type: "string", description: "Stencil id from get_guides." }, ...STENCIL_GEOMETRY }, required: ["id"], additionalProperties: false },
|
|
6710
6762
|
annotations: { title: "Change a stencil", ...SET },
|
|
6763
|
+
coordinates: { in: STENCIL_POSITIONS, out: STENCIL_POSITIONS },
|
|
6711
6764
|
handler: ({ id, ...update }) => c.updateStencil(id, update)
|
|
6712
6765
|
},
|
|
6713
6766
|
{
|
|
@@ -6760,6 +6813,7 @@ function createPaintWetTools(c) {
|
|
|
6760
6813
|
description: "Reads the wet media: whether any paint is still wet, the simulation clock (steps), whether time is paused, its speed (steps per second), the canvas tilt, the kernel running it (typescript, wasm, wasm-threads with the thread count, and a message when it runs slower than it could, e.g. without cross-origin isolation), and each wet layer: wet cells, where the water is (wetBounds), live tiles, its settings and flow map. Also engine: Engine B, the WebGPU accelerator (mode auto/off, state, reason, adapter, minTiles, activeTiles). Call it after wet strokes to see what is still wet.",
|
|
6761
6814
|
inputSchema: EMPTY2,
|
|
6762
6815
|
annotations: { title: "Get the wet paint", ...READ3 },
|
|
6816
|
+
coordinates: { out: ["layers[].wetBounds", "layers[].flow.center"] },
|
|
6763
6817
|
handler: () => c.wetState()
|
|
6764
6818
|
},
|
|
6765
6819
|
{
|
|
@@ -6786,6 +6840,7 @@ function createPaintWetTools(c) {
|
|
|
6786
6840
|
description: "Dries wet paint at once: everything settles where it is and the water goes, as a hair dryer at full blast would (the same deterministic bake that saving uses). On one layer or every wet layer, optionally only inside rect. Dried washes stay put: later washes glaze over them instead of mixing in. One undo step. Returns dried (each layer id with the rectangle that dried) and stillWet.",
|
|
6787
6841
|
inputSchema: { type: "object", properties: { layerId: { type: "string", description: "Only this layer (default every wet layer)." }, rect: RECT2 }, additionalProperties: false },
|
|
6788
6842
|
annotations: { title: "Dry the wet paint", ...WRITE3 },
|
|
6843
|
+
coordinates: { in: ["rect"], out: ["dried[].bounds"] },
|
|
6789
6844
|
handler: async ({ layerId, rect }) => {
|
|
6790
6845
|
await c.wetIdle();
|
|
6791
6846
|
return c.dryPaint({ layerId, rect });
|
|
@@ -6801,6 +6856,7 @@ function createPaintWetTools(c) {
|
|
|
6801
6856
|
additionalProperties: false
|
|
6802
6857
|
},
|
|
6803
6858
|
annotations: { title: "Pre-wet the paper", ...WRITE3 },
|
|
6859
|
+
coordinates: { in: ["rect"], out: ["rect"] },
|
|
6804
6860
|
handler: async ({ layerId, rect, amount }) => {
|
|
6805
6861
|
await c.wetIdle();
|
|
6806
6862
|
return c.wetPaper({ layerId, rect, amount });
|
|
@@ -6843,6 +6899,7 @@ function createPaintWetTools(c) {
|
|
|
6843
6899
|
additionalProperties: false
|
|
6844
6900
|
},
|
|
6845
6901
|
annotations: { title: "Set a flow map", ...SET2 },
|
|
6902
|
+
coordinates: { in: ["center"], out: ["flow.center"] },
|
|
6846
6903
|
handler: async ({ layerId, ...flow }) => {
|
|
6847
6904
|
await c.wetIdle();
|
|
6848
6905
|
return { layerId: layerId ?? c.document.activeLayerId, flow: c.setFlowMap(layerId, flow), stillWet: stillWet() };
|
|
@@ -6943,6 +7000,7 @@ function createPaintThickTools(c) {
|
|
|
6943
7000
|
description: "Reads a layer\u2019s thick paint: where the relief is (bounds, layer pixels), its highest point (0..255) and the lighting. relief is null when the layer is flat. Changes nothing.",
|
|
6944
7001
|
inputSchema: { type: "object", properties: { layerId: LAYER_ID3 }, additionalProperties: false },
|
|
6945
7002
|
annotations: { title: "Read a layer\u2019s relief", ...READ4 },
|
|
7003
|
+
coordinates: { out: ["relief.bounds"] },
|
|
6946
7004
|
handler: ({ layerId }) => ({ relief: c.reliefOf(layerId), lighting: c.getLighting() })
|
|
6947
7005
|
},
|
|
6948
7006
|
{
|
|
@@ -7039,7 +7097,7 @@ var TIP = {
|
|
|
7039
7097
|
type: "object",
|
|
7040
7098
|
description: "The tip (the shared brush engine). Missing fields keep their current values.",
|
|
7041
7099
|
properties: {
|
|
7042
|
-
size: { type: "number", minimum: 1, maximum: MAX_BRUSH_SIZE, description: "Diameter in
|
|
7100
|
+
size: { type: "number", minimum: 1, maximum: MAX_BRUSH_SIZE, description: "Diameter in pixels." },
|
|
7043
7101
|
hardness: { type: "number", minimum: 0, maximum: 1, description: "Edge hardness, 0 (soft) .. 1 (hard)." },
|
|
7044
7102
|
spacing: { type: "number", minimum: 0.01, maximum: 10, description: "Dab spacing as a fraction of the diameter (0.05 is smooth, 1 is dotted)." },
|
|
7045
7103
|
angle: { type: "number", minimum: -360, maximum: 360, description: "Tip angle in degrees, counter-clockwise; with rotation stroke or barrel it is added to the stroke direction or pen rotation." },
|
|
@@ -7289,7 +7347,7 @@ function brushSummary(b) {
|
|
|
7289
7347
|
};
|
|
7290
7348
|
}
|
|
7291
7349
|
function createPaintEditorToolProvider(controller, options = {}) {
|
|
7292
|
-
const c = controller;
|
|
7350
|
+
const c = typeof controller === "function" ? lazyController(controller) : controller;
|
|
7293
7351
|
const tools = [];
|
|
7294
7352
|
const add = (t) => tools.push(t);
|
|
7295
7353
|
const act = async (command, direct) => {
|
|
@@ -7301,9 +7359,10 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7301
7359
|
add({
|
|
7302
7360
|
name: "get_canvas_info",
|
|
7303
7361
|
title: "Get the canvas",
|
|
7304
|
-
description: "Reads the canvas: size in pixels and dpi, its origin (where (0, 0) is relative to the canvas it was extended from), the paper (preset, colour, grain depth), the active layer, the active brush id, the paint colour, the view (zoom, rotation, mirror), the hand, how many strokes are recorded, the undo / redo labels, and the blend modes. Call it first.",
|
|
7362
|
+
description: "Reads the canvas: size in pixels and dpi, its origin (where (0, 0) is relative to the canvas it was extended from), bounds (the rectangle the painting covers, in the coordinates every other tool takes), the paper (preset, colour, grain depth), the active layer, the active brush id, the paint colour, the view (zoom, rotation, mirror), the hand, how many strokes are recorded, the undo / redo labels, and the blend modes. Call it first.",
|
|
7305
7363
|
inputSchema: EMPTY3,
|
|
7306
7364
|
annotations: { title: "Get the canvas", ...READ6 },
|
|
7365
|
+
coordinates: { out: ["bounds"] },
|
|
7307
7366
|
handler: () => {
|
|
7308
7367
|
const s = c.getState();
|
|
7309
7368
|
const d = s.document;
|
|
@@ -7312,6 +7371,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7312
7371
|
height: d.canvas.height,
|
|
7313
7372
|
dpi: d.canvas.dpi,
|
|
7314
7373
|
origin: { ...d.origin ?? { x: 0, y: 0 } },
|
|
7374
|
+
bounds: { x: 0, y: 0, width: d.canvas.width, height: d.canvas.height },
|
|
7315
7375
|
paper: { preset: d.paper.preset, name: d.paper.name, color: d.paper.color, depth: d.paper.depth },
|
|
7316
7376
|
activeLayerId: d.activeLayerId,
|
|
7317
7377
|
layerCount: c.listLayers().length,
|
|
@@ -7440,6 +7500,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7440
7500
|
description: "Lists the layers top first: id, name, type (pixel or group), visibility, opacity, blend, clipping, locks, whether it is active, the painted bounds in document pixels (null when empty), parent and depth.",
|
|
7441
7501
|
inputSchema: EMPTY3,
|
|
7442
7502
|
annotations: { title: "List layers", ...READ6 },
|
|
7503
|
+
coordinates: { out: ["layers[].bounds"] },
|
|
7443
7504
|
handler: () => ({ layers: c.listLayers(), activeLayerId: c.document.activeLayerId })
|
|
7444
7505
|
});
|
|
7445
7506
|
add({
|
|
@@ -7457,6 +7518,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7457
7518
|
additionalProperties: false
|
|
7458
7519
|
},
|
|
7459
7520
|
annotations: { title: "Add a layer", ...WRITE5 },
|
|
7521
|
+
coordinates: { out: ["bounds"] },
|
|
7460
7522
|
handler: (a) => c.addLayer({ name: a.name, type: a.type, aboveId: a.aboveId, parentId: a.parentId })
|
|
7461
7523
|
});
|
|
7462
7524
|
add({
|
|
@@ -7492,6 +7554,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7492
7554
|
additionalProperties: false
|
|
7493
7555
|
},
|
|
7494
7556
|
annotations: { title: "Change a layer", ...SET4 },
|
|
7557
|
+
coordinates: { out: ["bounds"] },
|
|
7495
7558
|
handler: ({ layerId, ...u }) => c.updateLayer(layerId, u)
|
|
7496
7559
|
});
|
|
7497
7560
|
add({
|
|
@@ -7516,6 +7579,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7516
7579
|
description: "Makes a layer active: strokes, fills and clears without a layerId go to it. Editor state, not an undo step. Returns the layer.",
|
|
7517
7580
|
inputSchema: { type: "object", properties: { layerId: { type: "string", description: "Layer id from list_layers." } }, required: ["layerId"], additionalProperties: false },
|
|
7518
7581
|
annotations: { title: "Choose the active layer", ...SET4 },
|
|
7582
|
+
coordinates: { out: ["bounds"] },
|
|
7519
7583
|
handler: ({ layerId }) => {
|
|
7520
7584
|
c.setActiveLayer(layerId);
|
|
7521
7585
|
return c.describeLayer(c.getLayer(layerId));
|
|
@@ -7535,6 +7599,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7535
7599
|
description: "Copies a layer (a group with its children) directly above it; the copy becomes active. One undo step. Returns the copy.",
|
|
7536
7600
|
inputSchema: { type: "object", properties: { layerId: { type: "string", description: "Layer id from list_layers." } }, required: ["layerId"], additionalProperties: false },
|
|
7537
7601
|
annotations: { title: "Duplicate a layer", ...WRITE5 },
|
|
7602
|
+
coordinates: { out: ["bounds"] },
|
|
7538
7603
|
handler: ({ layerId }) => c.duplicateLayer(layerId)
|
|
7539
7604
|
});
|
|
7540
7605
|
add({
|
|
@@ -7552,6 +7617,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7552
7617
|
additionalProperties: false
|
|
7553
7618
|
},
|
|
7554
7619
|
annotations: { title: "Reorder a layer", ...WRITE5 },
|
|
7620
|
+
coordinates: { out: ["bounds"] },
|
|
7555
7621
|
handler: ({ layerId, index, parentId }) => c.moveLayer(layerId, index, parentId)
|
|
7556
7622
|
});
|
|
7557
7623
|
add({
|
|
@@ -7560,6 +7626,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7560
7626
|
description: "Merges a pixel layer into the pixel layer directly below it, which keeps its name, opacity and blend. One undo step. Returns the merged layer.",
|
|
7561
7627
|
inputSchema: { type: "object", properties: { layerId: { type: "string", description: "The upper layer, from list_layers." } }, required: ["layerId"], additionalProperties: false },
|
|
7562
7628
|
annotations: { title: "Merge a layer down", ...DESTRUCTIVE3 },
|
|
7629
|
+
coordinates: { out: ["bounds"] },
|
|
7563
7630
|
handler: ({ layerId }) => c.mergeDown(layerId)
|
|
7564
7631
|
});
|
|
7565
7632
|
add({
|
|
@@ -7579,6 +7646,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7579
7646
|
additionalProperties: false
|
|
7580
7647
|
},
|
|
7581
7648
|
annotations: { title: "Fill a layer", ...WRITE5 },
|
|
7649
|
+
coordinates: { out: ["bounds"] },
|
|
7582
7650
|
handler: (a) => act({ kind: "fill", fill: a }, () => c.fillLayer(a))
|
|
7583
7651
|
});
|
|
7584
7652
|
add({
|
|
@@ -7587,6 +7655,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7587
7655
|
description: "Removes every pixel of a pixel layer. One undo step. Returns the layer id and the bounds that were cleared (null if it was empty).",
|
|
7588
7656
|
inputSchema: { type: "object", properties: { layerId: LAYER_ID4 }, additionalProperties: false },
|
|
7589
7657
|
annotations: { title: "Clear a layer", ...DESTRUCTIVE3 },
|
|
7658
|
+
coordinates: { out: ["bounds"] },
|
|
7590
7659
|
handler: ({ layerId }) => c.clearLayer(layerId)
|
|
7591
7660
|
});
|
|
7592
7661
|
add({
|
|
@@ -7746,6 +7815,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7746
7815
|
additionalProperties: false
|
|
7747
7816
|
},
|
|
7748
7817
|
annotations: { title: "Pick a colour", ...READ6 },
|
|
7818
|
+
coordinates: { in: [""] },
|
|
7749
7819
|
handler: ({ x, y, layerId }) => c.pickColor(x, y, { layerId })
|
|
7750
7820
|
});
|
|
7751
7821
|
add({
|
|
@@ -7792,6 +7862,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7792
7862
|
additionalProperties: false
|
|
7793
7863
|
},
|
|
7794
7864
|
annotations: { title: "Paint a brush stroke", ...WRITE5 },
|
|
7865
|
+
coordinates: { in: ["points[]"], out: ["bounds"] },
|
|
7795
7866
|
handler: async (a) => {
|
|
7796
7867
|
await c.wetIdle();
|
|
7797
7868
|
const stroke = { points: a.points, layerId: a.layerId, brushId: a.brushId, brush: a.brush, color: a.color, paint: a.paint, seed: a.seed, snapToPerspective: a.snapToPerspective };
|
|
@@ -7812,6 +7883,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7812
7883
|
additionalProperties: false
|
|
7813
7884
|
},
|
|
7814
7885
|
annotations: { title: "List recorded strokes", ...READ6 },
|
|
7886
|
+
coordinates: { out: ["strokes[].bounds", "strokes[].points[]"] },
|
|
7815
7887
|
handler: ({ ids, last }) => {
|
|
7816
7888
|
const all = c.getStrokes();
|
|
7817
7889
|
const picked = ids ? all.filter((s) => ids.includes(s.id)) : all.slice(-(last ?? 100));
|
|
@@ -7869,6 +7941,7 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7869
7941
|
additionalProperties: false
|
|
7870
7942
|
},
|
|
7871
7943
|
annotations: { title: "Replay strokes, varied", ...WRITE5 },
|
|
7944
|
+
coordinates: { in: ["origin"], out: ["bounds"] },
|
|
7872
7945
|
handler: (a) => {
|
|
7873
7946
|
const replay = a;
|
|
7874
7947
|
return act({ kind: "replay", replay }, () => c.replayStrokes(replay));
|
|
@@ -7985,15 +8058,33 @@ function createPaintEditorToolProvider(controller, options = {}) {
|
|
|
7985
8058
|
tools.push(...createPaintReplayTools(c));
|
|
7986
8059
|
tools.push(...createPaintStickerTools(c));
|
|
7987
8060
|
tools.push(...createPigmentTools());
|
|
8061
|
+
const exclude = new Set(options.exclude ?? []);
|
|
8062
|
+
const description = "Natural-media painting: paper and grain, dry and wet brushes, the brush library, strokes as data, pigment mixing, stencils, guides, layers and export.";
|
|
7988
8063
|
return withPaintPlayback({
|
|
7989
8064
|
id: options.id ?? "paint-editor",
|
|
7990
8065
|
namespace: options.namespace,
|
|
7991
8066
|
title: "Paint editor",
|
|
7992
|
-
description:
|
|
8067
|
+
description: options.origin ? `${description} It is a layer of a larger canvas: every position is in canvas coordinates.` : description,
|
|
7993
8068
|
skill: "pixerate-paint-editor",
|
|
7994
|
-
tools
|
|
8069
|
+
tools: tools.filter((t) => !exclude.has(t.name)).map((t) => hostTool(t, options))
|
|
7995
8070
|
}, { director: !!options.director, surface: options.playbackSurface });
|
|
7996
8071
|
}
|
|
8072
|
+
function hostTool({ coordinates, ...tool }, { origin, onChange }) {
|
|
8073
|
+
if (!origin && !onChange) return tool;
|
|
8074
|
+
const write = !tool.annotations?.readOnlyHint;
|
|
8075
|
+
const done = (result) => {
|
|
8076
|
+
if (write) onChange?.({ tool: tool.name });
|
|
8077
|
+
return origin && coordinates?.out ? fromDocument(result, coordinates.out, origin) : result;
|
|
8078
|
+
};
|
|
8079
|
+
return {
|
|
8080
|
+
...tool,
|
|
8081
|
+
...origin ? { description: hostedText(tool.description ?? ""), inputSchema: hostedSchema(tool.inputSchema) } : {},
|
|
8082
|
+
handler: (args, context) => {
|
|
8083
|
+
const r = tool.handler(origin && coordinates?.in ? toDocument(args, coordinates.in, origin) : args, context);
|
|
8084
|
+
return r instanceof Promise ? r.then(done) : done(r);
|
|
8085
|
+
}
|
|
8086
|
+
};
|
|
8087
|
+
}
|
|
7997
8088
|
|
|
7998
8089
|
// src/paint-editor/ui.ts
|
|
7999
8090
|
var PAINT_TOOLS = [
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* category sticker) stay objects on their layer, which these tools list, move, turn, resize,
|
|
4
4
|
* recolour, reorder and delete. Each change is one undo step and redraws the layer.
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { PaintAgentTool } from './hosted.cjs';
|
|
7
7
|
import type { PaintEditorController } from './controller.cjs';
|
|
8
|
-
export declare function createPaintStickerTools(c: PaintEditorController):
|
|
8
|
+
export declare function createPaintStickerTools(c: PaintEditorController): PaintAgentTool[];
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* category sticker) stay objects on their layer, which these tools list, move, turn, resize,
|
|
4
4
|
* recolour, reorder and delete. Each change is one undo step and redraws the layer.
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { PaintAgentTool } from './hosted.js';
|
|
7
7
|
import type { PaintEditorController } from './controller.js';
|
|
8
|
-
export declare function createPaintStickerTools(c: PaintEditorController):
|
|
8
|
+
export declare function createPaintStickerTools(c: PaintEditorController): PaintAgentTool[];
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* impasto brush; these tools move the light, read and flatten a layer's relief, and switch instant
|
|
4
4
|
* drying of wet strokes.
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { PaintAgentTool } from './hosted.cjs';
|
|
7
7
|
import type { PaintEditorController } from './controller.cjs';
|
|
8
|
-
export declare function createPaintThickTools(c: PaintEditorController):
|
|
8
|
+
export declare function createPaintThickTools(c: PaintEditorController): PaintAgentTool[];
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
* impasto brush; these tools move the light, read and flatten a layer's relief, and switch instant
|
|
4
4
|
* drying of wet strokes.
|
|
5
5
|
*/
|
|
6
|
-
import type {
|
|
6
|
+
import type { PaintAgentTool } from './hosted.js';
|
|
7
7
|
import type { PaintEditorController } from './controller.js';
|
|
8
|
-
export declare function createPaintThickTools(c: PaintEditorController):
|
|
8
|
+
export declare function createPaintThickTools(c: PaintEditorController): PaintAgentTool[];
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* `list_papers`, `list_strokes`).
|
|
6
6
|
*/
|
|
7
7
|
import type { AgentToolProvider } from '../tools/registry.cjs';
|
|
8
|
+
import { type PaintOrigin } from './hosted.cjs';
|
|
8
9
|
import type { PaintEditorController } from './controller.cjs';
|
|
9
10
|
import type { PaintDirector } from './presence.cjs';
|
|
10
11
|
import type { PresenceActorInfo } from '../animation/presence.cjs';
|
|
@@ -25,6 +26,22 @@ export interface PaintEditorToolProviderOptions {
|
|
|
25
26
|
* Set it when one registry holds several editors of this kind, each attached under its own surface id.
|
|
26
27
|
*/
|
|
27
28
|
playbackSurface?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Hosts the document as a layer on a larger canvas: where document (0, 0) sits in host
|
|
31
|
+
* coordinates. Every position the tools take is then a host coordinate and every position they
|
|
32
|
+
* return is one too (strokes, picks, bounds, guides, stencils, stickers, wet areas); the
|
|
33
|
+
* descriptions say canvas coordinates. Directors receive document pixels.
|
|
34
|
+
*/
|
|
35
|
+
origin?: PaintOrigin;
|
|
36
|
+
/** Bare tool names to leave out (e.g. `new_canvas`, `export_psd` when the host owns the canvas). */
|
|
37
|
+
exclude?: readonly string[];
|
|
38
|
+
/** Called after each successful write (every tool not marked read-only), e.g. so the host can save. */
|
|
39
|
+
onChange?: (info: {
|
|
40
|
+
tool: string;
|
|
41
|
+
}) => void;
|
|
28
42
|
}
|
|
29
|
-
/**
|
|
30
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Agent tools for one paint editor. Pass a getter to create the document lazily: it is first
|
|
45
|
+
* called when a tool runs, never on registration.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createPaintEditorToolProvider(controller: PaintEditorController | (() => PaintEditorController), options?: PaintEditorToolProviderOptions): AgentToolProvider;
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* `list_papers`, `list_strokes`).
|
|
6
6
|
*/
|
|
7
7
|
import type { AgentToolProvider } from '../tools/registry.js';
|
|
8
|
+
import { type PaintOrigin } from './hosted.js';
|
|
8
9
|
import type { PaintEditorController } from './controller.js';
|
|
9
10
|
import type { PaintDirector } from './presence.js';
|
|
10
11
|
import type { PresenceActorInfo } from '../animation/presence.js';
|
|
@@ -25,6 +26,22 @@ export interface PaintEditorToolProviderOptions {
|
|
|
25
26
|
* Set it when one registry holds several editors of this kind, each attached under its own surface id.
|
|
26
27
|
*/
|
|
27
28
|
playbackSurface?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Hosts the document as a layer on a larger canvas: where document (0, 0) sits in host
|
|
31
|
+
* coordinates. Every position the tools take is then a host coordinate and every position they
|
|
32
|
+
* return is one too (strokes, picks, bounds, guides, stencils, stickers, wet areas); the
|
|
33
|
+
* descriptions say canvas coordinates. Directors receive document pixels.
|
|
34
|
+
*/
|
|
35
|
+
origin?: PaintOrigin;
|
|
36
|
+
/** Bare tool names to leave out (e.g. `new_canvas`, `export_psd` when the host owns the canvas). */
|
|
37
|
+
exclude?: readonly string[];
|
|
38
|
+
/** Called after each successful write (every tool not marked read-only), e.g. so the host can save. */
|
|
39
|
+
onChange?: (info: {
|
|
40
|
+
tool: string;
|
|
41
|
+
}) => void;
|
|
28
42
|
}
|
|
29
|
-
/**
|
|
30
|
-
|
|
43
|
+
/**
|
|
44
|
+
* Agent tools for one paint editor. Pass a getter to create the document lazily: it is first
|
|
45
|
+
* called when a tool runs, never on registration.
|
|
46
|
+
*/
|
|
47
|
+
export declare function createPaintEditorToolProvider(controller: PaintEditorController | (() => PaintEditorController), options?: PaintEditorToolProviderOptions): AgentToolProvider;
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* wet. Settings (wet parameters, flow maps, tilt) are saved with the document but are not undo
|
|
6
6
|
* steps; drying and wetting the paper are.
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
8
|
+
import type { PaintAgentTool } from './hosted.cjs';
|
|
9
9
|
import type { PaintEditorController } from './controller.cjs';
|
|
10
10
|
export declare const WET_PARAM_SCHEMA: {
|
|
11
11
|
readonly viscosity: {
|
|
@@ -63,4 +63,4 @@ export declare const WET_PARAM_SCHEMA: {
|
|
|
63
63
|
readonly description: string;
|
|
64
64
|
};
|
|
65
65
|
};
|
|
66
|
-
export declare function createPaintWetTools(c: PaintEditorController):
|
|
66
|
+
export declare function createPaintWetTools(c: PaintEditorController): PaintAgentTool[];
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* wet. Settings (wet parameters, flow maps, tilt) are saved with the document but are not undo
|
|
6
6
|
* steps; drying and wetting the paper are.
|
|
7
7
|
*/
|
|
8
|
-
import type {
|
|
8
|
+
import type { PaintAgentTool } from './hosted.js';
|
|
9
9
|
import type { PaintEditorController } from './controller.js';
|
|
10
10
|
export declare const WET_PARAM_SCHEMA: {
|
|
11
11
|
readonly viscosity: {
|
|
@@ -63,4 +63,4 @@ export declare const WET_PARAM_SCHEMA: {
|
|
|
63
63
|
readonly description: string;
|
|
64
64
|
};
|
|
65
65
|
};
|
|
66
|
-
export declare function createPaintWetTools(c: PaintEditorController):
|
|
66
|
+
export declare function createPaintWetTools(c: PaintEditorController): PaintAgentTool[];
|
package/package.json
CHANGED
|
@@ -255,6 +255,7 @@ in the source frame at the document's bit depth, colour mode and profile. Read
|
|
|
255
255
|
| `quick_select` | Quick Selection: `strokes` (brush paths of `{x, y}`), `size` (diameter, default 30), `mode`, `sampleAllLayers`, `enhanceEdge`. Each stroke grows out to its region's edges; with `replace` the first replaces and the rest add. Paint inside the area, then check and fix with `add` / `subtract` strokes. Returns the summary. |
|
|
256
256
|
| `select_object` | Object Selection (GrabCut): `rect` (a box around the object) or `point` (on it), `mode`, `sampleAllLayers`, `enhanceEdge`. Outside the box is background. Make the box a little larger than the object. Returns the summary. |
|
|
257
257
|
| `select_focus_area` | Select › Focus Area: the in-focus (sharp) parts, edge fitted to the image. `range` 0..100 (higher takes in softer areas), `noise` 0..100 (raise when grain is selected), `softness` px, `mode`. Returns the summary and `noiseLevel`. |
|
|
258
|
+
| `select_sky` | Select › Sky: the sky (blue or overcast, from the top down, with patches through gaps), edge fitted to the skyline. `threshold` 0..100 (higher takes in haze), `softness` px, `mode`. Returns the summary, and `note` when none was found. |
|
|
258
259
|
| `select_polygon` | The lasso: `points` (at least 3 `{x, y}` along the outline, closed automatically), `mode`, `feather`, `antiAlias`; `snapToEdges: {width, contrast}` makes it the Magnetic Lasso (points snap to edges, the outline follows them). Trace a subject's contour, not its box. Returns the summary (plus `outlinePoints` when snapped). |
|
|
259
260
|
| `magic_wand` | Select similar colour from `x`, `y`: `tolerance` 0..255 (default 32), `contiguous` (default true), `distance` `rgb` or `perceptual`, `sampleMerged`, `sample` (`composite` or `active-layer`), `mode`, `feather`. For flat backdrops and solid colours. Returns the summary. |
|
|
260
261
|
| `select_color_range` | Color Range, soft: `samples` (`[{color, x?, y?}]`) or a `preset` (reds … magentas, highlights, midtones, shadows, skinTones); `fuzziness` (samples 0..200, default 40; tone presets' falloff, default 20), `localized` px radius around positioned samples, `invert`, `sample`, `mode`, `feather`. Returns the summary. |
|
|
@@ -99,6 +99,7 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
|
|
|
99
99
|
| One object among others, where you can box it (a product on a table, a person in a group) | `select_object` with a `rect` a little larger than the object on every side; then `quick_select` add / subtract strokes for what it missed |
|
|
100
100
|
| A subject with a clear outline (product, person, animal) | `select_subject` (with a host model, by description; without one it takes whatever stands out most, so check `method` and the result); else `select_object` around it, or `select_polygon` with `snapToEdges` (12–30 points within `width` of the edge), then `refine_selection` |
|
|
101
101
|
| An area or subject that differs from its surroundings (a shirt, a sky, a car) | `quick_select` with strokes painted inside it, away from the edge (`size` a fraction of its width); `add` / `subtract` strokes where it fell short or leaked |
|
|
102
|
+
| The sky (to replace, recolour, or to leave out with `invert_selection`) | `select_sky`; raise `threshold` for haze or a sunset glow, lower it if pale buildings come in |
|
|
102
103
|
| A sharp subject on a blurred background (shallow depth of field) | `select_focus_area`; raise `range` if parts of the subject are missing, `noise` if grainy background comes in |
|
|
103
104
|
| A low-contrast or busy outline the edges don't follow | `select_polygon` without `snapToEdges`, along the contour (20–60 points) |
|
|
104
105
|
| Flat backdrop or solid colour (studio white, green screen, sky without clouds) | `magic_wand` on the backdrop (`tolerance` 20–40; `distance: "perceptual"` for gradients), then `invert_selection` |
|
|
@@ -118,8 +119,8 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
|
|
|
118
119
|
Add the new background with `add_pixel_layer` and move it under the cutout (`move_layer`).
|
|
119
120
|
`render_preview` and look for a halo; if there is one, load the copy's pixels back as a
|
|
120
121
|
selection, `shrink` 1 px, and mask.
|
|
121
|
-
- **Replace the sky:** `
|
|
122
|
-
`add` missed patches, `refine_selection` radius 8–16 along the horizon and trees, then
|
|
122
|
+
- **Replace the sky:** `select_sky` (or `magic_wand` / `select_color_range` `preset: "blues"`
|
|
123
|
+
if it finds none), `add` missed patches, `refine_selection` radius 8–16 along the horizon and trees, then
|
|
123
124
|
`add_layer_mask` on the new sky layer. Match it with a clipped curves layer (`clipped: true`).
|
|
124
125
|
- **Adjust only part of an image:** select, then `apply_selection_as_mask` with `kind` and
|
|
125
126
|
`params`, and `feather` in proportion to the image (a few px for objects, 50–200 px for
|
|
@@ -205,6 +206,7 @@ This skill is the method. `pixerate-image-editor` has the full tool reference.
|
|
|
205
206
|
- `quick_select`: brush strokes that grow out to the region's edges
|
|
206
207
|
- `select_object`: the object in a box or under a point
|
|
207
208
|
- `select_focus_area`: what is in focus
|
|
209
|
+
- `select_sky`: the sky
|
|
208
210
|
- `select_line`: a single row or column
|
|
209
211
|
- `select_layer_transparency`: a layer's own pixels
|
|
210
212
|
- `grow_selection`, `select_similar`: add similar colour, connected or anywhere
|
|
@@ -265,6 +265,15 @@ for the controls and presets.
|
|
|
265
265
|
and baked in, at `opacity` strength. Transparent areas stay transparent, and thick paint keeps
|
|
266
266
|
its relief and stays lit by the editor's light. Dry wet paint first (`dry_paint`).
|
|
267
267
|
|
|
268
|
+
## Canvas-hosted layer
|
|
269
|
+
|
|
270
|
+
A host can mount the painting as a layer of a larger canvas (`createPaintEditorToolProvider` with
|
|
271
|
+
`origin`). Then every position you pass or read back is a canvas coordinate, not a document
|
|
272
|
+
pixel: stroke points, picks, bounds, guides, stencils, stickers and wet areas alike.
|
|
273
|
+
`get_canvas_info` `bounds` says where the painting lies on that canvas; paint inside it. The
|
|
274
|
+
host may leave out tools it owns (`new_canvas`, `set_canvas`, `extend_canvas`, exports, the
|
|
275
|
+
view), so work with the tools you are given.
|
|
276
|
+
|
|
268
277
|
## Tool reference
|
|
269
278
|
|
|
270
279
|
### Canvas and paper
|