@pixerate/editor 0.201.0 → 0.203.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/dist/agent/index.cjs +2 -2
  2. package/dist/agent/index.js +1 -1
  3. package/dist/{chunk-GLQQ2VIL.cjs → chunk-53GOGBTM.cjs} +23 -1
  4. package/dist/{chunk-3TNA7JKI.js → chunk-ZPJEH456.js} +22 -0
  5. package/dist/commands/builtins.d.cts +6 -0
  6. package/dist/commands/builtins.d.ts +6 -0
  7. package/dist/commands/index.cjs +4 -2
  8. package/dist/commands/index.js +4 -2
  9. package/dist/image-editor/controller.d.cts +24 -1
  10. package/dist/image-editor/controller.d.ts +24 -1
  11. package/dist/image-editor/index.cjs +226 -171
  12. package/dist/image-editor/index.js +78 -23
  13. package/dist/image-editor/selection/segmentation.d.cts +7 -0
  14. package/dist/image-editor/selection/segmentation.d.ts +7 -0
  15. package/dist/paint-editor/guide-tools.d.cts +2 -2
  16. package/dist/paint-editor/guide-tools.d.ts +2 -2
  17. package/dist/paint-editor/hosted.d.cts +34 -0
  18. package/dist/paint-editor/hosted.d.ts +34 -0
  19. package/dist/paint-editor/index.cjs +101 -10
  20. package/dist/paint-editor/index.d.cts +1 -0
  21. package/dist/paint-editor/index.d.ts +1 -0
  22. package/dist/paint-editor/index.js +96 -5
  23. package/dist/paint-editor/sticker-tools.d.cts +2 -2
  24. package/dist/paint-editor/sticker-tools.d.ts +2 -2
  25. package/dist/paint-editor/thick-tools.d.cts +2 -2
  26. package/dist/paint-editor/thick-tools.d.ts +2 -2
  27. package/dist/paint-editor/tools.d.cts +19 -2
  28. package/dist/paint-editor/tools.d.ts +19 -2
  29. package/dist/paint-editor/wet-tools.d.cts +2 -2
  30. package/dist/paint-editor/wet-tools.d.ts +2 -2
  31. package/package.json +1 -1
  32. package/skills/pixerate-image-editor/SKILL.md +2 -2
  33. 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: _nullishCoalesce(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: _nullishCoalesce(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: _chunkEBDCRTZIcjs.MAX_BRUSH_SIZE, description: "Diameter in document pixels." },
7100
+ size: { type: "number", minimum: 1, maximum: _chunkEBDCRTZIcjs.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: { ..._nullishCoalesce(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(-(_nullishCoalesce(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(..._chunkV7GWZIZKcjs.createPigmentTools.call(void 0, ));
8061
+ const exclude = new Set(_nullishCoalesce(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: _nullishCoalesce(options.id, () => ( "paint-editor")),
7990
8065
  namespace: options.namespace,
7991
8066
  title: "Paint editor",
7992
- description: "Natural-media painting: paper and grain, dry and wet brushes, the brush library, strokes as data, pigment mixing, stencils, guides, layers and export.",
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 = !_optionalChain([tool, 'access', _320 => _320.annotations, 'optionalAccess', _321 => _321.readOnlyHint]);
8075
+ const done = (result) => {
8076
+ if (write) _optionalChain([onChange, 'optionalCall', _322 => _322({ tool: tool.name })]);
8077
+ return origin && _optionalChain([coordinates, 'optionalAccess', _323 => _323.out]) ? fromDocument(result, coordinates.out, origin) : result;
8078
+ };
8079
+ return {
8080
+ ...tool,
8081
+ ...origin ? { description: hostedText(_nullishCoalesce(tool.description, () => ( ""))), inputSchema: hostedSchema(tool.inputSchema) } : {},
8082
+ handler: (args, context) => {
8083
+ const r = tool.handler(origin && _optionalChain([coordinates, 'optionalAccess', _324 => _324.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 = [
@@ -8097,12 +8188,12 @@ var PaintGesture = (_class11 = class {
8097
8188
  if (!m || m.pointerId !== e.pointerId) return;
8098
8189
  this.mode = null;
8099
8190
  this.predicted = [];
8100
- if (m.kind === "stroke") _optionalChain([this, 'access', _320 => _320.controller, 'access', _321 => _321.activeStroke, 'optionalAccess', _322 => _322.end, 'call', _323 => _323()]);
8191
+ if (m.kind === "stroke") _optionalChain([this, 'access', _325 => _325.controller, 'access', _326 => _326.activeStroke, 'optionalAccess', _327 => _327.end, 'call', _328 => _328()]);
8101
8192
  }
8102
8193
  cancel() {
8103
8194
  this.penDown = false;
8104
8195
  this.predicted = [];
8105
- if (_optionalChain([this, 'access', _324 => _324.mode, 'optionalAccess', _325 => _325.kind]) === "stroke") _optionalChain([this, 'access', _326 => _326.controller, 'access', _327 => _327.activeStroke, 'optionalAccess', _328 => _328.cancel, 'call', _329 => _329()]);
8196
+ if (_optionalChain([this, 'access', _329 => _329.mode, 'optionalAccess', _330 => _330.kind]) === "stroke") _optionalChain([this, 'access', _331 => _331.controller, 'access', _332 => _332.activeStroke, 'optionalAccess', _333 => _333.cancel, 'call', _334 => _334()]);
8106
8197
  this.mode = null;
8107
8198
  }
8108
8199
  get active() {
@@ -8236,7 +8327,7 @@ var PAPER_SLIDERS = [
8236
8327
  ];
8237
8328
  function readPath(obj, path) {
8238
8329
  let v = obj;
8239
- for (const k of path.split(".")) v = _optionalChain([v, 'optionalAccess', _330 => _330[k]]);
8330
+ for (const k of path.split(".")) v = _optionalChain([v, 'optionalAccess', _335 => _335[k]]);
8240
8331
  return typeof v === "number" ? v : 0;
8241
8332
  }
8242
8333
  function patchPath(obj, path, value) {
@@ -8358,7 +8449,7 @@ function brushEngineControls(brush) {
8358
8449
  impastoTool: true
8359
8450
  };
8360
8451
  case "wet": {
8361
- const tool = _nullishCoalesce(_optionalChain([brush, 'access', _331 => _331.wet, 'optionalAccess', _332 => _332.tool]), () => ( "paint"));
8452
+ const tool = _nullishCoalesce(_optionalChain([brush, 'access', _336 => _336.wet, 'optionalAccess', _337 => _337.tool]), () => ( "paint"));
8362
8453
  const sliders = tool === "paint" ? [
8363
8454
  { path: "wet.water", label: "Water", min: 0, max: 1, step: 0.01 },
8364
8455
  { path: "wet.pigment", label: "Pigment", min: 0, max: 1, step: 0.01 },
@@ -8491,7 +8582,7 @@ function startWetClock(controller, frame, cancel) {
8491
8582
  id = raf(loop);
8492
8583
  return () => {
8493
8584
  stopped = true;
8494
- _optionalChain([caf, 'optionalCall', _333 => _333(id)]);
8585
+ _optionalChain([caf, 'optionalCall', _338 => _338(id)]);
8495
8586
  };
8496
8587
  }
8497
8588
  function drawWaterOverlay(controller, ctx) {
@@ -26,6 +26,7 @@ export * from './mixing-pad.cjs';
26
26
  export * from './controller.cjs';
27
27
  export * from './presence.cjs';
28
28
  export * from './tools.cjs';
29
+ export type { PaintAgentTool, PaintOrigin, PaintToolCoordinates } from './hosted.cjs';
29
30
  export * from './ui.cjs';
30
31
  export { BLEND_MODES, PAINTERLY_BLEND_MODES, isBlendMode, isPainterlyBlendMode, standardBlendMode, type BlendMode, type BlendModeInfo } from '../raster/blend.cjs';
31
32
  export * from './wet.cjs';
@@ -26,6 +26,7 @@ export * from './mixing-pad.js';
26
26
  export * from './controller.js';
27
27
  export * from './presence.js';
28
28
  export * from './tools.js';
29
+ export type { PaintAgentTool, PaintOrigin, PaintToolCoordinates } from './hosted.js';
29
30
  export * from './ui.js';
30
31
  export { BLEND_MODES, PAINTERLY_BLEND_MODES, isBlendMode, isPainterlyBlendMode, standardBlendMode, type BlendMode, type BlendModeInfo } from '../raster/blend.js';
31
32
  export * from './wet.js';