@pixerate/editor 0.214.0 → 0.216.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 (40) hide show
  1. package/dist/{chunk-UOGKSVRQ.cjs → chunk-B46UQUDC.cjs} +770 -733
  2. package/dist/{chunk-W3TOZTL7.js → chunk-F7Z2MKY2.js} +115 -4
  3. package/dist/{chunk-YBJXCQTR.js → chunk-QXY4YTRI.js} +738 -701
  4. package/dist/{chunk-H65IBXFI.cjs → chunk-S55JFWDA.cjs} +350 -239
  5. package/dist/{chunk-QYLWG22N.cjs → chunk-SS53NNZO.cjs} +304 -1
  6. package/dist/{chunk-LQ5SALHK.js → chunk-U7C655AM.js} +5 -311
  7. package/dist/{chunk-L52VMQZS.cjs → chunk-UC3PWFM3.cjs} +68 -374
  8. package/dist/{chunk-L3G23N5Y.js → chunk-ZNA3IQZF.js} +304 -1
  9. package/dist/image-editor/controller.d.cts +41 -0
  10. package/dist/image-editor/controller.d.ts +41 -0
  11. package/dist/image-editor/index.cjs +1461 -1125
  12. package/dist/image-editor/index.d.cts +1 -0
  13. package/dist/image-editor/index.d.ts +1 -0
  14. package/dist/image-editor/index.js +384 -48
  15. package/dist/image-editor/layers/edge-color.d.cts +27 -0
  16. package/dist/image-editor/layers/edge-color.d.ts +27 -0
  17. package/dist/image-editor/layers/serialize.d.cts +13 -0
  18. package/dist/image-editor/layers/serialize.d.ts +13 -0
  19. package/dist/image-editor/selection/index.d.cts +1 -0
  20. package/dist/image-editor/selection/index.d.ts +1 -0
  21. package/dist/image-editor/selection/inspect.d.cts +9 -0
  22. package/dist/image-editor/selection/inspect.d.ts +9 -0
  23. package/dist/image-editor/selection/uncertainty.d.cts +40 -0
  24. package/dist/image-editor/selection/uncertainty.d.ts +40 -0
  25. package/dist/image-editor/selection-worker.cjs +4 -4
  26. package/dist/image-editor/selection-worker.js +2 -2
  27. package/dist/paint-editor/index.cjs +79 -79
  28. package/dist/paint-editor/index.js +3 -3
  29. package/dist/raster/compositor.d.cts +10 -1
  30. package/dist/raster/compositor.d.ts +10 -1
  31. package/dist/raster/deep-compositor.d.cts +1 -1
  32. package/dist/raster/deep-compositor.d.ts +1 -1
  33. package/dist/raster/index.cjs +4 -2
  34. package/dist/raster/index.js +3 -1
  35. package/dist/raster/layers.d.cts +16 -0
  36. package/dist/raster/layers.d.ts +16 -0
  37. package/package.json +1 -1
  38. package/skills/pixerate-image-editor/SKILL.md +9 -7
  39. package/skills/pixerate-image-editor/references/paths.md +12 -0
  40. package/skills/pixerate-image-selection-masking/SKILL.md +11 -11
@@ -724,6 +724,298 @@ function modifySelection(mask, op, radius) {
724
724
  }
725
725
  }
726
726
 
727
+ // src/image-editor/selection/refine.ts
728
+ var DEFAULT_REFINE_EDGE_PARAMS = {
729
+ radius: 0,
730
+ smartRadius: false,
731
+ smooth: 0,
732
+ feather: 0,
733
+ contrast: 0,
734
+ shiftEdge: 0
735
+ };
736
+ var GUIDED_FILTER_EPS = 1e-4;
737
+ var fin = (v, d) => typeof v === "number" && Number.isFinite(v) ? v : d;
738
+ function normalizeRefineEdgeParams(p = {}) {
739
+ return {
740
+ radius: Math.min(250, Math.max(0, fin(p.radius, 0))),
741
+ smartRadius: p.smartRadius === true,
742
+ smooth: Math.min(100, Math.max(0, fin(p.smooth, 0))),
743
+ feather: Math.min(1e3, Math.max(0, fin(p.feather, 0))),
744
+ contrast: Math.min(100, Math.max(0, fin(p.contrast, 0))),
745
+ shiftEdge: Math.min(100, Math.max(-100, fin(p.shiftEdge, 0)))
746
+ };
747
+ }
748
+ var gfRadius = (p) => Math.min(64, Math.max(1, Math.ceil(p.radius)));
749
+ var smoothSigma = (p) => p.smooth / 100 * 4;
750
+ var featherSigma = (p) => p.feather / 2;
751
+ function shiftAmount(p) {
752
+ const px = p.shiftEdge / 100 * Math.max(p.radius, 4);
753
+ return Number.isFinite(px) ? Math.min(64, Math.max(-64, px)) : 0;
754
+ }
755
+ function refineEdgeHalo(params = {}) {
756
+ const p = normalizeRefineEdgeParams(params);
757
+ const g = gfRadius(p);
758
+ const edge = p.radius > 0 ? Math.max(Math.ceil(p.radius), 2 * g + (p.smartRadius ? g + 1 : 0)) : 0;
759
+ return edge + Math.ceil(3 * smoothSigma(p)) + Math.ceil(3 * featherSigma(p)) + Math.ceil(Math.abs(shiftAmount(p))) + 2;
760
+ }
761
+ function boxMean(src, w, h, r) {
762
+ const tmp = new Float32Array(w * h);
763
+ const pre = new Float64Array(Math.max(w, h) + 1);
764
+ for (let y = 0; y < h; y++) {
765
+ const row = y * w;
766
+ for (let x = 0; x < w; x++) pre[x + 1] = pre[x] + src[row + x];
767
+ for (let x = 0; x < w; x++) {
768
+ const lo = Math.max(0, x - r);
769
+ const hi = Math.min(w, x + r + 1);
770
+ tmp[row + x] = (pre[hi] - pre[lo]) / (hi - lo);
771
+ }
772
+ }
773
+ const out = new Float32Array(w * h);
774
+ for (let x = 0; x < w; x++) {
775
+ for (let y = 0; y < h; y++) pre[y + 1] = pre[y] + tmp[y * w + x];
776
+ for (let y = 0; y < h; y++) {
777
+ const lo = Math.max(0, y - r);
778
+ const hi = Math.min(h, y + r + 1);
779
+ out[y * w + x] = (pre[hi] - pre[lo]) / (hi - lo);
780
+ }
781
+ }
782
+ return out;
783
+ }
784
+ function guidedFilterColor(guide, p, w, h, r, eps = GUIDED_FILTER_EPS) {
785
+ const n = w * h;
786
+ const [c0, c1, c2] = guide;
787
+ const prod = (a, b2) => {
788
+ const o = new Float32Array(n);
789
+ for (let i = 0; i < n; i++) o[i] = a[i] * b2[i];
790
+ return o;
791
+ };
792
+ const m0 = boxMean(c0, w, h, r);
793
+ const m1 = boxMean(c1, w, h, r);
794
+ const m2 = boxMean(c2, w, h, r);
795
+ const mp = boxMean(p, w, h, r);
796
+ const mip0 = boxMean(prod(c0, p), w, h, r);
797
+ const mip1 = boxMean(prod(c1, p), w, h, r);
798
+ const mip2 = boxMean(prod(c2, p), w, h, r);
799
+ const s00 = boxMean(prod(c0, c0), w, h, r);
800
+ const s01 = boxMean(prod(c0, c1), w, h, r);
801
+ const s02 = boxMean(prod(c0, c2), w, h, r);
802
+ const s11 = boxMean(prod(c1, c1), w, h, r);
803
+ const s12 = boxMean(prod(c1, c2), w, h, r);
804
+ const s22 = boxMean(prod(c2, c2), w, h, r);
805
+ const a0 = new Float32Array(n);
806
+ const a1 = new Float32Array(n);
807
+ const a2 = new Float32Array(n);
808
+ const b = new Float32Array(n);
809
+ for (let i = 0; i < n; i++) {
810
+ const u0 = m0[i], u1 = m1[i], u2 = m2[i], up = mp[i];
811
+ const rr = s00[i] - u0 * u0 + eps;
812
+ const rg = s01[i] - u0 * u1;
813
+ const rb = s02[i] - u0 * u2;
814
+ const gg = s11[i] - u1 * u1 + eps;
815
+ const gb = s12[i] - u1 * u2;
816
+ const bb = s22[i] - u2 * u2 + eps;
817
+ const v0 = mip0[i] - u0 * up;
818
+ const v1 = mip1[i] - u1 * up;
819
+ const v2 = mip2[i] - u2 * up;
820
+ const i00 = gg * bb - gb * gb;
821
+ const i01 = gb * rb - rg * bb;
822
+ const i02 = rg * gb - gg * rb;
823
+ const i11 = rr * bb - rb * rb;
824
+ const i12 = rb * rg - rr * gb;
825
+ const i22 = rr * gg - rg * rg;
826
+ const det = rr * i00 + rg * i01 + rb * i02;
827
+ let x0 = 0, x1 = 0, x2 = 0;
828
+ if (Math.abs(det) >= 1e-30) {
829
+ x0 = (i00 * v0 + i01 * v1 + i02 * v2) / det;
830
+ x1 = (i01 * v0 + i11 * v1 + i12 * v2) / det;
831
+ x2 = (i02 * v0 + i12 * v1 + i22 * v2) / det;
832
+ }
833
+ a0[i] = x0;
834
+ a1[i] = x1;
835
+ a2[i] = x2;
836
+ b[i] = up - x0 * u0 - x1 * u1 - x2 * u2;
837
+ }
838
+ const ma0 = boxMean(a0, w, h, r);
839
+ const ma1 = boxMean(a1, w, h, r);
840
+ const ma2 = boxMean(a2, w, h, r);
841
+ const mb = boxMean(b, w, h, r);
842
+ const out = new Float32Array(n);
843
+ for (let i = 0; i < n; i++) out[i] = ma0[i] * c0[i] + ma1[i] * c1[i] + ma2[i] * c2[i] + mb[i];
844
+ return out;
845
+ }
846
+ function edgeWidth(guide, w, h, r) {
847
+ const n = w * h;
848
+ const y = new Float32Array(n);
849
+ for (let i = 0; i < n; i++) y[i] = 0.299 * guide[0][i] + 0.587 * guide[1][i] + 0.114 * guide[2][i];
850
+ const g = new Float32Array(n);
851
+ for (let yy = 0; yy < h; yy++) {
852
+ for (let xx = 0; xx < w; xx++) {
853
+ const gx = (y[yy * w + Math.min(w - 1, xx + 1)] - y[yy * w + Math.max(0, xx - 1)]) / 2;
854
+ const gy = (y[Math.min(h - 1, yy + 1) * w + xx] - y[Math.max(0, yy - 1) * w + xx]) / 2;
855
+ g[yy * w + xx] = Math.sqrt(gx * gx + gy * gy);
856
+ }
857
+ }
858
+ const gm = greyMorphology(g, w, h, r, true);
859
+ const hi = greyMorphology(y, w, h, r, true);
860
+ const lo = greyMorphology(y, w, h, r, false);
861
+ const out = new Float32Array(n);
862
+ for (let i = 0; i < n; i++) out[i] = (hi[i] - lo[i]) / Math.max(gm[i], 1e-4);
863
+ return out;
864
+ }
865
+ function refineCoverage(guide, m, w, h, params = {}, band = null) {
866
+ const p = normalizeRefineEdgeParams(params);
867
+ let a = Float32Array.from(m);
868
+ if ((p.radius > 0 || band) && guide) {
869
+ const n = w * h;
870
+ const boundary = new Uint8Array(n);
871
+ let any = false;
872
+ for (let y = 0; y < h; y++) {
873
+ for (let x = 0; x < w; x++) {
874
+ const i = y * w + x;
875
+ const v = m[i] >= 0.5;
876
+ const diff = x > 0 && m[i - 1] >= 0.5 !== v || x + 1 < w && m[i + 1] >= 0.5 !== v || y > 0 && m[i - w] >= 0.5 !== v || y + 1 < h && m[i + w] >= 0.5 !== v;
877
+ if (diff) {
878
+ boundary[i] = 1;
879
+ any = true;
880
+ }
881
+ }
882
+ }
883
+ if (any || band) {
884
+ const dist = distanceTransform(boundary, w, h);
885
+ const r = band ? Math.max(4, gfRadius(p)) : gfRadius(p);
886
+ const q = guidedFilterColor(guide, m, w, h, r);
887
+ const widths = p.smartRadius ? edgeWidth(guide, w, h, r) : null;
888
+ for (let i = 0; i < n; i++) {
889
+ const reff = widths ? Math.min(Math.max(p.radius, 1.5), Math.max(1.5, widths[i])) : p.radius;
890
+ const edge = p.radius > 0 ? Math.min(1, Math.max(0, reff + 1 - dist[i])) : 0;
891
+ const wgt = band ? Math.max(edge, Math.min(1, Math.max(0, band[i]))) : edge;
892
+ if (wgt > 0) {
893
+ const qi = Math.min(1, Math.max(0, q[i]));
894
+ a[i] = m[i] + (qi - m[i]) * wgt;
895
+ }
896
+ }
897
+ }
898
+ }
899
+ const ss = smoothSigma(p);
900
+ if (ss >= 0.3) {
901
+ a = gaussianBlur(a, w, h, ss, "clamp");
902
+ const gain = 1 + ss;
903
+ for (let i = 0; i < a.length; i++) a[i] = Math.min(1, Math.max(0, (a[i] - 0.5) * gain + 0.5));
904
+ }
905
+ a = gaussianBlur(a, w, h, featherSigma(p), "clamp");
906
+ if (p.contrast > 0) {
907
+ const gain = 1 / (1 - 0.99 * p.contrast / 100);
908
+ for (let i = 0; i < a.length; i++) a[i] = Math.min(1, Math.max(0, (a[i] - 0.5) * gain + 0.5));
909
+ }
910
+ const amount = shiftAmount(p);
911
+ if (Math.abs(amount) >= 1e-4) a = shiftCoverage(a, w, h, amount);
912
+ return a;
913
+ }
914
+ function guidePlanes(pixels, win) {
915
+ const box = win ?? { x: 0, y: 0, width: pixels.width, height: pixels.height };
916
+ const n = box.width * box.height;
917
+ const r = new Float32Array(n);
918
+ const g = new Float32Array(n);
919
+ const b = new Float32Array(n);
920
+ const d = pixels.data;
921
+ for (let y = 0; y < box.height; y++) {
922
+ for (let x = 0; x < box.width; x++) {
923
+ const i = y * box.width + x;
924
+ const p = ((box.y + y) * pixels.width + box.x + x) * 4;
925
+ r[i] = d[p] / 255;
926
+ g[i] = d[p + 1] / 255;
927
+ b[i] = d[p + 2] / 255;
928
+ }
929
+ }
930
+ return [r, g, b];
931
+ }
932
+ function refineSelection(pixels, mask, params = {}, refineBrush = null) {
933
+ const p = normalizeRefineEdgeParams(params);
934
+ if (pixels && (pixels.width !== mask.width || pixels.height !== mask.height)) {
935
+ throw new Error("Refine Edge needs pixels the size of the selection");
936
+ }
937
+ if (!mask.bounds) return mask;
938
+ const brush = refineBrush?.bounds ? refineBrush : null;
939
+ const halo = refineEdgeHalo(p) + (brush ? 2 * Math.max(4, gfRadius(p)) : 0);
940
+ const b = brush ? {
941
+ x: Math.min(mask.bounds.x, brush.bounds.x),
942
+ y: Math.min(mask.bounds.y, brush.bounds.y),
943
+ r: Math.max(mask.bounds.x + mask.bounds.width, brush.bounds.x + brush.bounds.width),
944
+ btm: Math.max(mask.bounds.y + mask.bounds.height, brush.bounds.y + brush.bounds.height)
945
+ } : { x: mask.bounds.x, y: mask.bounds.y, r: mask.bounds.x + mask.bounds.width, btm: mask.bounds.y + mask.bounds.height };
946
+ const x0 = Math.max(0, b.x - halo);
947
+ const y0 = Math.max(0, b.y - halo);
948
+ const x1 = Math.min(mask.width, b.r + halo);
949
+ const y1 = Math.min(mask.height, b.btm + halo);
950
+ const ww = x1 - x0;
951
+ const wh = y1 - y0;
952
+ const m = new Float32Array(ww * wh);
953
+ for (let y = 0; y < wh; y++) {
954
+ const src = (y0 + y) * mask.width + x0;
955
+ for (let x = 0; x < ww; x++) m[y * ww + x] = mask.data[src + x] / 255;
956
+ }
957
+ const guide = pixels && (p.radius > 0 || brush) ? guidePlanes(pixels, { x: x0, y: y0, width: ww, height: wh }) : null;
958
+ let band = null;
959
+ if (brush) {
960
+ band = new Float32Array(ww * wh);
961
+ const bd = brush.data;
962
+ for (let y = 0; y < wh; y++) {
963
+ const src = (y0 + y) * mask.width + x0;
964
+ for (let x = 0; x < ww; x++) band[y * ww + x] = bd[src + x] / 255;
965
+ }
966
+ }
967
+ const refined = refineCoverage(guide, m, ww, wh, p, band);
968
+ const out = new Uint8Array(mask.width * mask.height);
969
+ for (let y = 0; y < wh; y++) {
970
+ const dst = (y0 + y) * mask.width + x0;
971
+ for (let x = 0; x < ww; x++) out[dst + x] = coverageToByte(refined[y * ww + x]);
972
+ }
973
+ return createSelectionMask(mask.width, mask.height, out);
974
+ }
975
+ function decontaminateColors(rgba, coverage, w, h, amount, radius = 8) {
976
+ const out = Uint8ClampedArray.from(rgba);
977
+ const n = w * h;
978
+ const k = Math.min(1, Math.max(0, amount));
979
+ if (k === 0) return out;
980
+ const r = Math.max(2, Math.round(radius));
981
+ const fgW = new Float32Array(n);
982
+ const bgW = new Float32Array(n);
983
+ for (let i = 0; i < n; i++) {
984
+ fgW[i] = coverage[i] >= 250 ? 1 : 0;
985
+ bgW[i] = coverage[i] <= 5 ? 1 : 0;
986
+ }
987
+ const meanOf = (weights, radiusPx) => {
988
+ const wm = boxMean(weights, w, h, radiusPx);
989
+ const planes = [0, 1, 2].map((c) => {
990
+ const v = new Float32Array(n);
991
+ for (let i = 0; i < n; i++) v[i] = rgba[i * 4 + c] * weights[i];
992
+ const m = boxMean(v, w, h, radiusPx);
993
+ for (let i = 0; i < n; i++) m[i] = wm[i] > 1e-6 ? m[i] / wm[i] : NaN;
994
+ return m;
995
+ });
996
+ return { weight: wm, planes };
997
+ };
998
+ const fg = meanOf(fgW, r);
999
+ const fgWide = meanOf(fgW, r * 3);
1000
+ const bg = meanOf(bgW, r);
1001
+ for (let i = 0; i < n; i++) {
1002
+ const c = coverage[i];
1003
+ if (c <= 5 || c >= 250) continue;
1004
+ const a = c / 255;
1005
+ for (let ch = 0; ch < 3; ch++) {
1006
+ const C = rgba[i * 4 + ch];
1007
+ const B = bg.planes[ch][i];
1008
+ const Fm = Number.isNaN(fg.planes[ch][i]) ? fgWide.planes[ch][i] : fg.planes[ch][i];
1009
+ let F;
1010
+ if (a >= 0.5 && !Number.isNaN(B)) F = (C - (1 - a) * B) / a;
1011
+ else if (!Number.isNaN(Fm)) F = Fm;
1012
+ else F = C;
1013
+ out[i * 4 + ch] = Math.round(C + k * (Math.min(255, Math.max(0, F)) - C));
1014
+ }
1015
+ }
1016
+ return out;
1017
+ }
1018
+
727
1019
  export {
728
1020
  SELECTION_MODES,
729
1021
  isSelectionMode,
@@ -759,5 +1051,16 @@ export {
759
1051
  greyMorphology,
760
1052
  shiftCoverage,
761
1053
  SELECTION_MODIFY_OPS,
762
- modifySelection
1054
+ modifySelection,
1055
+ DEFAULT_REFINE_EDGE_PARAMS,
1056
+ GUIDED_FILTER_EPS,
1057
+ normalizeRefineEdgeParams,
1058
+ refineEdgeHalo,
1059
+ boxMean,
1060
+ guidedFilterColor,
1061
+ edgeWidth,
1062
+ refineCoverage,
1063
+ guidePlanes,
1064
+ refineSelection,
1065
+ decontaminateColors
763
1066
  };
@@ -1,5 +1,6 @@
1
1
  import { AdjustmentKind, AdjustmentLayer, AdjustmentParamsMap, Annotation, CreateAnnotationPayload, AspectRatio, CropBox, DepthMaskOptions, ExportOptions, ImageAdjustments, ImageDimensions, ImageEditorFit, ImageEditorOptions, ImageEditorState, ImageEditorTool, ImageTransform, ImagePoint, SerializedImageEditorState, DocumentOpener } from './types.cjs';
2
2
  import { type InspectMaskEdgeOptions, type MaskEdgeReport } from './selection/inspect.cjs';
3
+ import { type SelectionUncertainty, type SelectionUncertaintyOptions } from './selection/uncertainty.cjs';
3
4
  import { type PathToSelectionOptions, type SelectionToPathOptions } from './selection/path.cjs';
4
5
  import { type MagneticEdgeOptions } from './selection/magnetic.cjs';
5
6
  import type { ObjectTarget } from './selection/segment/grabcut.cjs';
@@ -283,6 +284,15 @@ export interface CloneSource {
283
284
  /** A vector mask's path: several `components`, or one `shape` / `subpaths` (with `op`). */
284
285
  /** What `inspectMaskEdge` reads: the selection, a layer's painted mask or its vector mask. */
285
286
  export type MaskEdgeTarget = 'selection' | 'layer-mask' | 'vector-mask';
287
+ /** A layer's edge colour as `getEdgeColor` describes it (percentages). */
288
+ export interface EdgeColorInfo {
289
+ layerId: string;
290
+ decontaminate: number;
291
+ despill: number;
292
+ ambient: number;
293
+ /** `auto` (what is behind the layer, live) or `#rrggbb`. */
294
+ ambientSource: string;
295
+ }
286
296
  export interface VectorMaskPathInput extends ShapeGeometryInput {
287
297
  components?: ShapeGeometryInput[];
288
298
  fillRule?: ShapeFillRule;
@@ -1025,6 +1035,26 @@ export declare class ImageEditorController {
1025
1035
  parentId: string | null;
1026
1036
  index: number;
1027
1037
  };
1038
+ /** A layer's edge colour as percentages (see `setEdgeColor`). */
1039
+ getEdgeColor(id: string): EdgeColorInfo;
1040
+ private requireEdgeLayer;
1041
+ /** Whether the layer's mask (painted and vector, with feather) has a soft band. */
1042
+ private maskHasSoftBand;
1043
+ /**
1044
+ * Edge colour on a layer's soft edge, applied when compositing (nothing is baked); amounts in
1045
+ * percent, 0 turns one off. `decontaminate` unmixes the old background (the pixels the mask
1046
+ * hides) from the edge and `despill` limits its hue there: both need a pixel layer whose mask
1047
+ * (painted or vector) has a soft band. `ambient` pulls the edge toward what is behind the layer
1048
+ * (`ambientColor` null: sampled live) or a fixed `#rrggbb`; it needs a soft mask or soft
1049
+ * pixels at the edge. Validates everything before changing anything; one undo step (none with
1050
+ * `commitToHistory` false, for the later moves of a slider drag). Returns the effective settings.
1051
+ */
1052
+ setEdgeColor(id: string, change: {
1053
+ decontaminate?: number;
1054
+ despill?: number;
1055
+ ambient?: number;
1056
+ ambientColor?: string | null;
1057
+ }, commitToHistory?: boolean): EdgeColorInfo;
1028
1058
  /**
1029
1059
  * Changes name, visibility, opacity, fill, blend, clipping, locks or (groups) collapsed state, as
1030
1060
  * one undo step (`commitToHistory = false` for the moves of a slider drag). Returns the layer.
@@ -1847,6 +1877,8 @@ export declare class ImageEditorController {
1847
1877
  decontaminate?: number;
1848
1878
  edits?: RefineEdit[];
1849
1879
  refineMode?: 'color' | 'object';
1880
+ /** Refine only inside these rectangles (e.g. `selectionUncertainty().regions`); the rest stays as it was. */
1881
+ regions?: CropBox[];
1850
1882
  signal?: AbortSignal;
1851
1883
  }): Promise<RefineOutputResult>;
1852
1884
  /**
@@ -2025,6 +2057,15 @@ export declare class ImageEditorController {
2025
2057
  target: MaskEdgeTarget;
2026
2058
  layerId?: string;
2027
2059
  };
2060
+ /** Whole-pixel rectangles inside the canvas, or a useful error; undefined stays undefined. */
2061
+ private checkRegions;
2062
+ /**
2063
+ * Where the selection is unsure of itself: the stretches of its edge with no image edge under
2064
+ * them, widened into a band (a trimap's unknown), as a mask and regions with bounds and
2065
+ * confidence, least confident first. Measured against the composite. Read-only; an empty
2066
+ * selection has none.
2067
+ */
2068
+ selectionUncertainty(options?: SelectionUncertaintyOptions): SelectionUncertainty;
2028
2069
  /** A layer's painted mask over the canvas as a selection mask; null without one (or for a depth mask). */
2029
2070
  private layerMaskCoverage;
2030
2071
  /**
@@ -1,5 +1,6 @@
1
1
  import { AdjustmentKind, AdjustmentLayer, AdjustmentParamsMap, Annotation, CreateAnnotationPayload, AspectRatio, CropBox, DepthMaskOptions, ExportOptions, ImageAdjustments, ImageDimensions, ImageEditorFit, ImageEditorOptions, ImageEditorState, ImageEditorTool, ImageTransform, ImagePoint, SerializedImageEditorState, DocumentOpener } from './types.js';
2
2
  import { type InspectMaskEdgeOptions, type MaskEdgeReport } from './selection/inspect.js';
3
+ import { type SelectionUncertainty, type SelectionUncertaintyOptions } from './selection/uncertainty.js';
3
4
  import { type PathToSelectionOptions, type SelectionToPathOptions } from './selection/path.js';
4
5
  import { type MagneticEdgeOptions } from './selection/magnetic.js';
5
6
  import type { ObjectTarget } from './selection/segment/grabcut.js';
@@ -283,6 +284,15 @@ export interface CloneSource {
283
284
  /** A vector mask's path: several `components`, or one `shape` / `subpaths` (with `op`). */
284
285
  /** What `inspectMaskEdge` reads: the selection, a layer's painted mask or its vector mask. */
285
286
  export type MaskEdgeTarget = 'selection' | 'layer-mask' | 'vector-mask';
287
+ /** A layer's edge colour as `getEdgeColor` describes it (percentages). */
288
+ export interface EdgeColorInfo {
289
+ layerId: string;
290
+ decontaminate: number;
291
+ despill: number;
292
+ ambient: number;
293
+ /** `auto` (what is behind the layer, live) or `#rrggbb`. */
294
+ ambientSource: string;
295
+ }
286
296
  export interface VectorMaskPathInput extends ShapeGeometryInput {
287
297
  components?: ShapeGeometryInput[];
288
298
  fillRule?: ShapeFillRule;
@@ -1025,6 +1035,26 @@ export declare class ImageEditorController {
1025
1035
  parentId: string | null;
1026
1036
  index: number;
1027
1037
  };
1038
+ /** A layer's edge colour as percentages (see `setEdgeColor`). */
1039
+ getEdgeColor(id: string): EdgeColorInfo;
1040
+ private requireEdgeLayer;
1041
+ /** Whether the layer's mask (painted and vector, with feather) has a soft band. */
1042
+ private maskHasSoftBand;
1043
+ /**
1044
+ * Edge colour on a layer's soft edge, applied when compositing (nothing is baked); amounts in
1045
+ * percent, 0 turns one off. `decontaminate` unmixes the old background (the pixels the mask
1046
+ * hides) from the edge and `despill` limits its hue there: both need a pixel layer whose mask
1047
+ * (painted or vector) has a soft band. `ambient` pulls the edge toward what is behind the layer
1048
+ * (`ambientColor` null: sampled live) or a fixed `#rrggbb`; it needs a soft mask or soft
1049
+ * pixels at the edge. Validates everything before changing anything; one undo step (none with
1050
+ * `commitToHistory` false, for the later moves of a slider drag). Returns the effective settings.
1051
+ */
1052
+ setEdgeColor(id: string, change: {
1053
+ decontaminate?: number;
1054
+ despill?: number;
1055
+ ambient?: number;
1056
+ ambientColor?: string | null;
1057
+ }, commitToHistory?: boolean): EdgeColorInfo;
1028
1058
  /**
1029
1059
  * Changes name, visibility, opacity, fill, blend, clipping, locks or (groups) collapsed state, as
1030
1060
  * one undo step (`commitToHistory = false` for the moves of a slider drag). Returns the layer.
@@ -1847,6 +1877,8 @@ export declare class ImageEditorController {
1847
1877
  decontaminate?: number;
1848
1878
  edits?: RefineEdit[];
1849
1879
  refineMode?: 'color' | 'object';
1880
+ /** Refine only inside these rectangles (e.g. `selectionUncertainty().regions`); the rest stays as it was. */
1881
+ regions?: CropBox[];
1850
1882
  signal?: AbortSignal;
1851
1883
  }): Promise<RefineOutputResult>;
1852
1884
  /**
@@ -2025,6 +2057,15 @@ export declare class ImageEditorController {
2025
2057
  target: MaskEdgeTarget;
2026
2058
  layerId?: string;
2027
2059
  };
2060
+ /** Whole-pixel rectangles inside the canvas, or a useful error; undefined stays undefined. */
2061
+ private checkRegions;
2062
+ /**
2063
+ * Where the selection is unsure of itself: the stretches of its edge with no image edge under
2064
+ * them, widened into a band (a trimap's unknown), as a mask and regions with bounds and
2065
+ * confidence, least confident first. Measured against the composite. Read-only; an empty
2066
+ * selection has none.
2067
+ */
2068
+ selectionUncertainty(options?: SelectionUncertaintyOptions): SelectionUncertainty;
2028
2069
  /** A layer's painted mask over the canvas as a selection mask; null without one (or for a depth mask). */
2029
2070
  private layerMaskCoverage;
2030
2071
  /**