@pixerate/editor 0.189.0 → 0.190.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-ZEHTUR7Z.js → chunk-7ZWLDVFH.js} +971 -753
- package/dist/{chunk-ED4L4UD6.cjs → chunk-T4U4O6DS.cjs} +1005 -787
- package/dist/image-editor/controller.d.cts +51 -1
- package/dist/image-editor/controller.d.ts +51 -1
- package/dist/image-editor/free-transform.d.cts +3 -6
- package/dist/image-editor/free-transform.d.ts +3 -6
- package/dist/image-editor/index.cjs +1219 -901
- package/dist/image-editor/index.d.cts +1 -0
- package/dist/image-editor/index.d.ts +1 -0
- package/dist/image-editor/index.js +321 -3
- package/dist/image-editor/layer-tools.d.cts +1 -1
- package/dist/image-editor/layer-tools.d.ts +1 -1
- package/dist/image-editor/layers/compositor.d.cts +3 -0
- package/dist/image-editor/layers/compositor.d.ts +3 -0
- package/dist/image-editor/layers/model.d.cts +23 -0
- package/dist/image-editor/layers/model.d.ts +23 -0
- package/dist/image-editor/layers/panel.d.cts +11 -1
- package/dist/image-editor/layers/panel.d.ts +11 -1
- package/dist/image-editor/layers/serialize.d.cts +11 -0
- package/dist/image-editor/layers/serialize.d.ts +11 -0
- package/dist/image-editor/layers/shape.d.cts +5 -0
- package/dist/image-editor/layers/shape.d.ts +5 -0
- package/dist/image-editor/layers/vector-mask.d.cts +51 -0
- package/dist/image-editor/layers/vector-mask.d.ts +51 -0
- package/dist/image-editor/psd/import.d.cts +0 -7
- package/dist/image-editor/psd/import.d.ts +0 -7
- package/dist/paint-editor/index.cjs +6 -6
- package/dist/paint-editor/index.js +1 -1
- package/package.json +1 -1
- package/skills/pixerate-image-editor/SKILL.md +6 -0
- package/skills/pixerate-image-selection-masking/SKILL.md +6 -2
|
@@ -36,6 +36,7 @@ export * from './layers/ops.cjs';
|
|
|
36
36
|
export * from './layers/smart.cjs';
|
|
37
37
|
export * from './smart-tools.cjs';
|
|
38
38
|
export * from './layers/shape.cjs';
|
|
39
|
+
export * from './layers/vector-mask.cjs';
|
|
39
40
|
export * from './shape-tools.cjs';
|
|
40
41
|
export * from './layers/text.cjs';
|
|
41
42
|
export * from './layers/text-shapers.cjs';
|
|
@@ -36,6 +36,7 @@ export * from './layers/ops.js';
|
|
|
36
36
|
export * from './layers/smart.js';
|
|
37
37
|
export * from './smart-tools.js';
|
|
38
38
|
export * from './layers/shape.js';
|
|
39
|
+
export * from './layers/vector-mask.js';
|
|
39
40
|
export * from './shape-tools.js';
|
|
40
41
|
export * from './layers/text.js';
|
|
41
42
|
export * from './layers/text-shapers.js';
|
|
@@ -99,6 +99,7 @@ import {
|
|
|
99
99
|
combineSelections,
|
|
100
100
|
compileAdjustmentLayer,
|
|
101
101
|
componentPath,
|
|
102
|
+
componentsCoverage,
|
|
102
103
|
compositeDeep,
|
|
103
104
|
computeSelectionBounds,
|
|
104
105
|
contentsBox,
|
|
@@ -137,6 +138,7 @@ import {
|
|
|
137
138
|
distanceTransform,
|
|
138
139
|
documentContents,
|
|
139
140
|
duplicateLayer,
|
|
141
|
+
effectiveLayerMask,
|
|
140
142
|
encodeDeepSurface,
|
|
141
143
|
encodeRle,
|
|
142
144
|
encodeSelectionRle,
|
|
@@ -148,6 +150,7 @@ import {
|
|
|
148
150
|
filterMaskCoverage,
|
|
149
151
|
flattenPath,
|
|
150
152
|
flipCorners,
|
|
153
|
+
followLayerMove,
|
|
151
154
|
fullSelection,
|
|
152
155
|
geometryHomography,
|
|
153
156
|
getAnnotationBounds,
|
|
@@ -177,6 +180,7 @@ import {
|
|
|
177
180
|
invertMatrix3,
|
|
178
181
|
invertSelection,
|
|
179
182
|
isActiveFilter,
|
|
183
|
+
isActiveVectorMask,
|
|
180
184
|
isAdjustmentKind,
|
|
181
185
|
isContentsDecoded,
|
|
182
186
|
isDefaultDepthMask,
|
|
@@ -237,6 +241,7 @@ import {
|
|
|
237
241
|
offsetLayer,
|
|
238
242
|
offsetShape,
|
|
239
243
|
offsetSmartObject,
|
|
244
|
+
offsetVectorMask,
|
|
240
245
|
parseAdjustmentBlock,
|
|
241
246
|
parseBlend,
|
|
242
247
|
parseLfx2,
|
|
@@ -290,6 +295,7 @@ import {
|
|
|
290
295
|
rerenderText,
|
|
291
296
|
resizeSelection,
|
|
292
297
|
resolveExternalAssets,
|
|
298
|
+
resolveVectorMasks,
|
|
293
299
|
rgbToGray,
|
|
294
300
|
rgbToHsl,
|
|
295
301
|
runLayerFilters,
|
|
@@ -341,6 +347,7 @@ import {
|
|
|
341
347
|
transformMap,
|
|
342
348
|
transformShapeComponents,
|
|
343
349
|
transformTypeLayer,
|
|
350
|
+
transformVectorMask,
|
|
344
351
|
transformedBox,
|
|
345
352
|
transformedPoint,
|
|
346
353
|
translateLayer,
|
|
@@ -359,6 +366,9 @@ import {
|
|
|
359
366
|
updateTypeLayer,
|
|
360
367
|
userErrorMessage,
|
|
361
368
|
uvToXy,
|
|
369
|
+
vectorMaskBounds,
|
|
370
|
+
vectorMaskCoverage,
|
|
371
|
+
vectorMaskThumbnail,
|
|
362
372
|
versionedDescriptorBytes,
|
|
363
373
|
vibranceLinear,
|
|
364
374
|
vibrancePixel,
|
|
@@ -373,7 +383,7 @@ import {
|
|
|
373
383
|
writePsd,
|
|
374
384
|
xyToTempTint,
|
|
375
385
|
xyToUv
|
|
376
|
-
} from "../chunk-
|
|
386
|
+
} from "../chunk-7ZWLDVFH.js";
|
|
377
387
|
import {
|
|
378
388
|
parseCssColor
|
|
379
389
|
} from "../chunk-3CI7FRMK.js";
|
|
@@ -4164,6 +4174,24 @@ var FreeTransformSession = class {
|
|
|
4164
4174
|
}
|
|
4165
4175
|
/** Resamples every part for the current geometry and shows it (or returns it, for commit). */
|
|
4166
4176
|
resample(final, preview = {}) {
|
|
4177
|
+
const out = this.resampleContent(final, preview);
|
|
4178
|
+
return { ...out, layers: this.moveVectorMask(out.layers) };
|
|
4179
|
+
}
|
|
4180
|
+
/** A linked vector mask follows the layer's content through the same document-space map. */
|
|
4181
|
+
moveVectorMask(layers) {
|
|
4182
|
+
const g = this.geometryValue;
|
|
4183
|
+
if (!this.layerId || this.copyId || !this.moves.includes("pixels") || isIdentityTransform(g)) return layers;
|
|
4184
|
+
const vm = findLayer(layers, this.layerId)?.layer.vectorMask;
|
|
4185
|
+
if (!vm || vm.linked === false) return layers;
|
|
4186
|
+
const warp = hasWarp(g);
|
|
4187
|
+
const f = warp ? transformMap(g) : null;
|
|
4188
|
+
const h = warp ? null : geometryHomography(g);
|
|
4189
|
+
const affine = !warp && h && Math.abs(h[6]) < 1e-12 && Math.abs(h[7]) < 1e-12 && Math.abs(h[8] - 1) < 1e-12 ? [h[0], h[3], h[1], h[4], h[2], h[5]] : void 0;
|
|
4190
|
+
const map = (p) => (f ? f(p) : h ? applyHomography(h, p) : p) ?? p;
|
|
4191
|
+
const moved = transformVectorMask(vm, map, affine);
|
|
4192
|
+
return mapLayer(layers, this.layerId, (l) => ({ ...l, vectorMask: moved }));
|
|
4193
|
+
}
|
|
4194
|
+
resampleContent(final, preview = {}) {
|
|
4167
4195
|
if (this.smart) return { layers: this.resampleSmart(final, preview), selection: this.startSelection };
|
|
4168
4196
|
if (this.typeTarget) return { layers: this.resampleType(), selection: this.startSelection };
|
|
4169
4197
|
const g = this.geometryValue;
|
|
@@ -6167,6 +6195,7 @@ var ImageEditorController = class {
|
|
|
6167
6195
|
let moved = { ...transformTypeLayer(layer, [1, 0, 0, 1, mx, my]), surface: translateSurface(layer.surface, mx, my) };
|
|
6168
6196
|
const mask = layer.mask;
|
|
6169
6197
|
if (mask && mask.linked !== false && !mask.source && (mx || my)) moved = { ...moved, mask: { ...mask, surface: translateSurface(mask.surface, mx, my) } };
|
|
6198
|
+
moved = followLayerMove(moved, mx, my);
|
|
6170
6199
|
this.commitLayers(mapLayer(this.state.layers, id, () => moved), void 0, commitToHistory);
|
|
6171
6200
|
const t = moved.transform ?? [1, 0, 0, 1, 0, 0];
|
|
6172
6201
|
return { offset: { x: Math.round(t[4]), y: Math.round(t[5]) }, bounds: this.getLayerBounds(id) };
|
|
@@ -6218,6 +6247,139 @@ var ImageEditorController = class {
|
|
|
6218
6247
|
if (this.state.activeLayerId === id) this.state.activeTarget = "pixels";
|
|
6219
6248
|
});
|
|
6220
6249
|
}
|
|
6250
|
+
// --- Vector masks ---
|
|
6251
|
+
/** The layer for a vector mask command: not a shape layer (its path is its vector mask), not a depth-masked one. */
|
|
6252
|
+
vectorMaskLayer(id) {
|
|
6253
|
+
const layer = requireLayer(this.state.layers, id).layer;
|
|
6254
|
+
const name = layer.name || id;
|
|
6255
|
+
if (layer.type === "shape") throw new LayerOperationError(`Layer "${name}" is a shape layer, whose path already is its vector mask; change it with set_shape_component, or rasterize the layer first.`);
|
|
6256
|
+
if (layer.mask?.source) throw new LayerOperationError(`Layer "${name}" has a depth mask, which cannot be combined with a vector mask; remove the depth mask first.`);
|
|
6257
|
+
return layer;
|
|
6258
|
+
}
|
|
6259
|
+
requireVectorMask(id) {
|
|
6260
|
+
const layer = this.vectorMaskLayer(id);
|
|
6261
|
+
if (!layer.vectorMask) throw new LayerOperationError(`Layer "${layer.name || id}" has no vector mask; add one with add_vector_mask.`);
|
|
6262
|
+
return { layer, vm: layer.vectorMask };
|
|
6263
|
+
}
|
|
6264
|
+
/** Components from a path spec: `components`, or one `shape` / `subpaths`. */
|
|
6265
|
+
vectorMaskComponents(path) {
|
|
6266
|
+
if (path.components?.length && (path.shape !== void 0 || path.subpaths !== void 0)) throw new LayerOperationError("Give components, or shape / subpaths, not both.");
|
|
6267
|
+
if (path.fillRule !== void 0 && path.fillRule !== "nonzero" && path.fillRule !== "evenodd") throw new LayerOperationError("fillRule must be nonzero or evenodd.");
|
|
6268
|
+
const specs = path.components?.length ? path.components : [path];
|
|
6269
|
+
return specs.map((c, i) => this.componentFrom(c, path.components?.length ? `components[${i}].` : ""));
|
|
6270
|
+
}
|
|
6271
|
+
/**
|
|
6272
|
+
* Layer › Vector Mask: `reveal-all` (an empty path showing everything), `hide-all` (an empty path
|
|
6273
|
+
* hiding everything) or `path` (reveals inside the path given). Replaces an existing vector mask;
|
|
6274
|
+
* the raster mask is kept and both apply. One undo step. Returns the vector mask.
|
|
6275
|
+
*/
|
|
6276
|
+
addVectorMask(id, from = "reveal-all", path = {}) {
|
|
6277
|
+
this.vectorMaskLayer(id);
|
|
6278
|
+
let vm;
|
|
6279
|
+
if (from === "path") {
|
|
6280
|
+
vm = { components: this.vectorMaskComponents(path) };
|
|
6281
|
+
if (path.fillRule === "evenodd") vm.fillRule = "evenodd";
|
|
6282
|
+
} else if (from === "reveal-all" || from === "hide-all") {
|
|
6283
|
+
if (path.components?.length || path.shape !== void 0 || path.subpaths !== void 0) throw new LayerOperationError(`A ${from} vector mask starts empty; use from path to give a path.`);
|
|
6284
|
+
vm = { components: [], ...from === "reveal-all" ? { inverted: true } : {} };
|
|
6285
|
+
} else {
|
|
6286
|
+
throw new LayerOperationError(`Unknown vector mask source "${String(from)}". Use reveal-all, hide-all or path.`);
|
|
6287
|
+
}
|
|
6288
|
+
this.commitLayers(mapLayer(this.state.layers, id, (l) => ({ ...l, vectorMask: vm })));
|
|
6289
|
+
return vm;
|
|
6290
|
+
}
|
|
6291
|
+
/** Replaces a vector mask's path (its properties are kept). One undo step. Returns the vector mask. */
|
|
6292
|
+
setVectorMaskPath(id, path) {
|
|
6293
|
+
const { vm } = this.requireVectorMask(id);
|
|
6294
|
+
const next = { ...vm, components: this.vectorMaskComponents(path) };
|
|
6295
|
+
if (path.fillRule === "evenodd") next.fillRule = "evenodd";
|
|
6296
|
+
else if (path.fillRule === "nonzero") delete next.fillRule;
|
|
6297
|
+
this.commitLayers(mapLayer(this.state.layers, id, (l) => ({ ...l, vectorMask: next })));
|
|
6298
|
+
return next;
|
|
6299
|
+
}
|
|
6300
|
+
/** Changes a vector mask's enabled, linked, inverted, density or feather (the path is kept). One undo step. */
|
|
6301
|
+
setVectorMaskProps(id, props) {
|
|
6302
|
+
const { vm } = this.requireVectorMask(id);
|
|
6303
|
+
if (props.density !== void 0 && !(props.density >= 0 && props.density <= 1)) throw new LayerOperationError("density must be from 0 to 1.");
|
|
6304
|
+
if (props.feather !== void 0 && !(props.feather >= 0 && props.feather <= 1e3)) throw new LayerOperationError("feather must be from 0 to 1000 px.");
|
|
6305
|
+
const next = { ...vm };
|
|
6306
|
+
for (const k of ["enabled", "linked", "inverted"]) {
|
|
6307
|
+
if (props[k] === void 0) continue;
|
|
6308
|
+
if (typeof props[k] !== "boolean") throw new LayerOperationError(`${k} must be true or false.`);
|
|
6309
|
+
if (k === "inverted" ? props[k] : !props[k]) next[k] = k === "inverted";
|
|
6310
|
+
else delete next[k];
|
|
6311
|
+
}
|
|
6312
|
+
if (props.density !== void 0) {
|
|
6313
|
+
if (props.density === 1) delete next.density;
|
|
6314
|
+
else next.density = props.density;
|
|
6315
|
+
}
|
|
6316
|
+
if (props.feather !== void 0) {
|
|
6317
|
+
if (props.feather === 0) delete next.feather;
|
|
6318
|
+
else next.feather = props.feather;
|
|
6319
|
+
}
|
|
6320
|
+
this.commitLayers(mapLayer(this.state.layers, id, (l) => ({ ...l, vectorMask: next })));
|
|
6321
|
+
return next;
|
|
6322
|
+
}
|
|
6323
|
+
/** Deletes a layer's vector mask (one undo step). */
|
|
6324
|
+
removeVectorMask(id) {
|
|
6325
|
+
this.requireVectorMask(id);
|
|
6326
|
+
this.commitLayers(
|
|
6327
|
+
mapLayer(this.state.layers, id, (l) => {
|
|
6328
|
+
const next = { ...l };
|
|
6329
|
+
delete next.vectorMask;
|
|
6330
|
+
return next;
|
|
6331
|
+
})
|
|
6332
|
+
);
|
|
6333
|
+
}
|
|
6334
|
+
/**
|
|
6335
|
+
* Layer › Rasterize › Vector Mask: multiplies the path (with its density and feather) into the
|
|
6336
|
+
* layer's raster mask, adding one when there is none, and removes the vector mask. The raster
|
|
6337
|
+
* mask keeps its own properties. One undo step. Returns the raster mask.
|
|
6338
|
+
*/
|
|
6339
|
+
rasterizeVectorMask(id) {
|
|
6340
|
+
const { layer } = this.requireVectorMask(id);
|
|
6341
|
+
const { width, height } = this.canvasSize;
|
|
6342
|
+
const raster = layer.mask;
|
|
6343
|
+
const plain3 = { ...layer, vectorMask: { ...layer.vectorMask, enabled: true }, mask: raster ? { surface: raster.surface, defaultValue: raster.defaultValue } : void 0 };
|
|
6344
|
+
const baked = effectiveLayerMask(plain3, width, height);
|
|
6345
|
+
const mask = raster ? { ...raster, surface: baked.surface, defaultValue: baked.defaultValue } : baked;
|
|
6346
|
+
this.commitLayers(
|
|
6347
|
+
mapLayer(this.state.layers, id, (l) => {
|
|
6348
|
+
const next = { ...l, mask };
|
|
6349
|
+
delete next.vectorMask;
|
|
6350
|
+
return next;
|
|
6351
|
+
})
|
|
6352
|
+
);
|
|
6353
|
+
return mask;
|
|
6354
|
+
}
|
|
6355
|
+
/** Load Selection from a vector mask's path (density and feather are not applied), inverted with `invert`. */
|
|
6356
|
+
selectionFromVectorMask(id, mode = "replace", invert2 = false) {
|
|
6357
|
+
const { vm } = this.requireVectorMask(id);
|
|
6358
|
+
const { width, height } = this.naturalSize;
|
|
6359
|
+
const cov = componentsCoverage(vm.components, { x: 0, y: 0, width, height }, vm.fillRule ?? "nonzero", vm.inverted === true !== invert2);
|
|
6360
|
+
const bytes = new Uint8Array(cov.length);
|
|
6361
|
+
for (let i = 0; i < cov.length; i++) bytes[i] = Math.round(Math.min(1, Math.max(0, cov[i])) * 255);
|
|
6362
|
+
return this.setSelection(createSelectionMask(width, height, bytes), mode);
|
|
6363
|
+
}
|
|
6364
|
+
/** A layer's vector mask with its paths (rounded), or `null` without one. */
|
|
6365
|
+
getVectorMask(id) {
|
|
6366
|
+
const layer = requireLayer(this.state.layers, id).layer;
|
|
6367
|
+
const vm = layer.vectorMask;
|
|
6368
|
+
if (!vm) return null;
|
|
6369
|
+
const b = vectorMaskBounds(vm);
|
|
6370
|
+
const r = (n) => Math.round(n * 1e3) / 1e3;
|
|
6371
|
+
return {
|
|
6372
|
+
layerId: id,
|
|
6373
|
+
enabled: vm.enabled !== false,
|
|
6374
|
+
linked: vm.linked !== false,
|
|
6375
|
+
inverted: vm.inverted === true,
|
|
6376
|
+
fillRule: vm.fillRule ?? "nonzero",
|
|
6377
|
+
density: vm.density ?? 1,
|
|
6378
|
+
feather: vm.feather ?? 0,
|
|
6379
|
+
bounds: b ? { x: r(b.x), y: r(b.y), width: r(b.width), height: r(b.height) } : null,
|
|
6380
|
+
components: vm.components.map((c, index) => ({ index, op: c.op, live: c.live ? JSON.parse(JSON.stringify(c.live)) : null, subpaths: roundSubpaths(c.subpaths) }))
|
|
6381
|
+
};
|
|
6382
|
+
}
|
|
6221
6383
|
/** Changes any layer mask's density, feather, enabled or linked flag (pixels kept). Returns the mask. */
|
|
6222
6384
|
setLayerMaskProps(id, props, commitToHistory = true) {
|
|
6223
6385
|
const layer = requireLayer(this.state.layers, id).layer;
|
|
@@ -11658,11 +11820,12 @@ function createImageSelectionTools(controller) {
|
|
|
11658
11820
|
const loadFromMask = {
|
|
11659
11821
|
name: "load_selection_from_mask",
|
|
11660
11822
|
title: "Load a selection from a layer mask",
|
|
11661
|
-
description: "Load Selection from a layer's mask (Ctrl/\u2318-click its mask thumbnail): the mask's pixels become the selection, combined by mode (density and feather are not applied). Use it to reuse a mask on another layer or to edit it as a selection. Fails when the layer has no
|
|
11823
|
+
description: "Load Selection from a layer's mask (Ctrl/\u2318-click its mask thumbnail): the raster mask's pixels, or with source vector the vector mask's path, become the selection, combined by mode (density and feather are not applied). Use it to reuse a mask on another layer or to edit it as a selection. Fails when the layer has no such mask. One undoable step. Returns the selection.",
|
|
11662
11824
|
inputSchema: {
|
|
11663
11825
|
type: "object",
|
|
11664
11826
|
properties: {
|
|
11665
11827
|
layerId: { type: "string", minLength: 1, description: "The layer whose mask to load (see list_layers)." },
|
|
11828
|
+
source: { type: "string", enum: ["pixels", "vector"], description: "pixels: the raster (layer) mask (default); vector: the vector mask's path." },
|
|
11666
11829
|
invert: { type: "boolean", description: "Select what the mask hides instead (default false)." },
|
|
11667
11830
|
mode: modeSchema
|
|
11668
11831
|
},
|
|
@@ -11674,6 +11837,11 @@ function createImageSelectionTools(controller) {
|
|
|
11674
11837
|
const id = String(args.layerId ?? "");
|
|
11675
11838
|
const layer = controller.getLayer(id);
|
|
11676
11839
|
if (!layer) throw new Error(`No layer "${id}". Call list_layers for ids.`);
|
|
11840
|
+
if (args.source === "vector") {
|
|
11841
|
+
if (!layer.vectorMask) throw new Error(`Layer "${id}" has no vector mask. Add one with add_vector_mask, or load its pixels mask (source pixels).`);
|
|
11842
|
+
controller.selectionFromVectorMask(id, parseSelectionMode(args.mode), args.invert === true);
|
|
11843
|
+
return summary(controller);
|
|
11844
|
+
}
|
|
11677
11845
|
if (!layer.mask) throw new Error(`Layer "${id}" has no mask. Add one with add_layer_mask, or pick a layer with a mask.`);
|
|
11678
11846
|
if (layer.mask.source) throw new Error(`Layer "${id}" has a computed (depth) mask, not a painted one, so it cannot be loaded as a selection.`);
|
|
11679
11847
|
controller.selectionFromLayerMask(id, parseSelectionMode(args.mode), args.invert === true);
|
|
@@ -12540,6 +12708,7 @@ function describeLayer(layer, activeId, boundsOf) {
|
|
|
12540
12708
|
clipped: Boolean(layer.clipped),
|
|
12541
12709
|
locks: layer.locks ? { ...layer.locks } : {},
|
|
12542
12710
|
mask: maskSummary(layer.mask),
|
|
12711
|
+
vectorMask: layer.vectorMask ? { enabled: layer.vectorMask.enabled !== false, linked: layer.vectorMask.linked !== false, inverted: layer.vectorMask.inverted === true, components: layer.vectorMask.components.length, bounds: vectorMaskBounds(layer.vectorMask) } : null,
|
|
12543
12712
|
effects: layer.effects ? { enabled: layer.effects.enabled, scale: layer.effects.scale ?? 1, items: layer.effects.items.map((e) => ({ id: e.id, kind: e.kind, enabled: e.enabled })) } : null,
|
|
12544
12713
|
active: layer.id === activeId
|
|
12545
12714
|
};
|
|
@@ -15726,7 +15895,122 @@ function createImageShapeTools(controller) {
|
|
|
15726
15895
|
annotations: { title: "Merge shape components", ...DESTRUCTIVE6 },
|
|
15727
15896
|
handler: ({ layerId }) => controller.mergeShapeComponents(shapeId(layerId))
|
|
15728
15897
|
};
|
|
15729
|
-
|
|
15898
|
+
const layerArg = (v) => {
|
|
15899
|
+
const id = String(v ?? "");
|
|
15900
|
+
if (!controller.getLayer(id)) throw new Error(`No layer "${id}". Call list_layers for ids.`);
|
|
15901
|
+
return id;
|
|
15902
|
+
};
|
|
15903
|
+
const pathSpec = (args) => {
|
|
15904
|
+
const spec = { ...geometry(args) };
|
|
15905
|
+
if (args.components !== void 0) spec.components = args.components.map(geometry);
|
|
15906
|
+
if (args.fillRule !== void 0) spec.fillRule = args.fillRule;
|
|
15907
|
+
return spec;
|
|
15908
|
+
};
|
|
15909
|
+
const PATH_PROPS = {
|
|
15910
|
+
...GEOMETRY2,
|
|
15911
|
+
components: {
|
|
15912
|
+
type: "array",
|
|
15913
|
+
minItems: 1,
|
|
15914
|
+
description: "Several components instead of shape / subpaths, bottom first, each with its path operation (combine, subtract, intersect, exclude).",
|
|
15915
|
+
items: { type: "object", description: "One component.", properties: { ...GEOMETRY2, op: OP }, additionalProperties: false }
|
|
15916
|
+
},
|
|
15917
|
+
fillRule: { type: "string", enum: ["nonzero", "evenodd"], description: "Winding rule within a component (default nonzero)." }
|
|
15918
|
+
};
|
|
15919
|
+
const LAYER = { type: "string", minLength: 1, description: "The layer (see list_layers). Not a shape layer: its path already is its vector mask." };
|
|
15920
|
+
const getVectorMask = {
|
|
15921
|
+
name: "get_vector_mask",
|
|
15922
|
+
title: "Read a vector mask",
|
|
15923
|
+
description: "Reads a layer's vector mask: enabled, linked, inverted, fill rule, density, feather, the bounds of its path and its components (op, live shape, subpaths in document pixels). Use it before set_vector_mask_path to edit the path. Returns null when the layer has none.",
|
|
15924
|
+
inputSchema: { type: "object", properties: { layerId: LAYER }, required: ["layerId"], additionalProperties: false },
|
|
15925
|
+
annotations: { title: "Read a vector mask", ...READ10 },
|
|
15926
|
+
handler: ({ layerId }) => ({ vectorMask: controller.getVectorMask(layerArg(layerId)) })
|
|
15927
|
+
};
|
|
15928
|
+
const addVectorMask = {
|
|
15929
|
+
name: "add_vector_mask",
|
|
15930
|
+
title: "Add a vector mask",
|
|
15931
|
+
description: "Layer \u203A Vector Mask: a path that reveals the layer inside it and stays sharp at any size, on top of any raster mask (both apply). from path takes a live shape, free subpaths or components like add_shape_layer; reveal-all and hide-all start with an empty path. Use it for hard-edged geometric or traced cutouts that must stay editable; use add_layer_mask for soft or painted ones. A linked vector mask moves and transforms with the layer. Replaces an existing vector mask. One undo step. Returns the vector mask.",
|
|
15932
|
+
inputSchema: {
|
|
15933
|
+
type: "object",
|
|
15934
|
+
$defs: DEFS2,
|
|
15935
|
+
properties: {
|
|
15936
|
+
layerId: LAYER,
|
|
15937
|
+
from: { type: "string", enum: ["path", "reveal-all", "hide-all"], description: "path (default when a shape, subpaths or components are given), reveal-all or hide-all (an empty path)." },
|
|
15938
|
+
...PATH_PROPS
|
|
15939
|
+
},
|
|
15940
|
+
required: ["layerId"],
|
|
15941
|
+
additionalProperties: false
|
|
15942
|
+
},
|
|
15943
|
+
annotations: { title: "Add a vector mask", ...WRITE8 },
|
|
15944
|
+
handler: (args) => {
|
|
15945
|
+
const id = layerArg(args.layerId);
|
|
15946
|
+
const spec = pathSpec(args);
|
|
15947
|
+
const hasPath = spec.components?.length || spec.shape !== void 0 || spec.subpaths !== void 0;
|
|
15948
|
+
const from = args.from ?? (hasPath ? "path" : "reveal-all");
|
|
15949
|
+
controller.addVectorMask(id, from, spec);
|
|
15950
|
+
return { vectorMask: controller.getVectorMask(id) };
|
|
15951
|
+
}
|
|
15952
|
+
};
|
|
15953
|
+
const setVectorMaskPath = {
|
|
15954
|
+
name: "set_vector_mask_path",
|
|
15955
|
+
title: "Replace a vector mask path",
|
|
15956
|
+
description: "Replaces the path of a layer's vector mask (shape, subpaths or components, document pixels); enabled, linked, inverted, density and feather are kept. Read the current path with get_vector_mask first to change part of it. One undo step. Returns the vector mask.",
|
|
15957
|
+
inputSchema: { type: "object", $defs: DEFS2, properties: { layerId: LAYER, ...PATH_PROPS }, required: ["layerId"], additionalProperties: false },
|
|
15958
|
+
annotations: { title: "Replace a vector mask path", ...WRITE_IDEMPOTENT5 },
|
|
15959
|
+
handler: (args) => {
|
|
15960
|
+
const id = layerArg(args.layerId);
|
|
15961
|
+
controller.setVectorMaskPath(id, pathSpec(args));
|
|
15962
|
+
return { vectorMask: controller.getVectorMask(id) };
|
|
15963
|
+
}
|
|
15964
|
+
};
|
|
15965
|
+
const setVectorMaskProps = {
|
|
15966
|
+
name: "set_vector_mask_properties",
|
|
15967
|
+
title: "Set vector mask properties",
|
|
15968
|
+
description: "Changes a layer's vector mask without touching its path: enabled (false ignores it), linked (false: it stays put when the layer moves or transforms), inverted (reveal outside the path), density 0..1 and feather in px (soften the edge without losing the path). One undo step. Returns the vector mask.",
|
|
15969
|
+
inputSchema: {
|
|
15970
|
+
type: "object",
|
|
15971
|
+
properties: {
|
|
15972
|
+
layerId: LAYER,
|
|
15973
|
+
enabled: { type: "boolean", description: "false ignores the vector mask but keeps it." },
|
|
15974
|
+
linked: { type: "boolean", description: "false keeps the path in place when the layer moves or transforms." },
|
|
15975
|
+
inverted: { type: "boolean", description: "true reveals outside the path instead." },
|
|
15976
|
+
density: { type: "number", minimum: 0, maximum: 1, description: "Density 0..1 (default 1): at 0.5 the hidden area still shows half." },
|
|
15977
|
+
feather: { type: "number", minimum: 0, maximum: 1e3, description: "Feather \u03C3 in px (default 0)." }
|
|
15978
|
+
},
|
|
15979
|
+
required: ["layerId"],
|
|
15980
|
+
additionalProperties: false
|
|
15981
|
+
},
|
|
15982
|
+
annotations: { title: "Set vector mask properties", ...WRITE_IDEMPOTENT5 },
|
|
15983
|
+
handler: ({ layerId, ...props }) => {
|
|
15984
|
+
const id = layerArg(layerId);
|
|
15985
|
+
controller.setVectorMaskProps(id, props);
|
|
15986
|
+
return { vectorMask: controller.getVectorMask(id) };
|
|
15987
|
+
}
|
|
15988
|
+
};
|
|
15989
|
+
const rasterizeVectorMask = {
|
|
15990
|
+
name: "rasterize_vector_mask",
|
|
15991
|
+
title: "Rasterize a vector mask",
|
|
15992
|
+
description: "Layer \u203A Rasterize \u203A Vector Mask: multiplies the path (with its density and feather) into the layer's raster mask, adding one if needed, and removes the vector mask, so the mask can be painted. The path can no longer be edited (undo_image brings it back). One undo step. Returns the layer's raster mask summary.",
|
|
15993
|
+
inputSchema: { type: "object", properties: { layerId: LAYER }, required: ["layerId"], additionalProperties: false },
|
|
15994
|
+
annotations: { title: "Rasterize a vector mask", ...DESTRUCTIVE6 },
|
|
15995
|
+
handler: ({ layerId }) => {
|
|
15996
|
+
const id = layerArg(layerId);
|
|
15997
|
+
const mask = controller.rasterizeVectorMask(id);
|
|
15998
|
+
return { layerId: id, mask: { defaultValue: mask.defaultValue, density: mask.density ?? 1, feather: mask.feather ?? 0, enabled: mask.enabled !== false } };
|
|
15999
|
+
}
|
|
16000
|
+
};
|
|
16001
|
+
const removeVectorMask = {
|
|
16002
|
+
name: "remove_vector_mask",
|
|
16003
|
+
title: "Delete a vector mask",
|
|
16004
|
+
description: "Deletes a layer's vector mask without applying it (the raster mask, if any, stays). Disable it instead with set_vector_mask_properties enabled false to keep it. One undo step. Returns the layer id.",
|
|
16005
|
+
inputSchema: { type: "object", properties: { layerId: LAYER }, required: ["layerId"], additionalProperties: false },
|
|
16006
|
+
annotations: { title: "Delete a vector mask", ...DESTRUCTIVE6 },
|
|
16007
|
+
handler: ({ layerId }) => {
|
|
16008
|
+
const id = layerArg(layerId);
|
|
16009
|
+
controller.removeVectorMask(id);
|
|
16010
|
+
return { layerId: id, vectorMask: null };
|
|
16011
|
+
}
|
|
16012
|
+
};
|
|
16013
|
+
return [get, add2, update, setComponent, addComponent, removeComponent, merge, getVectorMask, addVectorMask, setVectorMaskPath, setVectorMaskProps, rasterizeVectorMask, removeVectorMask];
|
|
15730
16014
|
}
|
|
15731
16015
|
|
|
15732
16016
|
// src/image-editor/type-tools.ts
|
|
@@ -16393,6 +16677,11 @@ function maskThumbnailClick(controller, layer, e) {
|
|
|
16393
16677
|
controller.setActiveLayer(layer.id, "mask");
|
|
16394
16678
|
}
|
|
16395
16679
|
}
|
|
16680
|
+
function vectorMaskThumbnailClick(controller, layer, e) {
|
|
16681
|
+
if (!layer.vectorMask) return;
|
|
16682
|
+
if (e.ctrlKey || e.metaKey) controller.selectionFromVectorMask(layer.id, selectionModeFromModifiers(e, "replace"));
|
|
16683
|
+
else if (e.shiftKey) controller.setVectorMaskProps(layer.id, { enabled: layer.vectorMask.enabled === false });
|
|
16684
|
+
}
|
|
16396
16685
|
function layerThumbnailClick(controller, layer, e) {
|
|
16397
16686
|
if (!(e.ctrlKey || e.metaKey)) return false;
|
|
16398
16687
|
controller.selectLayerTransparency(layer.id, { mode: selectionModeFromModifiers(e, "replace") });
|
|
@@ -16491,6 +16780,11 @@ function layerMenu(controller) {
|
|
|
16491
16780
|
{ action: "delete-mask", label: "Delete Layer Mask", enabled: Boolean(active?.mask) },
|
|
16492
16781
|
{ action: "toggle-mask", label: active?.mask?.enabled === false ? "Enable Layer Mask" : "Disable Layer Mask", enabled: Boolean(active?.mask) },
|
|
16493
16782
|
{ action: "toggle-mask-link", label: active?.mask?.linked === false ? "Link Layer Mask" : "Unlink Layer Mask", enabled: Boolean(active?.mask && !active.mask.source) },
|
|
16783
|
+
{ action: "add-vector-mask", label: "Add Vector Mask (Reveal All)", enabled: Boolean(active && active.type !== "shape" && !active.mask?.source) },
|
|
16784
|
+
{ action: "add-vector-mask-hide", label: "Add Vector Mask (Hide All)", enabled: Boolean(active && active.type !== "shape" && !active.mask?.source) },
|
|
16785
|
+
{ action: "delete-vector-mask", label: "Delete Vector Mask", enabled: Boolean(active?.vectorMask) },
|
|
16786
|
+
{ action: "toggle-vector-mask", label: active?.vectorMask?.enabled === false ? "Enable Vector Mask" : "Disable Vector Mask", enabled: Boolean(active?.vectorMask) },
|
|
16787
|
+
{ action: "rasterize-vector-mask", label: "Rasterize Vector Mask", enabled: Boolean(active?.vectorMask) },
|
|
16494
16788
|
{ action: "rasterize", label: "Rasterize Layer", enabled: active?.type === "annotation" || active?.type === "smart" || active?.type === "shape" || active?.type === "type" || isPixel && Boolean(active?.type === "pixel" && active.filters?.length) },
|
|
16495
16789
|
{ action: "convert-smart", label: "Convert to Smart Object", enabled: Boolean(active && active.type !== "annotation") },
|
|
16496
16790
|
{ action: "edit-contents", label: "Edit Contents", enabled: active?.type === "smart" },
|
|
@@ -16562,6 +16856,19 @@ async function runLayerMenuAction(controller, action) {
|
|
|
16562
16856
|
case "toggle-mask-link":
|
|
16563
16857
|
if (id && active?.mask) controller.setLayerMaskProps(id, { linked: active.mask.linked === false });
|
|
16564
16858
|
break;
|
|
16859
|
+
case "add-vector-mask":
|
|
16860
|
+
case "add-vector-mask-hide":
|
|
16861
|
+
if (id) controller.addVectorMask(id, action === "add-vector-mask" ? "reveal-all" : "hide-all");
|
|
16862
|
+
break;
|
|
16863
|
+
case "delete-vector-mask":
|
|
16864
|
+
if (id) controller.removeVectorMask(id);
|
|
16865
|
+
break;
|
|
16866
|
+
case "toggle-vector-mask":
|
|
16867
|
+
if (id && active?.vectorMask) controller.setVectorMaskProps(id, { enabled: active.vectorMask.enabled === false });
|
|
16868
|
+
break;
|
|
16869
|
+
case "rasterize-vector-mask":
|
|
16870
|
+
if (id) controller.rasterizeVectorMask(id);
|
|
16871
|
+
break;
|
|
16565
16872
|
case "rasterize":
|
|
16566
16873
|
if (id) await controller.rasterizeLayer(id);
|
|
16567
16874
|
break;
|
|
@@ -18154,6 +18461,7 @@ export {
|
|
|
18154
18461
|
combineSelections,
|
|
18155
18462
|
compileAdjustmentLayer,
|
|
18156
18463
|
componentPath,
|
|
18464
|
+
componentsCoverage,
|
|
18157
18465
|
compositeDeep,
|
|
18158
18466
|
compositeSurfaceOver,
|
|
18159
18467
|
computeHistogram,
|
|
@@ -18273,6 +18581,7 @@ export {
|
|
|
18273
18581
|
effectLayerTiming,
|
|
18274
18582
|
effectPixelsToSurface,
|
|
18275
18583
|
effectReach,
|
|
18584
|
+
effectiveLayerMask,
|
|
18276
18585
|
effectiveProof,
|
|
18277
18586
|
effectsFingerprint,
|
|
18278
18587
|
effectsMargin,
|
|
@@ -18309,6 +18618,7 @@ export {
|
|
|
18309
18618
|
flattenPath,
|
|
18310
18619
|
flipCorners,
|
|
18311
18620
|
flowField,
|
|
18621
|
+
followLayerMove,
|
|
18312
18622
|
fontOf,
|
|
18313
18623
|
formatControlValue,
|
|
18314
18624
|
fullSelection,
|
|
@@ -18373,6 +18683,7 @@ export {
|
|
|
18373
18683
|
invertMatrix3,
|
|
18374
18684
|
invertSelection,
|
|
18375
18685
|
isActiveFilter,
|
|
18686
|
+
isActiveVectorMask,
|
|
18376
18687
|
isAdjustmentKind,
|
|
18377
18688
|
isBitDepth,
|
|
18378
18689
|
isBlendMode,
|
|
@@ -18512,6 +18823,7 @@ export {
|
|
|
18512
18823
|
offsetLayer,
|
|
18513
18824
|
offsetShape,
|
|
18514
18825
|
offsetSmartObject,
|
|
18826
|
+
offsetVectorMask,
|
|
18515
18827
|
openMaskFile,
|
|
18516
18828
|
openPsdFile,
|
|
18517
18829
|
packRgba,
|
|
@@ -18632,6 +18944,7 @@ export {
|
|
|
18632
18944
|
resolveMosaicRegionBounds,
|
|
18633
18945
|
resolvePaintGradient,
|
|
18634
18946
|
resolveProfile,
|
|
18947
|
+
resolveVectorMasks,
|
|
18635
18948
|
restyleRange,
|
|
18636
18949
|
retouchFilter,
|
|
18637
18950
|
reversePaintGradient,
|
|
@@ -18761,6 +19074,7 @@ export {
|
|
|
18761
19074
|
transformShapeComponents,
|
|
18762
19075
|
transformSurface,
|
|
18763
19076
|
transformTypeLayer,
|
|
19077
|
+
transformVectorMask,
|
|
18764
19078
|
transformedBounds,
|
|
18765
19079
|
transformedBox,
|
|
18766
19080
|
transformedPoint,
|
|
@@ -18787,6 +19101,10 @@ export {
|
|
|
18787
19101
|
validateEffectGradient,
|
|
18788
19102
|
validateLayerEffect,
|
|
18789
19103
|
validateLayerEffects,
|
|
19104
|
+
vectorMaskBounds,
|
|
19105
|
+
vectorMaskCoverage,
|
|
19106
|
+
vectorMaskThumbnail,
|
|
19107
|
+
vectorMaskThumbnailClick,
|
|
18790
19108
|
versionedDescriptorBytes,
|
|
18791
19109
|
vibranceLinear,
|
|
18792
19110
|
vibrancePixel,
|
|
@@ -6,7 +6,7 @@ import type { CropBox } from './types.cjs';
|
|
|
6
6
|
export declare function maskSummary(mask: RasterMask | undefined): Record<string, unknown> | null;
|
|
7
7
|
/**
|
|
8
8
|
* A plain description of a layer (and its children) for agents: ids, types, names, document
|
|
9
|
-
* bounds, visibility, opacity, fill, blend, clipping, locks, mask and effects summaries and
|
|
9
|
+
* bounds, visibility, opacity, fill, blend, clipping, locks, mask, vector mask and effects summaries and
|
|
10
10
|
* whether it is active. `boundsOf` gives a pixel layer's document bounds.
|
|
11
11
|
*/
|
|
12
12
|
export declare function describeLayer(layer: ImageLayer, activeId: string | null, boundsOf: (layer: ImageLayer) => CropBox | null): Record<string, unknown>;
|
|
@@ -6,7 +6,7 @@ import type { CropBox } from './types.js';
|
|
|
6
6
|
export declare function maskSummary(mask: RasterMask | undefined): Record<string, unknown> | null;
|
|
7
7
|
/**
|
|
8
8
|
* A plain description of a layer (and its children) for agents: ids, types, names, document
|
|
9
|
-
* bounds, visibility, opacity, fill, blend, clipping, locks, mask and effects summaries and
|
|
9
|
+
* bounds, visibility, opacity, fill, blend, clipping, locks, mask, vector mask and effects summaries and
|
|
10
10
|
* whether it is active. `boundsOf` gives a pixel layer's document bounds.
|
|
11
11
|
*/
|
|
12
12
|
export declare function describeLayer(layer: ImageLayer, activeId: string | null, boundsOf: (layer: ImageLayer) => CropBox | null): Record<string, unknown>;
|
|
@@ -42,6 +42,9 @@ export declare function runLayerFiltersFloat(data: Float32Array, width: number,
|
|
|
42
42
|
/** The per-tile compositor for image editor layer stacks (see the raster module for the caches). */
|
|
43
43
|
export declare class LayerCompositor extends RasterCompositor<ImageLayer, CompositorSources> {
|
|
44
44
|
constructor(options?: CompositorOptions);
|
|
45
|
+
composite(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, options?: CompositeCallOptions): Uint8ClampedArray;
|
|
46
|
+
compositeTiles(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, onTile: (tx: number, ty: number, data: Uint8ClampedArray, changed: boolean) => void, options?: CompositeCallOptions): void;
|
|
47
|
+
tile(layers: readonly ImageLayer[], tx: number, ty: number, sources: CompositorSources): Uint8ClampedArray;
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
50
|
* The composite of an image editor stack at 16 or 32 bits (see `raster/deep-compositor`): float
|
|
@@ -42,6 +42,9 @@ export declare function runLayerFiltersFloat(data: Float32Array, width: number,
|
|
|
42
42
|
/** The per-tile compositor for image editor layer stacks (see the raster module for the caches). */
|
|
43
43
|
export declare class LayerCompositor extends RasterCompositor<ImageLayer, CompositorSources> {
|
|
44
44
|
constructor(options?: CompositorOptions);
|
|
45
|
+
composite(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, options?: CompositeCallOptions): Uint8ClampedArray;
|
|
46
|
+
compositeTiles(layers: readonly ImageLayer[], rect: RasterRect, sources: CompositorSources, onTile: (tx: number, ty: number, data: Uint8ClampedArray, changed: boolean) => void, options?: CompositeCallOptions): void;
|
|
47
|
+
tile(layers: readonly ImageLayer[], tx: number, ty: number, sources: CompositorSources): Uint8ClampedArray;
|
|
45
48
|
}
|
|
46
49
|
/**
|
|
47
50
|
* The composite of an image editor stack at 16 or 32 bits (see `raster/deep-compositor`): float
|
|
@@ -28,6 +28,29 @@ export type { LayerLocks };
|
|
|
28
28
|
export type RasterMask = BaseRasterMask<DepthMaskOptions>;
|
|
29
29
|
export interface LayerBase extends RasterLayerBase {
|
|
30
30
|
mask?: RasterMask;
|
|
31
|
+
/** A vector mask (Photoshop's): a path that reveals the layer, on top of `mask`. Not on shape layers. */
|
|
32
|
+
vectorMask?: VectorMask;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A vector mask: shape components in document pixels (the same paths shape layers use) that
|
|
36
|
+
* reveal the layer inside them (outside with `inverted`). It stays sharp at any size; the
|
|
37
|
+
* compositor multiplies it with the layer's raster mask. No components hides everything
|
|
38
|
+
* (reveals everything when inverted).
|
|
39
|
+
*/
|
|
40
|
+
export interface VectorMask {
|
|
41
|
+
components: ShapeComponent[];
|
|
42
|
+
/** How overlapping subpaths fill (default nonzero). */
|
|
43
|
+
fillRule?: ShapeFillRule;
|
|
44
|
+
/** Reveal outside the path instead (default false). */
|
|
45
|
+
inverted?: boolean;
|
|
46
|
+
/** Disabled masks are kept but ignored (default true). */
|
|
47
|
+
enabled?: boolean;
|
|
48
|
+
/** `false`: the path stays put when the layer moves or transforms (default true). */
|
|
49
|
+
linked?: boolean;
|
|
50
|
+
/** 0..1 (default 1): at 0.5 the hidden area still shows half. */
|
|
51
|
+
density?: number;
|
|
52
|
+
/** Gaussian feather σ in px (default 0). */
|
|
53
|
+
feather?: number;
|
|
31
54
|
}
|
|
32
55
|
/** The base sliders of the old document-level adjustments, as a filter on one layer. */
|
|
33
56
|
export interface DevelopSettings {
|
|
@@ -28,6 +28,29 @@ export type { LayerLocks };
|
|
|
28
28
|
export type RasterMask = BaseRasterMask<DepthMaskOptions>;
|
|
29
29
|
export interface LayerBase extends RasterLayerBase {
|
|
30
30
|
mask?: RasterMask;
|
|
31
|
+
/** A vector mask (Photoshop's): a path that reveals the layer, on top of `mask`. Not on shape layers. */
|
|
32
|
+
vectorMask?: VectorMask;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A vector mask: shape components in document pixels (the same paths shape layers use) that
|
|
36
|
+
* reveal the layer inside them (outside with `inverted`). It stays sharp at any size; the
|
|
37
|
+
* compositor multiplies it with the layer's raster mask. No components hides everything
|
|
38
|
+
* (reveals everything when inverted).
|
|
39
|
+
*/
|
|
40
|
+
export interface VectorMask {
|
|
41
|
+
components: ShapeComponent[];
|
|
42
|
+
/** How overlapping subpaths fill (default nonzero). */
|
|
43
|
+
fillRule?: ShapeFillRule;
|
|
44
|
+
/** Reveal outside the path instead (default false). */
|
|
45
|
+
inverted?: boolean;
|
|
46
|
+
/** Disabled masks are kept but ignored (default true). */
|
|
47
|
+
enabled?: boolean;
|
|
48
|
+
/** `false`: the path stays put when the layer moves or transforms (default true). */
|
|
49
|
+
linked?: boolean;
|
|
50
|
+
/** 0..1 (default 1): at 0.5 the hidden area still shows half. */
|
|
51
|
+
density?: number;
|
|
52
|
+
/** Gaussian feather σ in px (default 0). */
|
|
53
|
+
feather?: number;
|
|
31
54
|
}
|
|
32
55
|
/** The base sliders of the old document-level adjustments, as a filter on one layer. */
|
|
33
56
|
export interface DevelopSettings {
|
|
@@ -18,6 +18,16 @@ export declare function maskThumbnailClick(controller: ImageEditorController, la
|
|
|
18
18
|
shiftKey?: boolean;
|
|
19
19
|
altKey?: boolean;
|
|
20
20
|
}): void;
|
|
21
|
+
/**
|
|
22
|
+
* A click on a layer's vector mask thumbnail: Ctrl/⌘-click loads the path as the selection (Shift
|
|
23
|
+
* adds, Alt subtracts, both intersect) and Shift-click turns the vector mask on or off.
|
|
24
|
+
*/
|
|
25
|
+
export declare function vectorMaskThumbnailClick(controller: ImageEditorController, layer: ImageLayer, e: {
|
|
26
|
+
ctrlKey?: boolean;
|
|
27
|
+
metaKey?: boolean;
|
|
28
|
+
shiftKey?: boolean;
|
|
29
|
+
altKey?: boolean;
|
|
30
|
+
}): void;
|
|
21
31
|
/**
|
|
22
32
|
* A click on a layer's pixel thumbnail: Ctrl/⌘-click loads the layer's transparency as the
|
|
23
33
|
* selection (Shift adds, Alt subtracts, both intersect) and returns true; other clicks are left
|
|
@@ -57,7 +67,7 @@ export type DropZone = 'above' | 'below' | 'inside';
|
|
|
57
67
|
export declare function dropZone(fraction: number, isGroup: boolean): DropZone;
|
|
58
68
|
/** The placement for `move_layer` when `dragId` is dropped on row `targetId` in `zone`, or `null` for a no-op. */
|
|
59
69
|
export declare function dropPlacement(layers: readonly ImageLayer[], dragId: string, targetId: string, zone: DropZone): LayerPlacement | null;
|
|
60
|
-
export type LayerMenuAction = 'new-layer' | 'new-group' | 'duplicate' | 'delete' | 'group-selected' | 'ungroup' | 'merge-down' | 'merge-visible' | 'flatten' | 'via-copy' | 'via-cut' | 'add-mask' | 'add-mask-selection' | 'add-mask-hide-selection' | 'add-mask-transparency' | 'select-transparency' | 'apply-mask' | 'delete-mask' | 'toggle-mask' | 'toggle-mask-link' | 'toggle-clip' | 'rasterize' | 'convert-smart' | 'edit-contents' | 'smart-via-copy' | 'add-filter-mask';
|
|
70
|
+
export type LayerMenuAction = 'new-layer' | 'new-group' | 'duplicate' | 'delete' | 'group-selected' | 'ungroup' | 'merge-down' | 'merge-visible' | 'flatten' | 'via-copy' | 'via-cut' | 'add-mask' | 'add-vector-mask' | 'add-vector-mask-hide' | 'delete-vector-mask' | 'toggle-vector-mask' | 'rasterize-vector-mask' | 'add-mask-selection' | 'add-mask-hide-selection' | 'add-mask-transparency' | 'select-transparency' | 'apply-mask' | 'delete-mask' | 'toggle-mask' | 'toggle-mask-link' | 'toggle-clip' | 'rasterize' | 'convert-smart' | 'edit-contents' | 'smart-via-copy' | 'add-filter-mask';
|
|
61
71
|
export interface LayerMenuItem {
|
|
62
72
|
action: LayerMenuAction;
|
|
63
73
|
label: string;
|