@pixerate/editor 0.133.0 → 0.135.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 (77) 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-DL2wPsFf.d.cts → bezier-J8i4uqSH.d.cts} +1 -1
  6. package/dist/{bezier-BAGdL89w.d.ts → bezier-lNEOp0Jx.d.ts} +1 -1
  7. package/dist/canvas/index.d.cts +7 -7
  8. package/dist/canvas/index.d.ts +7 -7
  9. package/dist/{chunk-VGZSIXX3.js → chunk-7RJ6P7MI.js} +2 -2
  10. package/dist/{chunk-AOCFL7L7.js → chunk-ESIWZMMK.js} +2 -2
  11. package/dist/{chunk-FOBB6AIO.js → chunk-HHTCDFZC.js} +97 -5
  12. package/dist/chunk-HHTCDFZC.js.map +1 -0
  13. package/dist/{chunk-6CTZAGZ2.js → chunk-ILYFOJUN.js} +56 -1
  14. package/dist/chunk-ILYFOJUN.js.map +1 -0
  15. package/dist/{chunk-2QKUD3E2.cjs → chunk-T6TV6HY6.cjs} +36 -36
  16. package/dist/{chunk-2QKUD3E2.cjs.map → chunk-T6TV6HY6.cjs.map} +1 -1
  17. package/dist/{chunk-65GNHOD4.cjs → chunk-YAFS2QFZ.cjs} +82 -82
  18. package/dist/{chunk-65GNHOD4.cjs.map → chunk-YAFS2QFZ.cjs.map} +1 -1
  19. package/dist/{chunk-NVTHUPJ6.cjs → chunk-YT3JL7YM.cjs} +71 -16
  20. package/dist/chunk-YT3JL7YM.cjs.map +1 -0
  21. package/dist/{chunk-F7GGEAO2.cjs → chunk-Z2H7KECK.cjs} +151 -59
  22. package/dist/chunk-Z2H7KECK.cjs.map +1 -0
  23. package/dist/effects/index.d.cts +4 -4
  24. package/dist/effects/index.d.ts +4 -4
  25. package/dist/{export-BICOEM9d.d.cts → export-AZSwX087.d.cts} +13 -5
  26. package/dist/{export-XnuX8FZ2.d.ts → export-BDkyDNM8.d.ts} +13 -5
  27. package/dist/{export-zB4HITwP.d.ts → export-DMeG6E06.d.ts} +1 -1
  28. package/dist/{export-CeFplzAW.d.cts → export-KzubBKo7.d.cts} +1 -1
  29. package/dist/{helpers-cssRNUzH.d.ts → helpers-Coa4e2j_.d.ts} +12 -0
  30. package/dist/{helpers-BuKdqUlM.d.cts → helpers-DHl28yLO.d.cts} +12 -0
  31. package/dist/image-editor/index.cjs +739 -647
  32. package/dist/image-editor/index.cjs.map +1 -1
  33. package/dist/image-editor/index.d.cts +26 -11
  34. package/dist/image-editor/index.d.ts +26 -11
  35. package/dist/image-editor/index.js +104 -12
  36. package/dist/image-editor/index.js.map +1 -1
  37. package/dist/{opentype-C3iyIFRx.d.cts → opentype-BWR7Qv8W.d.cts} +1 -1
  38. package/dist/{opentype-DG5Lkwul.d.ts → opentype-Dfcx6wFx.d.ts} +1 -1
  39. package/dist/paint/index.cjs +10 -2
  40. package/dist/paint/index.cjs.map +1 -1
  41. package/dist/paint/index.d.cts +2 -2
  42. package/dist/paint/index.d.ts +2 -2
  43. package/dist/paint/index.js +9 -1
  44. package/dist/paint-editor/index.cjs +7 -7
  45. package/dist/paint-editor/index.d.cts +7 -7
  46. package/dist/paint-editor/index.d.ts +7 -7
  47. package/dist/paint-editor/index.js +2 -2
  48. package/dist/{pathfinder-DKLt4UDG.d.ts → pathfinder-Celbi3e6.d.ts} +1 -1
  49. package/dist/{pathfinder-8vXik95z.d.cts → pathfinder-D8KQ6blQ.d.cts} +1 -1
  50. package/dist/{pool-C69pkWRz.d.ts → pool-8JZGWHvR.d.ts} +1 -1
  51. package/dist/{pool-VAWcYUbs.d.cts → pool-BfuxBKTd.d.cts} +1 -1
  52. package/dist/{rand-BaqEOs1Y.d.ts → rand-BszBST4s.d.ts} +31 -1
  53. package/dist/{rand-DdpFzCo6.d.cts → rand-DOH_aMvm.d.cts} +31 -1
  54. package/dist/{svg-CEYpvsgH.d.ts → svg-CxxqG3_W.d.ts} +2 -2
  55. package/dist/{svg-BThlZmja.d.cts → svg-pwXWq6s2.d.cts} +2 -2
  56. package/dist/{tools-CRnZJXXr.d.cts → tools-3EBjR91b.d.cts} +1 -1
  57. package/dist/{tools-CVGqEpO8.d.ts → tools-y1M6O3UT.d.ts} +1 -1
  58. package/dist/vector/index.cjs +1292 -1035
  59. package/dist/vector/index.cjs.map +1 -1
  60. package/dist/vector/index.d.cts +183 -91
  61. package/dist/vector/index.d.ts +183 -91
  62. package/dist/vector/index.js +267 -10
  63. package/dist/vector/index.js.map +1 -1
  64. package/dist/video-editor/index.d.cts +4 -4
  65. package/dist/video-editor/index.d.ts +4 -4
  66. package/dist/{warp-mKylbLiF.d.ts → warp-C1gwu2G_.d.ts} +1 -1
  67. package/dist/{warp-ChcFDdlT.d.cts → warp-Ceqdz7kj.d.cts} +1 -1
  68. package/package.json +1 -1
  69. package/skills/pixerate-image-editor/SKILL.md +4 -4
  70. package/skills/pixerate-vector/SKILL.md +18 -54
  71. package/skills/pixerate-vector/references/pitfalls.md +62 -0
  72. package/dist/chunk-6CTZAGZ2.js.map +0 -1
  73. package/dist/chunk-F7GGEAO2.cjs.map +0 -1
  74. package/dist/chunk-FOBB6AIO.js.map +0 -1
  75. package/dist/chunk-NVTHUPJ6.cjs.map +0 -1
  76. /package/dist/{chunk-VGZSIXX3.js.map → chunk-7RJ6P7MI.js.map} +0 -0
  77. /package/dist/{chunk-AOCFL7L7.js.map → chunk-ESIWZMMK.js.map} +0 -0
@@ -363,7 +363,7 @@ import {
363
363
  writePsd,
364
364
  xyToTempTint,
365
365
  xyToUv
366
- } from "../chunk-AOCFL7L7.js";
366
+ } from "../chunk-ESIWZMMK.js";
367
367
  import {
368
368
  parseCssColor
369
369
  } from "../chunk-CTTF4KYV.js";
@@ -439,21 +439,25 @@ import {
439
439
  } from "../chunk-GX332W2C.js";
440
440
  import {
441
441
  DEFAULT_RETOUCH,
442
+ IDENTITY_CLONE_TRANSFORM,
442
443
  RETOUCH_SAMPLES,
443
444
  RegionPixelSource,
444
445
  RetouchDabStroke,
445
446
  SPOT_HEAL_TYPES,
446
447
  TONE_RANGES,
447
448
  TilePixelSource,
449
+ TransformedPixelSource,
448
450
  applyRetouchToTile,
449
451
  applyToneToDeepTile,
450
452
  bestOffset,
451
453
  boxBlurPixels,
454
+ cloneSamplePoint,
452
455
  contentAwareFill,
453
456
  dilateMask,
454
457
  dodgeBurn,
455
458
  dodgeBurnCurve,
456
459
  healPixels,
460
+ isIdentityCloneTransform,
457
461
  luma,
458
462
  maskBounds,
459
463
  membraneFill,
@@ -471,7 +475,7 @@ import {
471
475
  spotHealPixels,
472
476
  spotHealWindow,
473
477
  toneOf
474
- } from "../chunk-6CTZAGZ2.js";
478
+ } from "../chunk-ILYFOJUN.js";
475
479
  import {
476
480
  BrushstrokeCache,
477
481
  brushstrokeDataUrl,
@@ -806,7 +810,7 @@ import {
806
810
  warpMeshParamAt,
807
811
  warpMeshRows,
808
812
  warpPresetMesh
809
- } from "../chunk-FOBB6AIO.js";
813
+ } from "../chunk-HHTCDFZC.js";
810
814
  import "../chunk-YGJIER2R.js";
811
815
  import "../chunk-KHNZ6MOU.js";
812
816
  import {
@@ -4180,7 +4184,7 @@ var ImageEditorController = class {
4180
4184
  activeLayerId: null,
4181
4185
  activeTarget: "pixels",
4182
4186
  outputOpacity: 1,
4183
- paint: { brush: { ...DEFAULT_BRUSH }, foreground: "#000000", background: "#ffffff", paint: { type: "color", color: "#000000" }, mixer: { ...DEFAULT_MIXER }, mixerLoad: EMPTY_MIXER_LOAD, retouch: { ...DEFAULT_RETOUCH }, cloneSource: null },
4187
+ paint: { brush: { ...DEFAULT_BRUSH }, foreground: "#000000", background: "#ffffff", paint: { type: "color", color: "#000000" }, mixer: { ...DEFAULT_MIXER }, mixerLoad: EMPTY_MIXER_LOAD, retouch: { ...DEFAULT_RETOUCH }, cloneSource: null, cloneTransform: { ...IDENTITY_CLONE_TRANSFORM } },
4184
4188
  fit: options.fit ?? "margin"
4185
4189
  };
4186
4190
  if (options.image instanceof Object && "width" in options.image) {
@@ -6038,7 +6042,8 @@ var ImageEditorController = class {
6038
6042
  mixer: options.mixer ? normalizeMixer({ ...cur.mixer, ...options.mixer }) : cur.mixer,
6039
6043
  mixerLoad: cur.mixerLoad,
6040
6044
  retouch,
6041
- cloneSource
6045
+ cloneSource,
6046
+ cloneTransform: cur.cloneTransform
6042
6047
  };
6043
6048
  this.state.paint = next;
6044
6049
  this.notify();
@@ -6079,6 +6084,24 @@ var ImageEditorController = class {
6079
6084
  this.notify();
6080
6085
  return source;
6081
6086
  }
6087
+ /**
6088
+ * The Clone Source panel's transform for the Clone Stamp and Healing Brush: `scaleX` / `scaleY`
6089
+ * (factors, 0.01..10 in size; negative flips) and `rotate` (degrees, clockwise), merged into the
6090
+ * current one; `null` resets it. View state, no undo. Returns the transform.
6091
+ */
6092
+ setCloneTransform(t) {
6093
+ const cur = this.state.paint.cloneTransform;
6094
+ const next = t ? { ...cur, ...t } : { ...IDENTITY_CLONE_TRANSFORM };
6095
+ for (const k of ["scaleX", "scaleY"]) {
6096
+ const v = next[k];
6097
+ if (!Number.isFinite(v) || Math.abs(v) < 0.01 || Math.abs(v) > 10) throw new LayerOperationError(`${k} must be a scale factor between 0.01 and 10 in size (negative flips), not ${String(v)}.`);
6098
+ }
6099
+ if (!Number.isFinite(next.rotate)) throw new LayerOperationError("rotate must be a number of degrees.");
6100
+ next.rotate = ((next.rotate + 180) % 360 + 360) % 360 - 180;
6101
+ this.state.paint = { ...this.state.paint, cloneTransform: next };
6102
+ this.notify();
6103
+ return next;
6104
+ }
6082
6105
  /** D: black foreground, white background. */
6083
6106
  resetPaintColors() {
6084
6107
  this.setPaintOptions({ foreground: "#000000", background: "#ffffff" });
@@ -6261,7 +6284,9 @@ var ImageEditorController = class {
6261
6284
  if (!cs) throw new LayerOperationError(`The ${tool === "clone" ? "Clone Stamp" : "Healing Brush"} needs a source point; Alt-click the canvas, call set_clone_source, or pass source { x, y }.`);
6262
6285
  let offset = { x: 0, y: 0 };
6263
6286
  let first = { x: 0, y: 0 };
6264
- const paint = () => ({ kind: "source", source, dx: offset.x, dy: offset.y });
6287
+ const xf = this.state.paint.cloneTransform;
6288
+ const transformed = isIdentityCloneTransform(xf) ? null : new TransformedPixelSource(source, (x, y) => cloneSamplePoint(xf, cs, { x: cs.x - offset.x, y: cs.y - offset.y }, x, y));
6289
+ const paint = () => transformed ? { kind: "source", source: transformed, dx: 0, dy: 0 } : { kind: "source", source, dx: offset.x, dy: offset.y };
6265
6290
  return {
6266
6291
  kind: "coverage",
6267
6292
  coverage,
@@ -6275,7 +6300,7 @@ var ImageEditorController = class {
6275
6300
  finalPaint: tool === "heal" ? (cov) => {
6276
6301
  if (!cov.bounds) return null;
6277
6302
  const g = pad(cov.bounds, 2);
6278
- const src = readSourceRegion(source, g, offset.x, offset.y);
6303
+ const src = transformed ? readSourceRegion(transformed, g) : readSourceRegion(source, g, offset.x, offset.y);
6279
6304
  const dst = readSourceRegion(source, g);
6280
6305
  return { kind: "source", source: new RegionPixelSource(g, healPixels(g.width, g.height, src, dst, covered(cov, g))), dx: 0, dy: 0 };
6281
6306
  } : void 0,
@@ -7088,7 +7113,10 @@ var ImageEditorController = class {
7088
7113
  if (s.activeTool !== "clone" && s.activeTool !== "heal") return null;
7089
7114
  const cs = s.paint.cloneSource;
7090
7115
  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 };
7116
+ if (cursor && cs.offset && s.paint.retouch.aligned) {
7117
+ const [x, y] = cloneSamplePoint(s.paint.cloneTransform, cs, { x: cs.x - cs.offset.x, y: cs.y - cs.offset.y }, cursor.x, cursor.y);
7118
+ return { x, y };
7119
+ }
7092
7120
  return { x: cs.x, y: cs.y };
7093
7121
  }
7094
7122
  async render(targetCanvas, options) {
@@ -10719,6 +10747,30 @@ var RETOUCH_OPTION_CONTROLS = [
10719
10747
  { 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
10748
  { id: "historySteps", label: "Steps back", section: "layer", min: 0, max: 50, step: 1, default: 0, decimals: 0, track: plain2, unit: "", tools: ["history"] }
10721
10749
  ];
10750
+ var CLONE_TRANSFORM_CONTROLS = [
10751
+ { id: "width", label: "Source W", section: "layer", min: 1, max: 400, step: 1, default: 100, decimals: 0, track: plain2, unit: "%" },
10752
+ { id: "height", label: "Source H", section: "layer", min: 1, max: 400, step: 1, default: 100, decimals: 0, track: plain2, unit: "%" },
10753
+ { id: "angle", label: "Source angle", section: "layer", min: -180, max: 180, step: 1, default: 0, decimals: 0, track: plain2, unit: "\xB0" }
10754
+ ];
10755
+ function cloneTransformView(controller) {
10756
+ const t = controller.getState().paint.cloneTransform;
10757
+ return {
10758
+ width: Math.round(Math.abs(t.scaleX) * 100),
10759
+ height: Math.round(Math.abs(t.scaleY) * 100),
10760
+ angle: t.rotate,
10761
+ flipH: t.scaleX < 0,
10762
+ flipV: t.scaleY < 0,
10763
+ identity: t.scaleX === 1 && t.scaleY === 1 && t.rotate === 0
10764
+ };
10765
+ }
10766
+ function setCloneTransformValue(controller, id, value) {
10767
+ const t = controller.getState().paint.cloneTransform;
10768
+ if (id === "width") controller.setCloneTransform({ scaleX: Math.sign(t.scaleX) * (value ?? 100) / 100 });
10769
+ else if (id === "height") controller.setCloneTransform({ scaleY: Math.sign(t.scaleY) * (value ?? 100) / 100 });
10770
+ else if (id === "angle") controller.setCloneTransform({ rotate: value ?? 0 });
10771
+ else if (id === "flipH") controller.setCloneTransform({ scaleX: -t.scaleX });
10772
+ else controller.setCloneTransform({ scaleY: -t.scaleY });
10773
+ }
10722
10774
  var RETOUCH_CHOICES = [
10723
10775
  {
10724
10776
  id: "sample",
@@ -11526,12 +11578,12 @@ function createImageRetouchTools(controller) {
11526
11578
  const getOptions = {
11527
11579
  name: "get_retouch_options",
11528
11580
  title: "Get retouch options",
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.",
11581
+ description: "Reads the retouching tools' options (aligned, sample, spotType, range, exposure, protectTones, spongeMode, vibrance, strength, historySteps), the Clone Source transform ({scaleX, scaleY, rotate}) 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.",
11530
11582
  inputSchema: { type: "object", properties: {}, additionalProperties: false },
11531
11583
  annotations: { title: "Get retouch options", ...READ6 },
11532
11584
  handler: () => {
11533
- const { retouch, cloneSource } = controller.getState().paint;
11534
- return { options: { ...retouch }, cloneSource: cloneSource ? { ...cloneSource } : null };
11585
+ const { retouch, cloneSource, cloneTransform } = controller.getState().paint;
11586
+ return { options: { ...retouch }, cloneSource: cloneSource ? { ...cloneSource } : null, cloneTransform: { ...cloneTransform } };
11535
11587
  }
11536
11588
  };
11537
11589
  const setOptions = {
@@ -11580,7 +11632,40 @@ function createImageRetouchTools(controller) {
11580
11632
  return controller.setCloneSource(checkSource(controller, { x: args.x, y: args.y }));
11581
11633
  }
11582
11634
  };
11583
- return [cloneStamp, healBrush, spotHeal, smudge, dodge, burn, spongeTool, blurTool, sharpenTool, historyBrush, getOptions, setOptions, setSource];
11635
+ const setTransform = {
11636
+ name: "set_clone_transform",
11637
+ title: "Transform the clone source",
11638
+ description: "Photoshop's Clone Source panel: the Clone Stamp and Healing Brush lay the source scaled by `scaleX` / `scaleY` (percent, default 100; negative or the flip flags mirror it) and rotated by `rotate` degrees clockwise about the stroke's start. Merged into the current transform; reset true goes back to 100 %, 0\xB0. Use it to clone a reflection, a smaller copy or a tilted patch. View state, not an undo step. Returns the transform ({scaleX, scaleY, rotate} as factors and degrees).",
11639
+ inputSchema: {
11640
+ type: "object",
11641
+ properties: {
11642
+ scaleX: { type: "number", description: "Width in percent of the source (1..1000; negative flips horizontally)." },
11643
+ scaleY: { type: "number", description: "Height in percent of the source (1..1000; negative flips vertically)." },
11644
+ rotate: { type: "number", description: "Rotation in degrees, clockwise (-180..180)." },
11645
+ flipHorizontal: { type: "boolean", description: "Mirror the source left to right (negates the width scale; default false)." },
11646
+ flipVertical: { type: "boolean", description: "Mirror the source top to bottom (negates the height scale; default false)." },
11647
+ reset: { type: "boolean", description: "Back to 100 %, no flip, 0\xB0 (default false); other arguments are not allowed with it." }
11648
+ },
11649
+ additionalProperties: false
11650
+ },
11651
+ annotations: { title: "Transform the clone source", ...WRITE5, idempotentHint: false },
11652
+ handler: (args) => {
11653
+ if (args.reset === true) {
11654
+ if (Object.keys(args).some((k) => k !== "reset")) throw new LayerOperationError("Give either reset true or transform values, not both.");
11655
+ return controller.setCloneTransform(null);
11656
+ }
11657
+ const cur = controller.getState().paint.cloneTransform;
11658
+ const next = {};
11659
+ if (args.scaleX !== void 0) next.scaleX = args.scaleX / 100;
11660
+ if (args.scaleY !== void 0) next.scaleY = args.scaleY / 100;
11661
+ if (args.rotate !== void 0) next.rotate = args.rotate;
11662
+ if (args.flipHorizontal === true) next.scaleX = -(next.scaleX ?? cur.scaleX);
11663
+ if (args.flipVertical === true) next.scaleY = -(next.scaleY ?? cur.scaleY);
11664
+ if (!Object.keys(next).length) throw new LayerOperationError("Pass scaleX, scaleY, rotate, flipHorizontal or flipVertical, or reset true.");
11665
+ return controller.setCloneTransform(next);
11666
+ }
11667
+ };
11668
+ return [cloneStamp, healBrush, spotHeal, smudge, dodge, burn, spongeTool, blurTool, sharpenTool, historyBrush, getOptions, setOptions, setSource, setTransform];
11584
11669
  }
11585
11670
  function pick(args, keys) {
11586
11671
  const out = {};
@@ -15003,6 +15088,7 @@ export {
15003
15088
  BRUSH_OPTION_CONTROLS,
15004
15089
  BUILTIN_PROFILES,
15005
15090
  BrushstrokeCache,
15091
+ CLONE_TRANSFORM_CONTROLS,
15006
15092
  COLOR_MODE_OPTIONS,
15007
15093
  CoverageMap,
15008
15094
  D50,
@@ -15056,6 +15142,7 @@ export {
15056
15142
  HIDE,
15057
15143
  HISTOGRAM_BINS,
15058
15144
  HUE_RANGE_LABELS,
15145
+ IDENTITY_CLONE_TRANSFORM,
15059
15146
  IDENTITY_MATRIX3,
15060
15147
  IMAGE_ADJUSTMENT_CONTROLS,
15061
15148
  IMAGE_CROP_PRESETS,
@@ -15190,6 +15277,7 @@ export {
15190
15277
  TilePixelSource,
15191
15278
  TiledSurface,
15192
15279
  TransformTooLargeError,
15280
+ TransformedPixelSource,
15193
15281
  TypeToolGesture,
15194
15282
  U_MAX,
15195
15283
  VIBRANCE_GREY,
@@ -15297,7 +15385,9 @@ export {
15297
15385
  clampControlValue,
15298
15386
  clampCropToImage,
15299
15387
  cleanMixer,
15388
+ cloneSamplePoint,
15300
15389
  cloneSourceLabel,
15390
+ cloneTransformView,
15301
15391
  collectTiles,
15302
15392
  colorBalanceChannel,
15303
15393
  colorModePlanes,
@@ -15516,6 +15606,7 @@ export {
15516
15606
  isContentsDecoded,
15517
15607
  isDefaultDepthMask,
15518
15608
  isGradientPaint,
15609
+ isIdentityCloneTransform,
15519
15610
  isIdentityTransform,
15520
15611
  isInterpolation,
15521
15612
  isLayerEffectKind,
@@ -15785,6 +15876,7 @@ export {
15785
15876
  serializeLayersExternal,
15786
15877
  serializePatterns,
15787
15878
  serializeSelection,
15879
+ setCloneTransformValue,
15788
15880
  setControlPath,
15789
15881
  setLayerEffects,
15790
15882
  setLayerEffectsEnabled,