@pixerate/editor 0.130.0 → 0.133.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 (96) hide show
  1. package/dist/animation-editor/index.cjs +55 -55
  2. package/dist/animation-editor/index.d.cts +4 -4
  3. package/dist/animation-editor/index.d.ts +4 -4
  4. package/dist/animation-editor/index.js +2 -2
  5. package/dist/{bezier-CigmusV_.d.ts → bezier-BAGdL89w.d.ts} +1 -1
  6. package/dist/{bezier-CdoGygwG.d.cts → bezier-DL2wPsFf.d.cts} +1 -1
  7. package/dist/canvas/index.d.cts +7 -7
  8. package/dist/canvas/index.d.ts +7 -7
  9. package/dist/{chunk-MZXQTKUM.cjs → chunk-24ATT2PE.cjs} +10 -6
  10. package/dist/chunk-24ATT2PE.cjs.map +1 -0
  11. package/dist/{chunk-AHUYMDYW.js → chunk-2HC4DRF4.js} +10 -6
  12. package/dist/chunk-2HC4DRF4.js.map +1 -0
  13. package/dist/{chunk-2PXRKMOH.cjs → chunk-2QKUD3E2.cjs} +36 -36
  14. package/dist/{chunk-2PXRKMOH.cjs.map → chunk-2QKUD3E2.cjs.map} +1 -1
  15. package/dist/{chunk-PMHPXCJC.cjs → chunk-65GNHOD4.cjs} +268 -199
  16. package/dist/chunk-65GNHOD4.cjs.map +1 -0
  17. package/dist/{chunk-YRZ6NVE3.js → chunk-6CTZAGZ2.js} +235 -4
  18. package/dist/chunk-6CTZAGZ2.js.map +1 -0
  19. package/dist/{chunk-ERYGCA72.js → chunk-AOCFL7L7.js} +88 -19
  20. package/dist/chunk-AOCFL7L7.js.map +1 -0
  21. package/dist/{chunk-LXGOYRPE.cjs → chunk-F7GGEAO2.cjs} +322 -75
  22. package/dist/chunk-F7GGEAO2.cjs.map +1 -0
  23. package/dist/{chunk-PRYMC442.js → chunk-FOBB6AIO.js} +257 -10
  24. package/dist/chunk-FOBB6AIO.js.map +1 -0
  25. package/dist/{chunk-W6M7C7R3.cjs → chunk-NVTHUPJ6.cjs} +236 -5
  26. package/dist/chunk-NVTHUPJ6.cjs.map +1 -0
  27. package/dist/{chunk-L5OTIYOO.js → chunk-VGZSIXX3.js} +2 -2
  28. package/dist/effects/index.d.cts +4 -4
  29. package/dist/effects/index.d.ts +4 -4
  30. package/dist/{export-CejjC2iI.d.cts → export-BICOEM9d.d.cts} +33 -11
  31. package/dist/{export-5WDKJqTy.d.cts → export-CeFplzAW.d.cts} +1 -1
  32. package/dist/{export-B4r8b9Ft.d.ts → export-XnuX8FZ2.d.ts} +33 -11
  33. package/dist/{export-CU_ARP3A.d.ts → export-zB4HITwP.d.ts} +1 -1
  34. package/dist/{helpers-CzZu51eI.d.cts → helpers-BuKdqUlM.d.cts} +56 -1
  35. package/dist/{helpers-X_rycLNJ.d.ts → helpers-cssRNUzH.d.ts} +56 -1
  36. package/dist/image-editor/index.cjs +1064 -903
  37. package/dist/image-editor/index.cjs.map +1 -1
  38. package/dist/image-editor/index.d.cts +32 -13
  39. package/dist/image-editor/index.d.ts +32 -13
  40. package/dist/image-editor/index.js +180 -19
  41. package/dist/image-editor/index.js.map +1 -1
  42. package/dist/index.cjs +11 -4
  43. package/dist/index.cjs.map +1 -1
  44. package/dist/index.js +11 -4
  45. package/dist/index.js.map +1 -1
  46. package/dist/{opentype-DlRMYcCW.d.cts → opentype-C3iyIFRx.d.cts} +1 -1
  47. package/dist/{opentype-CrHAsKh3.d.ts → opentype-DG5Lkwul.d.ts} +1 -1
  48. package/dist/paint/index.cjs +8 -2
  49. package/dist/paint/index.cjs.map +1 -1
  50. package/dist/paint/index.d.cts +14 -3
  51. package/dist/paint/index.d.ts +14 -3
  52. package/dist/paint/index.js +7 -1
  53. package/dist/paint-editor/index.cjs +78 -78
  54. package/dist/paint-editor/index.d.cts +7 -7
  55. package/dist/paint-editor/index.d.ts +7 -7
  56. package/dist/paint-editor/index.js +3 -3
  57. package/dist/{pathfinder-CKfKOVP4.d.cts → pathfinder-8vXik95z.d.cts} +1 -1
  58. package/dist/{pathfinder-Dlss70hL.d.ts → pathfinder-DKLt4UDG.d.ts} +1 -1
  59. package/dist/{pool-DDOo0xJH.d.ts → pool-C69pkWRz.d.ts} +1 -1
  60. package/dist/{pool-DKic69LD.d.cts → pool-VAWcYUbs.d.cts} +1 -1
  61. package/dist/{rand-pQQ2sJTk.d.ts → rand-BaqEOs1Y.d.ts} +17 -4
  62. package/dist/{rand-DTXAssW1.d.cts → rand-DdpFzCo6.d.cts} +17 -4
  63. package/dist/raster/index.cjs +2 -2
  64. package/dist/raster/index.d.cts +4 -4
  65. package/dist/raster/index.d.ts +4 -4
  66. package/dist/raster/index.js +1 -1
  67. package/dist/{svg-BRaii3pI.d.cts → svg-BThlZmja.d.cts} +2 -2
  68. package/dist/{svg-BYVZeVo0.d.ts → svg-CEYpvsgH.d.ts} +2 -2
  69. package/dist/{tools-CtheDLDr.d.cts → tools-CRnZJXXr.d.cts} +1 -1
  70. package/dist/{tools-D8bZ-7YG.d.ts → tools-CVGqEpO8.d.ts} +1 -1
  71. package/dist/vector/index.cjs +1493 -1032
  72. package/dist/vector/index.cjs.map +1 -1
  73. package/dist/vector/index.d.cts +155 -12
  74. package/dist/vector/index.d.ts +155 -12
  75. package/dist/vector/index.js +469 -8
  76. package/dist/vector/index.js.map +1 -1
  77. package/dist/video-editor/index.d.cts +4 -4
  78. package/dist/video-editor/index.d.ts +4 -4
  79. package/dist/{warp-BFoYxy_s.d.cts → warp-ChcFDdlT.d.cts} +1 -1
  80. package/dist/{warp-CZVC8pB5.d.ts → warp-mKylbLiF.d.ts} +1 -1
  81. package/package.json +2 -2
  82. package/skills/pixerate-image-editor/SKILL.md +7 -3
  83. package/skills/pixerate-image-editor/references/color-management.md +1 -1
  84. package/skills/pixerate-image-editor/references/photoshop-files.md +2 -2
  85. package/skills/pixerate-image-editor/references/retouching.md +12 -3
  86. package/skills/pixerate-vector/SKILL.md +17 -0
  87. package/skills/pixerate-vector/references/commands.md +19 -0
  88. package/dist/chunk-AHUYMDYW.js.map +0 -1
  89. package/dist/chunk-ERYGCA72.js.map +0 -1
  90. package/dist/chunk-LXGOYRPE.cjs.map +0 -1
  91. package/dist/chunk-MZXQTKUM.cjs.map +0 -1
  92. package/dist/chunk-PMHPXCJC.cjs.map +0 -1
  93. package/dist/chunk-PRYMC442.js.map +0 -1
  94. package/dist/chunk-W6M7C7R3.cjs.map +0 -1
  95. package/dist/chunk-YRZ6NVE3.js.map +0 -1
  96. /package/dist/{chunk-L5OTIYOO.js.map → chunk-VGZSIXX3.js.map} +0 -0
@@ -75,6 +75,7 @@ import {
75
75
  applyDepthMask,
76
76
  applyImageProjectionToContext,
77
77
  applyLegacyAdjustments,
78
+ applyLegacyFloat,
78
79
  applyMatrix3,
79
80
  applyParams,
80
81
  applyProcess2Adjustments,
@@ -362,7 +363,7 @@ import {
362
363
  writePsd,
363
364
  xyToTempTint,
364
365
  xyToUv
365
- } from "../chunk-ERYGCA72.js";
366
+ } from "../chunk-AOCFL7L7.js";
366
367
  import {
367
368
  parseCssColor
368
369
  } from "../chunk-CTTF4KYV.js";
@@ -426,7 +427,7 @@ import {
426
427
  validateLayerEffects,
427
428
  warpSurface,
428
429
  workingToSamples
429
- } from "../chunk-AHUYMDYW.js";
430
+ } from "../chunk-2HC4DRF4.js";
430
431
  import {
431
432
  applyHomography,
432
433
  boxQuad,
@@ -447,6 +448,8 @@ import {
447
448
  applyRetouchToTile,
448
449
  applyToneToDeepTile,
449
450
  bestOffset,
451
+ boxBlurPixels,
452
+ contentAwareFill,
450
453
  dilateMask,
451
454
  dodgeBurn,
452
455
  dodgeBurnCurve,
@@ -457,6 +460,7 @@ import {
457
460
  normalizeRetouch,
458
461
  perDabStrength,
459
462
  readSourceRegion,
463
+ retouchFilter,
460
464
  rgbaViewOfGrey,
461
465
  saturationOf,
462
466
  seamlessClone,
@@ -467,7 +471,7 @@ import {
467
471
  spotHealPixels,
468
472
  spotHealWindow,
469
473
  toneOf
470
- } from "../chunk-YRZ6NVE3.js";
474
+ } from "../chunk-6CTZAGZ2.js";
471
475
  import {
472
476
  BrushstrokeCache,
473
477
  brushstrokeDataUrl,
@@ -802,7 +806,7 @@ import {
802
806
  warpMeshParamAt,
803
807
  warpMeshRows,
804
808
  warpPresetMesh
805
- } from "../chunk-PRYMC442.js";
809
+ } from "../chunk-FOBB6AIO.js";
806
810
  import "../chunk-YGJIER2R.js";
807
811
  import "../chunk-KHNZ6MOU.js";
808
812
  import {
@@ -1332,6 +1336,27 @@ var ImageEditorRenderer = class {
1332
1336
  }
1333
1337
  ctx.restore();
1334
1338
  }
1339
+ if (options.cloneSourceMarker) {
1340
+ const m = projectImagePoint(options.cloneSourceMarker, projection);
1341
+ ctx.save();
1342
+ try {
1343
+ const r = 7;
1344
+ ctx.beginPath();
1345
+ ctx.arc(m.x, m.y, r, 0, Math.PI * 2);
1346
+ ctx.moveTo(m.x - r - 4, m.y);
1347
+ ctx.lineTo(m.x + r + 4, m.y);
1348
+ ctx.moveTo(m.x, m.y - r - 4);
1349
+ ctx.lineTo(m.x, m.y + r + 4);
1350
+ ctx.lineWidth = 3;
1351
+ ctx.strokeStyle = "rgba(0,0,0,0.6)";
1352
+ ctx.stroke();
1353
+ ctx.lineWidth = 1;
1354
+ ctx.strokeStyle = "#ffffff";
1355
+ ctx.stroke();
1356
+ } catch {
1357
+ }
1358
+ ctx.restore();
1359
+ }
1335
1360
  if (options.textEdit) {
1336
1361
  const t = options.textEdit;
1337
1362
  ctx.save();
@@ -3492,6 +3517,19 @@ function colorModePlanes(doc, mode) {
3492
3517
  return planes;
3493
3518
  };
3494
3519
  }
3520
+ function colorModePlanes16(doc, mode) {
3521
+ const src = resolveProfile(doc);
3522
+ const cmyk = mode.mode === "cmyk";
3523
+ const t = cmyk ? Transform.create(src, resolveProfile(mode.profile), { intent: mode.intent, bpc: true }) : Transform.create(src, builtinProfile("lab-d50"), { intent: "relative", bpc: false });
3524
+ const k = cmyk ? 4 : 3;
3525
+ return (rgba, n) => {
3526
+ const out = new Uint16Array(n * 4);
3527
+ t.convert16(rgba, 4, out, 4, false);
3528
+ const planes = Array.from({ length: k }, () => new Uint16Array(n));
3529
+ for (let i = 0; i < n; i++) for (let c = 0; c < k; c++) planes[c][i] = cmyk ? 65535 - out[i * 4 + c] : out[i * 4 + c];
3530
+ return planes;
3531
+ };
3532
+ }
3495
3533
  var resolved = /* @__PURE__ */ new WeakMap();
3496
3534
  var fromBytes = /* @__PURE__ */ new Map();
3497
3535
  function resolveProfile(doc) {
@@ -3651,7 +3689,7 @@ function convertSurfaceColors(surface, t) {
3651
3689
 
3652
3690
  // src/image-editor/controller.ts
3653
3691
  var IMAGE_EDITOR_SCHEMA_VERSION = LAYERED_SCHEMA_VERSION;
3654
- var RETOUCH_TOOLS = ["clone", "heal", "spot-heal", "smudge", "dodge", "burn", "sponge"];
3692
+ var RETOUCH_TOOLS = ["clone", "heal", "spot-heal", "smudge", "dodge", "burn", "sponge", "blur", "sharpen", "history"];
3655
3693
  var isRetouchTool = (tool) => RETOUCH_TOOLS.includes(tool);
3656
3694
  var STROKE_TOOLS = ["brush", "eraser", "mixer", ...RETOUCH_TOOLS];
3657
3695
  function paintCoverageInto(base, coverage, apply, rects, depth = null) {
@@ -3925,6 +3963,18 @@ var PaintStrokeSession = class {
3925
3963
  this.controller._strokeCommit(this.before, this.target.id, this.target.channel, null);
3926
3964
  }
3927
3965
  };
3966
+ function workRegionOf(surface, rect, depth) {
3967
+ if (!depth) return Float32Array.from(readRegion(surface, rect));
3968
+ const d = readDeepRegion(surface, rect, depth);
3969
+ const out = new Float32Array(d.length);
3970
+ if (d instanceof Uint16Array) {
3971
+ for (let i = 0; i < d.length; i++) out[i] = d[i] / 257;
3972
+ return out;
3973
+ }
3974
+ const ch = surface.channels;
3975
+ for (let i = 0; i < d.length; i++) out[i] = (ch === 4 && (i & 3) !== 3 ? srgbEncode(d[i]) : d[i]) * 255;
3976
+ return out;
3977
+ }
3928
3978
  function workTileOf(tile) {
3929
3979
  if (!tile) return null;
3930
3980
  const d = tile.deep;
@@ -6124,7 +6174,7 @@ var ImageEditorController = class {
6124
6174
  if (tool === "smudge") return { kind: "dabs", create: (pixels) => new RetouchDabStroke(brush, pixels, smudgeEffect(brush, settings.strength)) };
6125
6175
  if (tool === "sponge") return { kind: "dabs", create: (pixels) => new RetouchDabStroke(brush, pixels, spongeEffect(brush, settings.spongeMode === "saturate", settings.vibrance)) };
6126
6176
  const retouchApply = {
6127
- opacity: tool === "clone" || tool === "heal" ? brush.opacity : 1,
6177
+ opacity: tool === "clone" || tool === "heal" || tool === "history" ? brush.opacity : tool === "blur" || tool === "sharpen" ? brush.opacity * settings.strength : 1,
6128
6178
  blend: tool === "clone" && !mask ? brush.blend : "normal",
6129
6179
  lockTransparency: target.lockTransparency,
6130
6180
  selection: apply.selection,
@@ -6136,6 +6186,41 @@ var ImageEditorController = class {
6136
6186
  return { kind: "coverage", coverage, apply: retouchApply, start: () => {
6137
6187
  }, paint: () => paint2 };
6138
6188
  }
6189
+ if (tool === "blur" || tool === "sharpen") {
6190
+ const n = target.surface.channels;
6191
+ const depth = !mask && this.bitDepth !== 8 ? this.bitDepth : null;
6192
+ const m = 6;
6193
+ const filtered = new TilePixelSource(
6194
+ (tx, ty) => {
6195
+ const r = { x: tx * TILE - m, y: ty * TILE - m, width: TILE + 2 * m, height: TILE + 2 * m };
6196
+ const d = workRegionOf(target.surface, r, depth);
6197
+ retouchFilter(d, r.width, r.height, n, tool, 2, 1.2);
6198
+ const out = new Float32Array(TILE * TILE * n);
6199
+ for (let y = 0; y < TILE; y++) out.set(d.subarray(((y + m) * r.width + m) * n, ((y + m) * r.width + m + TILE) * n), y * TILE * n);
6200
+ return out;
6201
+ },
6202
+ n,
6203
+ target.offset
6204
+ );
6205
+ const paint2 = { kind: "source", source: filtered, dx: 0, dy: 0 };
6206
+ return { kind: "coverage", coverage, apply: retouchApply, start: () => {
6207
+ }, paint: () => paint2 };
6208
+ }
6209
+ if (tool === "history") {
6210
+ const steps = settings.historySteps;
6211
+ const entry = steps > 0 ? this.undoStack[this.undoStack.length - steps] ?? this.undoStack[0] : this.undoStack[0];
6212
+ if (!entry) throw new LayerOperationError("There is no earlier state to paint from yet: the History Brush paints a layer as it was some undo steps back. Make a change first.");
6213
+ const then = findLayer(entry.layers, target.id)?.layer;
6214
+ if (!then) throw new LayerOperationError(`Layer "${target.id}" did not exist ${steps > 0 ? `${steps} steps back` : "in the oldest state"}; choose fewer history steps (historySteps).`);
6215
+ const surface = target.channel === "mask" ? then.mask?.surface : then.type === "pixel" ? then.surface : void 0;
6216
+ if (!surface) throw new LayerOperationError(`Layer "${target.id}" had no ${target.channel === "mask" ? "mask" : "pixels"} in that state.`);
6217
+ const offset2 = then.type === "pixel" ? then.offset : { x: 0, y: 0 };
6218
+ const depth = !mask && this.bitDepth !== 8 ? this.bitDepth : null;
6219
+ const source2 = depth ? new TilePixelSource((tx, ty) => workTileOf(surface.tiles.get(tileKey(tx, ty))), 4, offset2) : this.surfaceSource(surface, offset2);
6220
+ const paint2 = { kind: "source", source: source2, dx: 0, dy: 0 };
6221
+ return { kind: "coverage", coverage, apply: retouchApply, start: () => {
6222
+ }, paint: () => paint2 };
6223
+ }
6139
6224
  const sample = mask ? "current" : settings.sample;
6140
6225
  const deep = !mask && this.bitDepth !== 8 ? this.bitDepth : null;
6141
6226
  const own = deep ? new TilePixelSource((tx, ty) => workTileOf(target.surface.tiles.get(tileKey(tx, ty))), 4, target.offset) : this.surfaceSource(target.surface, target.offset);
@@ -6992,6 +7077,20 @@ var ImageEditorController = class {
6992
7077
  }
6993
7078
  return this.blankSource;
6994
7079
  }
7080
+ /**
7081
+ * Where the Clone Stamp or Healing Brush samples for a brush at `cursor` (document pixels), or
7082
+ * `null` when neither tool is active or no source is set: the cursor plus the aligned offset
7083
+ * once a stroke has fixed it, else the source point itself (where the next stroke starts
7084
+ * sampling). The editors draw it as the clone source marker.
7085
+ */
7086
+ cloneSourceMarker(cursor = null) {
7087
+ const s = this.state;
7088
+ if (s.activeTool !== "clone" && s.activeTool !== "heal") return null;
7089
+ const cs = s.paint.cloneSource;
7090
+ if (!cs) return null;
7091
+ if (cursor && cs.offset && s.paint.retouch.aligned) return { x: cursor.x + cs.offset.x, y: cursor.y + cs.offset.y };
7092
+ return { x: cs.x, y: cs.y };
7093
+ }
6995
7094
  async render(targetCanvas, options) {
6996
7095
  const source = this.drawSource();
6997
7096
  if (!source) return;
@@ -7001,6 +7100,8 @@ var ImageEditorController = class {
7001
7100
  try {
7002
7101
  await this.renderer.renderToCanvas(targetCanvas, source, this.state, {
7003
7102
  ...options,
7103
+ // The clone source follows the brush cursor (both editors pass it).
7104
+ cloneSourceMarker: options?.cloneSourceMarker !== void 0 ? options.cloneSourceMarker : options?.brushCursor ? this.cloneSourceMarker(options.brushCursor) : null,
7004
7105
  dirty: dirty === "all" ? void 0 : dirty,
7005
7106
  checkpointLayerId: this.state.activeLayerId
7006
7107
  });
@@ -7841,7 +7942,8 @@ var ImageEditorController = class {
7841
7942
  warnings.push(`Adjustment layers were written with their RGB settings; in a ${m.mode.toUpperCase()} file Photoshop applies them to its own channels, so they may look different there.`);
7842
7943
  }
7843
7944
  const planes = colorModePlanes(this.state.colorProfile, m);
7844
- return m.mode === "cmyk" ? { colorMode: "cmyk", colorPlanes: planes, iccProfile: profileBytes(m.profile) } : { colorMode: "lab", colorPlanes: planes };
7945
+ const planes16 = this.bitDepth === 8 ? void 0 : colorModePlanes16(this.state.colorProfile, m);
7946
+ return m.mode === "cmyk" ? { colorMode: "cmyk", colorPlanes: planes, colorPlanes16: planes16, iccProfile: profileBytes(m.profile) } : { colorMode: "lab", colorPlanes: planes, colorPlanes16: planes16 };
7845
7947
  }
7846
7948
  /** Pixels per inch of the last opened PSD (written back by `toPsd`). */
7847
7949
  psdResolution;
@@ -8054,8 +8156,8 @@ var ImageEditorController = class {
8054
8156
  for (const [msg, names] of note) warnings.push(`${msg} (${names.length === 1 ? "layer" : "layers"} ${names.map((n) => `"${n}"`).join(", ")}).`);
8055
8157
  const merged = compositor.composite(prepared, canvas, { ...sources, version: `psd:${Date.now()}` });
8056
8158
  const color = this.psdColorOptions(warnings);
8057
- if (color.colorMode && this.bitDepth !== 8) warnings.push(`${color.colorMode.toUpperCase()} files are written at 8 bits per channel; the ${this.bitDepth}-bit samples were rounded.`);
8058
- const depth = color.colorMode ? 8 : this.bitDepth;
8159
+ if (color.colorMode && this.bitDepth === 32) warnings.push(`${color.colorMode.toUpperCase()} files are written at 16 bits per channel at most; the 32-bit samples were converted (colour above 1 clipped).`);
8160
+ const depth = color.colorMode && this.bitDepth === 32 ? 16 : this.bitDepth;
8059
8161
  const mergedDeep = depth === 8 ? void 0 : workingToSamples(compositeDeep(prepared, canvas, sources, depth), depth);
8060
8162
  const result = exportPsd({ width, height, layers: prepared, merged, mergedDeep, resolution: this.psdResolution, patterns: this.state.patterns, globalLight: this.state.globalLight, depth, ...color });
8061
8163
  warnings.push(...result.warnings);
@@ -10605,13 +10707,17 @@ var PAINT_TOOLS = [
10605
10707
  { id: "smudge", label: "Smudge", shortcut: "" },
10606
10708
  { id: "dodge", label: "Dodge", shortcut: "O" },
10607
10709
  { id: "burn", label: "Burn", shortcut: "Shift+O" },
10608
- { id: "sponge", label: "Sponge", shortcut: "Shift+O" }
10710
+ { id: "sponge", label: "Sponge", shortcut: "Shift+O" },
10711
+ { id: "blur", label: "Blur", shortcut: "" },
10712
+ { id: "sharpen", label: "Sharpen", shortcut: "" },
10713
+ { id: "history", label: "History", shortcut: "Y" }
10609
10714
  ];
10610
10715
  var isPaintTool = (tool) => PAINT_TOOLS.some((t) => t.id === tool);
10611
- var isRetouchPaintTool = (tool) => tool === "clone" || tool === "heal" || tool === "spot-heal" || tool === "smudge" || tool === "dodge" || tool === "burn" || tool === "sponge";
10716
+ var isRetouchPaintTool = (tool) => tool === "clone" || tool === "heal" || tool === "spot-heal" || tool === "smudge" || tool === "dodge" || tool === "burn" || tool === "sponge" || tool === "blur" || tool === "sharpen" || tool === "history";
10612
10717
  var RETOUCH_OPTION_CONTROLS = [
10613
10718
  { id: "exposure", label: "Exposure", section: "layer", min: 0, max: 1, step: 0.01, default: 0.5, decimals: 0, track: plain2, displayScale: 100, unit: "%", tools: ["dodge", "burn"] },
10614
- { id: "strength", label: "Strength", section: "layer", min: 0, max: 1, step: 0.01, default: 0.5, decimals: 0, track: plain2, displayScale: 100, unit: "%", tools: ["smudge"] }
10719
+ { id: "strength", label: "Strength", section: "layer", min: 0, max: 1, step: 0.01, default: 0.5, decimals: 0, track: plain2, displayScale: 100, unit: "%", tools: ["smudge", "blur", "sharpen"] },
10720
+ { id: "historySteps", label: "Steps back", section: "layer", min: 0, max: 50, step: 1, default: 0, decimals: 0, track: plain2, unit: "", tools: ["history"] }
10615
10721
  ];
10616
10722
  var RETOUCH_CHOICES = [
10617
10723
  {
@@ -10630,6 +10736,7 @@ var RETOUCH_CHOICES = [
10630
10736
  tools: ["spot-heal"],
10631
10737
  options: [
10632
10738
  { id: "proximity-match", label: "Proximity Match", title: "Copy the best matching patch nearby" },
10739
+ { id: "content-aware", label: "Content-Aware", title: "Synthesise texture and edges from patches around the spot" },
10633
10740
  { id: "smooth", label: "Smooth", title: "Fill smoothly from the edges" }
10634
10741
  ]
10635
10742
  },
@@ -10702,6 +10809,9 @@ function handlePaintShortcut(controller, e) {
10702
10809
  controller.setTool(next);
10703
10810
  return true;
10704
10811
  }
10812
+ case "y":
10813
+ controller.setTool("history");
10814
+ return true;
10705
10815
  case "d":
10706
10816
  controller.resetPaintColors();
10707
10817
  return true;
@@ -11145,7 +11255,7 @@ function createImageColorTools(controller) {
11145
11255
  const setMode = {
11146
11256
  name: "set_color_mode",
11147
11257
  title: "Set the colour mode",
11148
- description: "Image \u203A Mode: rgb, cmyk (for print: give the press `profile`, default coated-cmyk, and the `intent` RGB is separated with) or lab. The document is still edited as RGB in its working space; a CMYK document is soft-proofed through its profile in the viewport (unless set_proof_setup says otherwise), and export_psd writes CMYK or Lab channels with the profile. Changes no pixels. One undo step. Returns the profile summary with mode and cmykProfile.",
11258
+ description: "Image \u203A Mode: rgb, cmyk (for print: give the press `profile`, default coated-cmyk, and the `intent` RGB is separated with) or lab. The document is still edited as RGB in its working space; a CMYK document is soft-proofed through its profile in the viewport (unless set_proof_setup says otherwise), and export_psd writes CMYK or Lab channels with the profile, at 8 or 16 bits (a 32-bit document is written at 16). Changes no pixels. One undo step. Returns the profile summary with mode and cmykProfile.",
11149
11259
  inputSchema: {
11150
11260
  type: "object",
11151
11261
  properties: {
@@ -11286,12 +11396,12 @@ function createImageRetouchTools(controller) {
11286
11396
  const spotHeal = {
11287
11397
  name: "spot_heal",
11288
11398
  title: "Spot Healing stroke",
11289
- description: "Removes what a stroke covers without a source point (Photoshop's Spot Healing Brush): proximity-match copies the best matching patch nearby and blends it in; smooth fills from the edges inwards (for spots on smooth areas). Paint just over the spot with a brush a little larger than it. One undo step. Returns the layerId, channel, changed bounds, dab count and spotHeal {offset} (the patch offset copied, or null for a smooth fill).",
11399
+ description: "Removes what a stroke covers without a source point (Photoshop's Spot Healing Brush): proximity-match copies the best matching patch nearby and blends it in; content-aware synthesises texture and continues edges from patches all around (best across lines, on patterns and for larger spots, a little slower); smooth fills from the edges inwards (for spots on smooth areas). Paint just over the spot with a brush a little larger than it. One undo step. Returns the layerId, channel, changed bounds, dab count and spotHeal {offset} (the patch offset copied, or null for a smooth fill).",
11290
11400
  inputSchema: {
11291
11401
  type: "object",
11292
11402
  properties: {
11293
11403
  points: POINTS,
11294
- type: { type: "string", enum: [...SPOT_HEAL_TYPES], description: "proximity-match (default) or smooth." },
11404
+ type: { type: "string", enum: [...SPOT_HEAL_TYPES], description: "proximity-match (default the current option), content-aware or smooth." },
11295
11405
  sample: SAMPLE,
11296
11406
  brush: BRUSH2,
11297
11407
  ...TARGET2
@@ -11368,10 +11478,55 @@ function createImageRetouchTools(controller) {
11368
11478
  annotations: { title: "Sponge stroke", ...WRITE5 },
11369
11479
  handler: (args) => stroke("sponge", { ...args, channel: "pixels" }, { ...pick(args, ["vibrance"]), ...args.mode !== void 0 ? { spongeMode: args.mode } : {} })
11370
11480
  };
11481
+ const filterSchema = (verb) => ({
11482
+ type: "object",
11483
+ properties: {
11484
+ points: POINTS,
11485
+ strength: { type: "number", minimum: 0, maximum: 1, description: `How strongly to ${verb}, 0..1 (default the current option, 0.5); multiplies the brush opacity.` },
11486
+ brush: BRUSH2,
11487
+ ...TARGET2
11488
+ },
11489
+ required: ["points"],
11490
+ additionalProperties: false
11491
+ });
11492
+ const blurTool = {
11493
+ name: "blur_stroke",
11494
+ title: "Blur stroke",
11495
+ description: "Softens the pixels a stroke covers (Photoshop's Blur tool): a soft blur of the layer laid through the brush at `strength`. Overlapping dabs in one stroke do not compound; another stroke blurs further. Works on pixels and masks, at any bit depth. One undo step. Returns the layerId, channel, changed bounds and dab count.",
11496
+ inputSchema: filterSchema("blur"),
11497
+ annotations: { title: "Blur stroke", ...WRITE5 },
11498
+ handler: (args) => stroke("blur", args, pick(args, ["strength"]))
11499
+ };
11500
+ const sharpenTool = {
11501
+ name: "sharpen_stroke",
11502
+ title: "Sharpen stroke",
11503
+ description: "Crisps the pixels a stroke covers (Photoshop's Sharpen tool): an unsharp mask of the layer laid through the brush at `strength`. Keep strength low (0.2..0.5) and inspect with capture_view; strong sharpening shows halos and noise. One undo step. Returns the layerId, channel, changed bounds and dab count.",
11504
+ inputSchema: filterSchema("sharpen"),
11505
+ annotations: { title: "Sharpen stroke", ...WRITE5 },
11506
+ handler: (args) => stroke("sharpen", args, pick(args, ["strength"]))
11507
+ };
11508
+ const historyBrush = {
11509
+ name: "history_brush",
11510
+ title: "History Brush stroke",
11511
+ description: "Paints the layer back as it was earlier (Photoshop's History Brush): the same layer's pixels (or mask) from `historySteps` undo steps back, or from the oldest state history keeps (0, the default, usually the document as opened), laid through the brush. Use it to undo part of a filter, paint or retouch in one area. Fails if the layer did not exist then or there is no history yet. One undo step. Returns the layerId, channel, changed bounds and dab count.",
11512
+ inputSchema: {
11513
+ type: "object",
11514
+ properties: {
11515
+ points: POINTS,
11516
+ historySteps: { type: "integer", minimum: 0, maximum: 1e3, description: "Undo steps back to paint from; 0 (default the current option) is the oldest state history keeps." },
11517
+ brush: BRUSH2,
11518
+ ...TARGET2
11519
+ },
11520
+ required: ["points"],
11521
+ additionalProperties: false
11522
+ },
11523
+ annotations: { title: "History Brush stroke", ...WRITE5 },
11524
+ handler: (args) => stroke("history", args, pick(args, ["historySteps"]))
11525
+ };
11371
11526
  const getOptions = {
11372
11527
  name: "get_retouch_options",
11373
11528
  title: "Get retouch options",
11374
- description: "Reads the retouching tools' options (aligned, sample, spotType, range, exposure, protectTones, spongeMode, vibrance, strength) and the clone source ({x, y, offset} or null; offset is the aligned offset the last clone or heal stroke fixed). Call it before clone_stamp or heal_brush without a source to see where they will sample.",
11529
+ description: "Reads the retouching tools' options (aligned, sample, spotType, range, exposure, protectTones, spongeMode, vibrance, strength, historySteps) and the clone source ({x, y, offset} or null; offset is the aligned offset the last clone or heal stroke fixed: an aligned stroke samples at the brush plus offset, which the editors show as a crosshair). Call it before clone_stamp or heal_brush without a source to see where they will sample.",
11375
11530
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
11376
11531
  annotations: { title: "Get retouch options", ...READ6 },
11377
11532
  handler: () => {
@@ -11388,13 +11543,14 @@ function createImageRetouchTools(controller) {
11388
11543
  properties: {
11389
11544
  aligned: { type: "boolean", description: "Clone / heal: keep the offset from stroke to stroke (default true)." },
11390
11545
  sample: SAMPLE,
11391
- spotType: { type: "string", enum: [...SPOT_HEAL_TYPES], description: "Spot heal: proximity-match (default) or smooth." },
11546
+ spotType: { type: "string", enum: [...SPOT_HEAL_TYPES], description: "Spot heal: proximity-match (default), content-aware or smooth." },
11392
11547
  range: { type: "string", enum: [...TONE_RANGES], description: "Dodge / burn: shadows, midtones (default) or highlights." },
11393
11548
  exposure: { type: "number", minimum: 0, maximum: 1, description: "Dodge / burn strength 0..1 (default 0.5)." },
11394
11549
  protectTones: { type: "boolean", description: "Dodge / burn: keep hue and saturation (default true)." },
11395
11550
  spongeMode: { type: "string", enum: ["desaturate", "saturate"], description: "Sponge: desaturate (default) or saturate." },
11396
11551
  vibrance: { type: "boolean", description: "Sponge: damp clipping (default true)." },
11397
- strength: { type: "number", minimum: 0, maximum: 1, description: "Smudge strength 0..1 (default 0.5)." }
11552
+ strength: { type: "number", minimum: 0, maximum: 1, description: "Smudge, blur and sharpen strength 0..1 (default 0.5)." },
11553
+ historySteps: { type: "integer", minimum: 0, maximum: 1e3, description: "History Brush: undo steps back it paints from; 0 is the oldest state (default 0)." }
11398
11554
  },
11399
11555
  additionalProperties: false
11400
11556
  },
@@ -11424,7 +11580,7 @@ function createImageRetouchTools(controller) {
11424
11580
  return controller.setCloneSource(checkSource(controller, { x: args.x, y: args.y }));
11425
11581
  }
11426
11582
  };
11427
- return [cloneStamp, healBrush, spotHeal, smudge, dodge, burn, spongeTool, getOptions, setOptions, setSource];
11583
+ return [cloneStamp, healBrush, spotHeal, smudge, dodge, burn, spongeTool, blurTool, sharpenTool, historyBrush, getOptions, setOptions, setSource];
11428
11584
  }
11429
11585
  function pick(args, keys) {
11430
11586
  const out = {};
@@ -15085,6 +15241,7 @@ export {
15085
15241
  applyHomography,
15086
15242
  applyImageProjectionToContext,
15087
15243
  applyLegacyAdjustments,
15244
+ applyLegacyFloat,
15088
15245
  applyMatrix3,
15089
15246
  applyOutputStage,
15090
15247
  applyParams,
@@ -15113,6 +15270,7 @@ export {
15113
15270
  blenderDab,
15114
15271
  blurFilter,
15115
15272
  borderCoverage,
15273
+ boxBlurPixels,
15116
15274
  boxMean,
15117
15275
  boxPointOf,
15118
15276
  boxQuad,
@@ -15143,6 +15301,7 @@ export {
15143
15301
  collectTiles,
15144
15302
  colorBalanceChannel,
15145
15303
  colorModePlanes,
15304
+ colorModePlanes16,
15146
15305
  colorPanelView,
15147
15306
  colorRange,
15148
15307
  combineSelections,
@@ -15152,6 +15311,7 @@ export {
15152
15311
  compositeSurfaceOver,
15153
15312
  computeHistogram,
15154
15313
  computeSelectionBounds,
15314
+ contentAwareFill,
15155
15315
  contentBounds,
15156
15316
  contentsBox,
15157
15317
  contentsSurface,
@@ -15592,6 +15752,7 @@ export {
15592
15752
  resolvePaintGradient,
15593
15753
  resolveProfile,
15594
15754
  restyleRange,
15755
+ retouchFilter,
15595
15756
  reversePaintGradient,
15596
15757
  rgbToGray,
15597
15758
  rgbToHsl,