From 5ad6ab7cb209e8dc0676777e3e9ab9e5f3ee3646 Mon Sep 17 00:00:00 2001 From: Piyush Aryan Date: Tue, 20 Jan 2026 17:23:11 +0530 Subject: [PATCH 01/11] feat: add trim transformation support --- .../imagekit-editor-dev/src/schema/index.ts | 107 +++++++++++++++++- 1 file changed, 101 insertions(+), 6 deletions(-) diff --git a/packages/imagekit-editor-dev/src/schema/index.ts b/packages/imagekit-editor-dev/src/schema/index.ts index 94fec2b..e93f2b0 100644 --- a/packages/imagekit-editor-dev/src/schema/index.ts +++ b/packages/imagekit-editor-dev/src/schema/index.ts @@ -1575,6 +1575,76 @@ export const transformationSchema: TransformationSchema[] = [ }, ], }, + { + key: "adjust-trim", + name: "Trim", + description: + "Trim solid or nearly solid backgrounds from the edges of the image, leaving only the central object.", + docsLink: "https://imagekit.io/docs/effects-and-enhancements", + defaultTransformation: {}, + schema: z + .object({ + trimEnabled: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + trim: z + .union([ + z.literal("auto"), + z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(1) + .max(99), + ]) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, + { + message: "At least one value is required", + path: [], + }, + ), + transformations: [ + { + label: "Enable Trim", + name: "trimEnabled", + fieldType: "switch", + isTransformation: false, + transformationGroup: "trim", + helpText: + "Toggle to trim background edges for images with solid or near-solid backgrounds.", + }, + { + label: "Threshold", + name: "trim", + fieldType: "slider", + isTransformation: false, + transformationGroup: "trim", + helpText: + "Trim edges for images with solid or near-solid backgrounds. Use a threshold between 1 and 99.", + fieldProps: { + defaultValue: "auto", + min: 1, + max: 99, + step: 1, + autoOption: true, + }, + isVisible: ({ trimEnabled }) => trimEnabled === true, + }, + ], + }, ], }, { @@ -2795,10 +2865,11 @@ export const transformationFormatters: Record< shadowOffsetX !== null && shadowOffsetX !== "" ) { - if (shadowOffsetX < 0) { - params.push(`x-N${Math.abs(shadowOffsetX)}`) + const offsetX = Number(shadowOffsetX) + if (!Number.isNaN(offsetX) && offsetX < 0) { + params.push(`x-N${Math.abs(offsetX)}`) } else { - params.push(`x-${shadowOffsetX}`) + params.push(`x-${offsetX}`) } } // Vertical offset; negative values should include N prefix as part of the value @@ -2807,10 +2878,11 @@ export const transformationFormatters: Record< shadowOffsetY !== null && shadowOffsetY !== "" ) { - if (shadowOffsetY < 0) { - params.push(`y-N${Math.abs(shadowOffsetY)}`) + const offsetY = Number(shadowOffsetY) + if (!Number.isNaN(offsetY) && offsetY < 0) { + params.push(`y-N${Math.abs(offsetY)}`) } else { - params.push(`y-${shadowOffsetY}`) + params.push(`y-${offsetY}`) } } // Compose the final transform string @@ -3058,6 +3130,29 @@ export const transformationFormatters: Record< transforms.flip = flip.join("_") } }, + trim: (values, transforms) => { + const { trimEnabled, trim } = values as { + trimEnabled?: boolean + trim?: "auto" | number + } + + // If not enabled, don't apply trim at all + if (!trimEnabled) return + + // Auto mode (similar to rotate's "auto"): send boolean true + if (trim === "auto" || typeof trim === "undefined") { + transforms.trim = true + return + } + + // Numeric threshold 1–99 + if (typeof trim === "number") { + const threshold = Math.trunc(trim) + if (threshold >= 1 && threshold <= 99) { + transforms.trim = threshold + } + } + }, aiChangeBackground: (values, transforms) => { if (values.changebg) { if (SIMPLE_OVERLAY_TEXT_REGEX.test(values.changebg as string)) { From 6293e838a82a70a3cd098e393347811b90b21221 Mon Sep 17 00:00:00 2001 From: Piyush Aryan Date: Wed, 21 Jan 2026 23:08:34 +0530 Subject: [PATCH 02/11] fix: update trim transformation schema and default values for improved functionality --- .../imagekit-editor-dev/src/schema/index.ts | 50 +++++++------------ 1 file changed, 19 insertions(+), 31 deletions(-) diff --git a/packages/imagekit-editor-dev/src/schema/index.ts b/packages/imagekit-editor-dev/src/schema/index.ts index e93f2b0..e68ae88 100644 --- a/packages/imagekit-editor-dev/src/schema/index.ts +++ b/packages/imagekit-editor-dev/src/schema/index.ts @@ -1580,28 +1580,25 @@ export const transformationSchema: TransformationSchema[] = [ name: "Trim", description: "Trim solid or nearly solid backgrounds from the edges of the image, leaving only the central object.", - docsLink: "https://imagekit.io/docs/effects-and-enhancements", + docsLink: "https://imagekit.io/docs/effects-and-enhancements#trim-edges---t", defaultTransformation: {}, schema: z - .object({ - trimEnabled: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - trim: z - .union([ - z.literal("auto"), - z.coerce - .number({ - invalid_type_error: "Should be a number.", - }) - .int() - .min(1) - .max(99), - ]) - .optional(), - }) + .object({ + trimEnabled: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + trim: + z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(1) + .max(99) + .optional(), + }) .refine( (val) => { if ( @@ -1635,11 +1632,10 @@ export const transformationSchema: TransformationSchema[] = [ helpText: "Trim edges for images with solid or near-solid backgrounds. Use a threshold between 1 and 99.", fieldProps: { - defaultValue: "auto", + defaultValue: 10, min: 1, max: 99, step: 1, - autoOption: true, }, isVisible: ({ trimEnabled }) => trimEnabled === true, }, @@ -3133,18 +3129,10 @@ export const transformationFormatters: Record< trim: (values, transforms) => { const { trimEnabled, trim } = values as { trimEnabled?: boolean - trim?: "auto" | number + trim?: "default" | number } - - // If not enabled, don't apply trim at all if (!trimEnabled) return - // Auto mode (similar to rotate's "auto"): send boolean true - if (trim === "auto" || typeof trim === "undefined") { - transforms.trim = true - return - } - // Numeric threshold 1–99 if (typeof trim === "number") { const threshold = Math.trunc(trim) From d7fdd83ad630ccb5abd8242d8568ae76b2aaf0f3 Mon Sep 17 00:00:00 2001 From: Piyush Aryan Date: Thu, 22 Jan 2026 17:03:37 +0530 Subject: [PATCH 03/11] feat: add color replace transformation with validation and integration into schema --- .../components/common/ColorPickerField.tsx | 8 +- .../sidebar/transformation-config-sidebar.tsx | 2 + .../imagekit-editor-dev/src/schema/index.ts | 104 ++++++++++++++++++ 3 files changed, 112 insertions(+), 2 deletions(-) diff --git a/packages/imagekit-editor-dev/src/components/common/ColorPickerField.tsx b/packages/imagekit-editor-dev/src/components/common/ColorPickerField.tsx index 76eee42..afb84c4 100644 --- a/packages/imagekit-editor-dev/src/components/common/ColorPickerField.tsx +++ b/packages/imagekit-editor-dev/src/components/common/ColorPickerField.tsx @@ -7,17 +7,19 @@ import { PopoverTrigger, } from "@chakra-ui/react" import { memo, useEffect, useState } from "react" -import ColorPicker from "react-best-gradient-color-picker" +import ColorPicker, { ColorPickerProps } from "react-best-gradient-color-picker" import { useDebounce } from "../../hooks/useDebounce" const ColorPickerField = ({ fieldName, value, setValue, + fieldProps, }: { fieldName: string value: string setValue: (name: string, value: string) => void + fieldProps?: ColorPickerProps }) => { const [localValue, setLocalValue] = useState(value) @@ -35,7 +37,7 @@ const ColorPickerField = ({ .map((v) => v.toString(16).padStart(2, "0")) .join("") - if (a === undefined) { + if (fieldProps?.hideOpacity === true || a === undefined) { setLocalValue(`#${rgbHex}`) } else { const alphaDec = a > 1 ? a / 100 : a @@ -107,6 +109,8 @@ const ColorPickerField = ({ hideInputs hideAdvancedSliders hideColorGuide + // pass color picker props @ts-ignore + {...fieldProps} /> diff --git a/packages/imagekit-editor-dev/src/components/sidebar/transformation-config-sidebar.tsx b/packages/imagekit-editor-dev/src/components/sidebar/transformation-config-sidebar.tsx index 24a8bb6..c920463 100644 --- a/packages/imagekit-editor-dev/src/components/sidebar/transformation-config-sidebar.tsx +++ b/packages/imagekit-editor-dev/src/components/sidebar/transformation-config-sidebar.tsx @@ -55,6 +55,7 @@ import { SidebarBody } from "./sidebar-body" import { SidebarFooter } from "./sidebar-footer" import { SidebarHeader } from "./sidebar-header" import { SidebarRoot } from "./sidebar-root" +import { ColorPickerProps } from "react-best-gradient-color-picker" export const TransformationConfigSidebar: React.FC = () => { const { @@ -533,6 +534,7 @@ export const TransformationConfigSidebar: React.FC = () => { fieldName={field.name} value={watch(field.name) as string} setValue={setValue} + fieldProps={field.fieldProps as ColorPickerProps} /> ) : null} {field.fieldType === "anchor" ? ( diff --git a/packages/imagekit-editor-dev/src/schema/index.ts b/packages/imagekit-editor-dev/src/schema/index.ts index e68ae88..01783f2 100644 --- a/packages/imagekit-editor-dev/src/schema/index.ts +++ b/packages/imagekit-editor-dev/src/schema/index.ts @@ -1641,6 +1641,83 @@ export const transformationSchema: TransformationSchema[] = [ }, ], }, + { + key: "adjust-color-replace", + name: "Color Replace", + description: + "Replace specific colors in the image with a new color, while preserving the original image's luminance and chroma relationships.", + docsLink: "https://imagekit.io/docs/effects-and-enhancements#color-replace---cr", + defaultTransformation: {}, + schema: z + .object({ + toColor: colorValidator, + tolerance: z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(0) + .max(100) + .optional(), + fromColor: z.union([colorValidator, z.literal("")]).optional(), + }) + .refine( + (val) => { + // At least toColor must be provided + return val.toColor !== undefined && val.toColor !== "" + }, + { + message: "To Color is required", + path: ["toColor"], + }, + ), + transformations: [ + { + label: "To Color", + name: "toColor", + fieldType: "color-picker", + examples: ["FFFFFF", "FF0000"], + fieldProps:{ + hideOpacity: true, + showHexAlpha: false, + }, + isTransformation: false, + transformationGroup: "colorReplace", + helpText: + "Select the target color to replace with.", + }, { + label: "Tolerance", + name: "tolerance", + fieldType: "slider", + isTransformation: false, + transformationGroup: "colorReplace", + helpText: + "Set the tolerance for the color replacement. Use a number between 0 and 100. Lower values are more precise, but may not work for all colors. Higher values are more forgiving, but may introduce more color variations.", + fieldProps: { + defaultValue: 35, + min: 0, + max: 100, + step: 1, + }, + }, + { + label: "From Color", + examples: ["FFFFFF", "FF0000"], + name: "fromColor", + fieldType: "color-picker", + isTransformation: false, + fieldProps:{ + hideOpacity: true, + showHexAlpha: false, + }, + transformationGroup: "colorReplace", + helpText: + "Select the source color you want to replace (optional - if not specified, dominant color will be replaced).", + }, + + + ], + }, ], }, { @@ -3161,4 +3238,31 @@ export const transformationFormatters: Record< transforms.rotation = "auto" } }, + colorReplace: (values, transforms) => { + const { fromColor, toColor, tolerance } = values as { + fromColor?: string + toColor?: string + tolerance?: number + } + + // Color replace requires at least toColor + if (!toColor || toColor === "") return + + const params: string[] = [] + + // Remove # from colors if present + const cleanToColor = (toColor as string).replace(/^#/, "") + params.push(cleanToColor) + if (tolerance !== undefined && tolerance !== null) { + params.push(String(tolerance)) + } + // Check if fromColor is provided and not empty + if (fromColor && fromColor !== "") { + const cleanFromColor = (fromColor as string).replace(/^#/, "") + params.push(cleanFromColor) + } + + + transforms.cr = params.join("_") + }, } From bc2da353e87793c4a91899ffdf259c80c74bc197 Mon Sep 17 00:00:00 2001 From: Piyush Aryan Date: Tue, 27 Jan 2026 15:03:43 +0530 Subject: [PATCH 04/11] feat: add border and sharpen transformations to image processing schema with validation and integration --- .../imagekit-editor-dev/src/schema/index.ts | 256 +++++++++++++++++- 1 file changed, 251 insertions(+), 5 deletions(-) diff --git a/packages/imagekit-editor-dev/src/schema/index.ts b/packages/imagekit-editor-dev/src/schema/index.ts index 01783f2..5c19bec 100644 --- a/packages/imagekit-editor-dev/src/schema/index.ts +++ b/packages/imagekit-editor-dev/src/schema/index.ts @@ -1575,6 +1575,70 @@ export const transformationSchema: TransformationSchema[] = [ }, ], }, + { + key: "adjust-border", + name: "Border", + description: + "Add a border to the image. Specify a border width and color.", + docsLink: "https://imagekit.io/docs/effects-and-enhancements#border---b", + defaultTransformation: {}, + schema: z + .object({ + borderWidth: z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(1) + .optional(), + borderColor: colorValidator + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, + { + message: "Border width and color are required", + path: [], + }, + ), + transformations: [ + { + label: "Border Width", + name: "borderWidth", + fieldType: "input", + isTransformation: false, + transformationGroup: "border", + helpText: + "Enter a border width", + fieldProps: { + defaultValue: 1, + min: 1, + max: 99, + step: 1, + }, + }, + { + label: "Border Color", + name: "borderColor", + fieldType: "color-picker", + isTransformation: false, + transformationGroup: "border", + helpText: + "Select the color of the border.", + fieldProps:{ + hideOpacity: true, + showHexAlpha: false, + defaultValue: "#000000", + }, + }, + ], + }, { key: "adjust-trim", name: "Trim", @@ -1713,9 +1777,77 @@ export const transformationSchema: TransformationSchema[] = [ transformationGroup: "colorReplace", helpText: "Select the source color you want to replace (optional - if not specified, dominant color will be replaced).", + }, + ], + }, + { + key: "adjust-sharpen", + name: "Sharpen", + description: + "Sharpen the image to highlight the edges and finer details within an image.", + docsLink: "https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen", + defaultTransformation: {}, + schema: z + .object({ + sharpenEnabled: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + sharpen: + z.union([ + z.literal("auto"), + z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(1) + .max(99) + ]) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, + { + message: "At least one value is required", + path: [], + }, + ), + transformations: [ + { + label: "Sharpen Image", + name: "sharpenEnabled", + fieldType: "switch", + isTransformation: false, + transformationGroup: "sharpen", + helpText: + "Toggle to sharpen the image to highlight the edges and finer details within an image.", + }, + { + label: "Threshold", + name: "sharpen", + fieldType: "slider", + isTransformation: false, + transformationGroup: "sharpen", + helpText: + "Sharpen the image to highlight the edges and finer details within an image. Use a threshold between 1 and 99.", + fieldProps: { + autoOption: true, + defaultValue: "auto", + min: 1, + max: 99, + step: 1, + }, + isVisible: ({ sharpenEnabled }) => sharpenEnabled === true, }, - - ], }, ], @@ -1723,11 +1855,9 @@ export const transformationSchema: TransformationSchema[] = [ { key: "ai", name: "AI Transformations", - items: [ - { + items: [ { key: "ai-removedotbg", name: "Remove Background using Remove.bg", - // This option removes the background using the third-party remove.bg service. description: "Remove the background of the image using Remove.bg (external service). This isolates the subject and makes the background transparent.", docsLink: @@ -2589,6 +2719,29 @@ export const transformationSchema: TransformationSchema[] = [ invalid_type_error: "Should be a number.", }) .optional(), + borderWidth: z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .optional(), + borderColor: colorValidator.optional(), + sharpenEnabled: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + sharpen: z + .union([ + z.literal("auto"), + z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(1) + .max(99), + ]) + .optional(), }) .refine( (val) => { @@ -2789,6 +2942,66 @@ export const transformationSchema: TransformationSchema[] = [ defaultValue: "0", }, }, + { + label: "Border Width", + name: "borderWidth", + fieldType: "input", + isTransformation: false, + transformationKey: "borderWidth", + transformationGroup: "imageLayer", + fieldProps: { + defaultValue: 0, + }, + helpText: + "Enter the width of the border of the overlay image.", + }, + { + label: "Border Color", + name: "borderColor", + fieldType: "color-picker", + isTransformation: false, + transformationKey: "borderColor", + transformationGroup: "imageLayer", + isVisible: ({ borderWidth }) => borderWidth as number > 0, + helpText: + "Select the color of the border of the overlay image.", + fieldProps: { + hideOpacity: true, + showHexAlpha: false, + defaultValue: "#000000", + }, + }, + { + label: "Sharpen Overlay", + name: "sharpenEnabled", + fieldType: "switch", + isTransformation: true, + transformationKey: "sharpenEnabled", + transformationGroup: "imageLayer", + helpText: + "Toggle to sharpen the overlay image to highlight edges and fine details.", + fieldProps: { + defaultValue: false, + }, + }, + { + label: "Sharpen Threshold", + name: "sharpen", + fieldType: "slider", + isTransformation: true, + transformationKey: "sharpen", + transformationGroup: "imageLayer", + helpText: + "Sharpen the overlay image. Use a threshold between 1 and 99 (or auto).", + fieldProps: { + autoOption: true, + defaultValue: "auto", + min: 1, + max: 99, + step: 1, + }, + isVisible: ({ sharpenEnabled }) => sharpenEnabled === true, + } ], }, ], @@ -3170,6 +3383,18 @@ export const transformationFormatters: Record< overlayTransform.blur = values.blur } + // Sharpen overlay (same semantics as base-image sharpen: auto => empty string) + if (values.sharpenEnabled === true) { + if (values.sharpen === "auto") { + overlayTransform.sharpen = "" + } else if (typeof values.sharpen === "number") { + overlayTransform.sharpen = values.sharpen + } + } + if ((values.borderWidth && typeof values.borderWidth === "number" && values.borderWidth > 0) && (values.borderColor && typeof values.borderColor === "string")) { + overlayTransform.b = `${values.borderWidth}_${values.borderColor.replace(/^#/, "")}` + } + if (Object.keys(overlayTransform).length > 0) { overlay.transformation = [overlayTransform] } @@ -3265,4 +3490,25 @@ export const transformationFormatters: Record< transforms.cr = params.join("_") }, + border: (values, transforms) => { + const { borderWidth, borderColor } = values as { + borderWidth?: number + borderColor?: string + } + if(!borderWidth || !borderColor) return + const cleanBorderColor = borderColor.replace(/^#/, "") + transforms.b = `${borderWidth}_${cleanBorderColor}` + }, + sharpen: (values, transforms) => { + const { sharpenEnabled, sharpen } = values as { + sharpenEnabled?: boolean + sharpen?: "auto" | number + } + if(!sharpenEnabled) return + if(sharpen === "auto") { + transforms.sharpen = "" + } else { + transforms.sharpen = sharpen + } + }, } From 0cac58cd6060dad5460161630c8d16078270c678 Mon Sep 17 00:00:00 2001 From: Piyush Aryan Date: Tue, 27 Jan 2026 16:21:31 +0530 Subject: [PATCH 05/11] refactor: improve transformation schema for border, trim, and sharpen with enhanced validation and default values --- .../imagekit-editor-dev/src/schema/index.ts | 899 +++++++++--------- 1 file changed, 457 insertions(+), 442 deletions(-) diff --git a/packages/imagekit-editor-dev/src/schema/index.ts b/packages/imagekit-editor-dev/src/schema/index.ts index 5c19bec..d404cd6 100644 --- a/packages/imagekit-editor-dev/src/schema/index.ts +++ b/packages/imagekit-editor-dev/src/schema/index.ts @@ -1583,16 +1583,16 @@ export const transformationSchema: TransformationSchema[] = [ docsLink: "https://imagekit.io/docs/effects-and-enhancements#border---b", defaultTransformation: {}, schema: z - .object({ - borderWidth: z.coerce - .number({ - invalid_type_error: "Should be a number.", - }) - .int() - .min(1) - .optional(), - borderColor: colorValidator - }) + .object({ + borderWidth: z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(1) + .optional(), + borderColor: colorValidator + }) .refine( (val) => { if ( @@ -1631,7 +1631,7 @@ export const transformationSchema: TransformationSchema[] = [ transformationGroup: "border", helpText: "Select the color of the border.", - fieldProps:{ + fieldProps: { hideOpacity: true, showHexAlpha: false, defaultValue: "#000000", @@ -1647,13 +1647,13 @@ export const transformationSchema: TransformationSchema[] = [ docsLink: "https://imagekit.io/docs/effects-and-enhancements#trim-edges---t", defaultTransformation: {}, schema: z - .object({ - trimEnabled: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - trim: + .object({ + trimEnabled: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + trim: z.coerce .number({ invalid_type_error: "Should be a number.", @@ -1661,8 +1661,8 @@ export const transformationSchema: TransformationSchema[] = [ .int() .min(1) .max(99) - .optional(), - }) + .optional(), + }) .refine( (val) => { if ( @@ -1713,50 +1713,51 @@ export const transformationSchema: TransformationSchema[] = [ docsLink: "https://imagekit.io/docs/effects-and-enhancements#color-replace---cr", defaultTransformation: {}, schema: z - .object({ - toColor: colorValidator, - tolerance: z.coerce - .number({ - invalid_type_error: "Should be a number.", - }) - .int() - .min(0) - .max(100) - .optional(), - fromColor: z.union([colorValidator, z.literal("")]).optional(), - }) - .refine( - (val) => { - // At least toColor must be provided - return val.toColor !== undefined && val.toColor !== "" - }, - { - message: "To Color is required", - path: ["toColor"], - }, - ), + .object({ + toColor: colorValidator, + tolerance: z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(0) + .max(100) + .optional(), + fromColor: z.union([colorValidator, z.literal("")]).optional(), + }) + .refine( + (val) => { + // At least toColor must be provided + return val.toColor !== undefined && val.toColor !== "" + }, + { + message: "To Color is required", + path: ["toColor"], + }, + ), transformations: [ { - label: "To Color", - name: "toColor", - fieldType: "color-picker", + label: "From Color", examples: ["FFFFFF", "FF0000"], - fieldProps:{ + name: "fromColor", + fieldType: "color-picker", + isTransformation: false, + fieldProps: { hideOpacity: true, showHexAlpha: false, }, - isTransformation: false, transformationGroup: "colorReplace", helpText: - "Select the target color to replace with.", - }, { + "Select the source color you want to replace (optional - if not specified, dominant color will be replaced).", + }, + { label: "Tolerance", name: "tolerance", fieldType: "slider", isTransformation: false, transformationGroup: "colorReplace", helpText: - "Set the tolerance for the color replacement. Use a number between 0 and 100. Lower values are more precise, but may not work for all colors. Higher values are more forgiving, but may introduce more color variations.", + "Set the tolerance for the color replacement. Use a number between 0 and 100. Lower values are more precise, but may not work for all colors. Higher values are more forgiving, but may introduce more color variations.", fieldProps: { defaultValue: 35, min: 0, @@ -1765,19 +1766,19 @@ export const transformationSchema: TransformationSchema[] = [ }, }, { - label: "From Color", - examples: ["FFFFFF", "FF0000"], - name: "fromColor", + label: "To Color", + name: "toColor", fieldType: "color-picker", - isTransformation: false, - fieldProps:{ + examples: ["FFFFFF", "FF0000"], + fieldProps: { hideOpacity: true, showHexAlpha: false, }, + isTransformation: false, transformationGroup: "colorReplace", helpText: - "Select the source color you want to replace (optional - if not specified, dominant color will be replaced).", - }, + "Select the target color to replace with.", + }, ], }, { @@ -1788,25 +1789,22 @@ export const transformationSchema: TransformationSchema[] = [ docsLink: "https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen", defaultTransformation: {}, schema: z - .object({ - sharpenEnabled: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - sharpen: - z.union([ - z.literal("auto"), - z.coerce + .object({ + sharpenEnabled: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + sharpen: + z.coerce .number({ invalid_type_error: "Should be a number.", }) .int() .min(1) .max(99) - ]) - .optional(), - }) + .optional(), + }) .refine( (val) => { if ( @@ -1838,13 +1836,12 @@ export const transformationSchema: TransformationSchema[] = [ isTransformation: false, transformationGroup: "sharpen", helpText: - "Sharpen the image to highlight the edges and finer details within an image. Use a threshold between 1 and 99.", + "Sharpen the image to highlight the edges and finer details within an image. Control the intensity of this effect using a threshold value between 1% and 99%.", fieldProps: { - autoOption: true, - defaultValue: "auto", min: 1, max: 99, step: 1, + defaultValue: 50, }, isVisible: ({ sharpenEnabled }) => sharpenEnabled === true, }, @@ -1855,374 +1852,374 @@ export const transformationSchema: TransformationSchema[] = [ { key: "ai", name: "AI Transformations", - items: [ { - key: "ai-removedotbg", - name: "Remove Background using Remove.bg", - description: - "Remove the background of the image using Remove.bg (external service). This isolates the subject and makes the background transparent.", - docsLink: - "https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg", - defaultTransformation: {}, - schema: z - .object({ - removedotbg: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ + items: [{ + key: "ai-removedotbg", + name: "Remove Background using Remove.bg", + description: + "Remove the background of the image using Remove.bg (external service). This isolates the subject and makes the background transparent.", + docsLink: + "https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg", + defaultTransformation: {}, + schema: z + .object({ + removedotbg: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, { - label: "Remove Background using Remove.bg", - name: "removedotbg", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiRemoveBackgroundExternal", - helpText: - "Toggle to remove the background using Remove.bg. Processing may take a few seconds depending on image complexity.", + message: "At least one value is required", + path: [], }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Remove Background using Remove.bg to {imageList.length} items. ", + ), + transformations: [ + { + label: "Remove Background using Remove.bg", + name: "removedotbg", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiRemoveBackgroundExternal", + helpText: + "Toggle to remove the background using Remove.bg. Processing may take a few seconds depending on image complexity.", }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Remove Background using Remove.bg to {imageList.length} items. ", }, - { - key: "ai-bgremove", - name: "Remove Background using ImageKit AI", - description: - "Remove the background using ImageKit's built-in background removal model. This method is cost-effective compared to Remove.bg.", - docsLink: - "https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove", - defaultTransformation: {}, - schema: z - .object({ - bgremove: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ + }, + { + key: "ai-bgremove", + name: "Remove Background using ImageKit AI", + description: + "Remove the background using ImageKit's built-in background removal model. This method is cost-effective compared to Remove.bg.", + docsLink: + "https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove", + defaultTransformation: {}, + schema: z + .object({ + bgremove: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, { - label: "Remove Background using ImageKit AI", - name: "bgremove", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiRemoveBackground", - helpText: - "Toggle to remove the background using ImageKit's own background removal.", + message: "At least one value is required", + path: [], }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Remove Background using ImageKit AI to {imageList.length} items. ", + ), + transformations: [ + { + label: "Remove Background using ImageKit AI", + name: "bgremove", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiRemoveBackground", + helpText: + "Toggle to remove the background using ImageKit's own background removal.", }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Remove Background using ImageKit AI to {imageList.length} items. ", }, - { - key: "ai-changebg", - name: "Change Background", - description: - "Replace the background of the image with a new scene described by a text prompt. Use AI to generate a new background.", - docsLink: - "https://imagekit.io/docs/ai-transformations#change-background-e-changebg", - defaultTransformation: {}, - schema: z - .object({ - changebg: z.string().optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ + }, + { + key: "ai-changebg", + name: "Change Background", + description: + "Replace the background of the image with a new scene described by a text prompt. Use AI to generate a new background.", + docsLink: + "https://imagekit.io/docs/ai-transformations#change-background-e-changebg", + defaultTransformation: {}, + schema: z + .object({ + changebg: z.string().optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, { - label: "Change Background", - name: "changebg", - fieldType: "input", - isTransformation: true, - transformationKey: "aiChangeBackground", - transformationGroup: "aiChangeBackground", - helpText: "Enter a descriptive prompt for the new background.", - examples: ["snowy mountains", "sunset beach"], + message: "At least one value is required", + path: [], }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Change Background to {imageList.length} items. ", + ), + transformations: [ + { + label: "Change Background", + name: "changebg", + fieldType: "input", + isTransformation: true, + transformationKey: "aiChangeBackground", + transformationGroup: "aiChangeBackground", + helpText: "Enter a descriptive prompt for the new background.", + examples: ["snowy mountains", "sunset beach"], }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Change Background to {imageList.length} items. ", }, - { - key: "ai-edit", - name: "Edit Image using AI", - description: - "Use AI to modify the image based on a descriptive prompt. Add or remove objects or alter colors and textures.", - docsLink: - "https://imagekit.io/docs/ai-transformations#edit-image-e-edit", - defaultTransformation: {}, - schema: z - .object({ - edit: z.string().optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ + }, + { + key: "ai-edit", + name: "Edit Image using AI", + description: + "Use AI to modify the image based on a descriptive prompt. Add or remove objects or alter colors and textures.", + docsLink: + "https://imagekit.io/docs/ai-transformations#edit-image-e-edit", + defaultTransformation: {}, + schema: z + .object({ + edit: z.string().optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, { - label: "Edit Image using AI", - name: "edit", - fieldType: "input", - isTransformation: true, - transformationKey: "e-edit-prompt", - helpText: "Enter a prompt describing how to edit the image.", - examples: ["add sunglasses", "make the sky blue"], + message: "At least one value is required", + path: [], }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Edit Image using AI to {imageList.length} items. ", + ), + transformations: [ + { + label: "Edit Image using AI", + name: "edit", + fieldType: "input", + isTransformation: true, + transformationKey: "e-edit-prompt", + helpText: "Enter a prompt describing how to edit the image.", + examples: ["add sunglasses", "make the sky blue"], }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Edit Image using AI to {imageList.length} items. ", }, - { - key: "ai-dropshadow", - name: "Drop Shadow", - description: - "Add a realistic AI-generated drop shadow around the object. Requires a transparent background; remove the background first for best results.", - docsLink: - "https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow", - defaultTransformation: {}, - schema: z - .object({ - dropshadow: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ + }, + { + key: "ai-dropshadow", + name: "Drop Shadow", + description: + "Add a realistic AI-generated drop shadow around the object. Requires a transparent background; remove the background first for best results.", + docsLink: + "https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow", + defaultTransformation: {}, + schema: z + .object({ + dropshadow: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, { - label: "Drop Shadow", - name: "dropshadow", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiDropShadow", - helpText: - "Toggle to add an AI-generated drop shadow. Requires transparent background.", + message: "At least one value is required", + path: [], }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Drop Shadow to {imageList.length} items. ", + ), + transformations: [ + { + label: "Drop Shadow", + name: "dropshadow", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiDropShadow", + helpText: + "Toggle to add an AI-generated drop shadow. Requires transparent background.", }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Drop Shadow to {imageList.length} items. ", }, - { - key: "ai-retouch", - name: "Retouch", - description: "Improve the quality of the image using AI retouching.", - docsLink: - "https://imagekit.io/docs/ai-transformations#retouch-e-retouch", - defaultTransformation: {}, - schema: z - .object({ - retouch: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ + }, + { + key: "ai-retouch", + name: "Retouch", + description: "Improve the quality of the image using AI retouching.", + docsLink: + "https://imagekit.io/docs/ai-transformations#retouch-e-retouch", + defaultTransformation: {}, + schema: z + .object({ + retouch: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, { - label: "Retouch", - name: "retouch", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiRetouch", - helpText: - "Toggle to apply AI retouching and enhance image quality.", + message: "At least one value is required", + path: [], }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Retouch to {imageList.length} items. ", + ), + transformations: [ + { + label: "Retouch", + name: "retouch", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiRetouch", + helpText: + "Toggle to apply AI retouching and enhance image quality.", }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Retouch to {imageList.length} items. ", }, - { - key: "ai-upscale", - name: "Upscale", - description: - "Increase the resolution of low-resolution images using AI upscaling. The output can be up to 16 MP.", - docsLink: - "https://imagekit.io/docs/ai-transformations#upscale-e-upscale", - defaultTransformation: {}, - schema: z - .object({ - upscale: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ + }, + { + key: "ai-upscale", + name: "Upscale", + description: + "Increase the resolution of low-resolution images using AI upscaling. The output can be up to 16 MP.", + docsLink: + "https://imagekit.io/docs/ai-transformations#upscale-e-upscale", + defaultTransformation: {}, + schema: z + .object({ + upscale: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, { - label: "Upscale", - name: "upscale", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiUpscale", - helpText: - "Toggle to increase resolution of the image using AI upscaling (max 16 MP input).", + message: "At least one value is required", + path: [], }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Upscale to {imageList.length} items. ", + ), + transformations: [ + { + label: "Upscale", + name: "upscale", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiUpscale", + helpText: + "Toggle to increase resolution of the image using AI upscaling (max 16 MP input).", }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Upscale to {imageList.length} items. ", }, - { - key: "ai-genvar", - name: "Generate Variations", - description: - "Create a new variation of the original image using AI, altering colors and textures while preserving the structure.", - docsLink: - "https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar", - defaultTransformation: {}, - schema: z - .object({ - genvar: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ + }, + { + key: "ai-genvar", + name: "Generate Variations", + description: + "Create a new variation of the original image using AI, altering colors and textures while preserving the structure.", + docsLink: + "https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar", + defaultTransformation: {}, + schema: z + .object({ + genvar: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, { - label: "Generate Variations", - name: "genvar", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiVariation", - helpText: - "Toggle to generate a new variation of the image using AI.", + message: "At least one value is required", + path: [], }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to generate variations of {imageList.length} items. ", + ), + transformations: [ + { + label: "Generate Variations", + name: "genvar", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiVariation", + helpText: + "Toggle to generate a new variation of the image using AI.", }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to generate variations of {imageList.length} items. ", }, + }, ], }, { @@ -2704,11 +2701,19 @@ export const transformationSchema: TransformationSchema[] = [ invalid_type_error: "Should be a number.", }) .optional(), - trim: z.coerce + trimEnabled: z.coerce .boolean({ invalid_type_error: "Should be a boolean.", }) .optional(), + trimThreshold: z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(1) + .max(99) + .optional(), quality: z.coerce .number({ invalid_type_error: "Should be a number.", @@ -2730,18 +2735,15 @@ export const transformationSchema: TransformationSchema[] = [ invalid_type_error: "Should be a boolean.", }) .optional(), - sharpen: z - .union([ - z.literal("auto"), - z.coerce - .number({ - invalid_type_error: "Should be a number.", - }) - .int() - .min(1) - .max(99), - ]) - .optional(), + sharpen: + z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(1) + .max(99) + .optional(), }) .refine( (val) => { @@ -2902,16 +2904,32 @@ export const transformationSchema: TransformationSchema[] = [ }, { label: "Trim", - name: "trim", + name: "trimEnabled", fieldType: "switch", isTransformation: true, - transformationKey: "trim", + transformationKey: "trimEnabled", transformationGroup: "imageLayer", helpText: "Control trimming of the overlay image.", fieldProps: { defaultValue: true, }, }, + { + label: "Trim Threshold", + name: "trimThreshold", + fieldType: "slider", + isTransformation: true, + transformationKey: "trimThreshold", + transformationGroup: "imageLayer", + helpText: "Control the intensity of this effect using a threshold value between 1% and 99%.", + fieldProps: { + min: 1, + max: 99, + step: 1, + defaultValue: 10, + }, + isVisible: ({ trimEnabled }) => trimEnabled === true, + }, { label: "Quality", name: "quality", @@ -2975,7 +2993,7 @@ export const transformationSchema: TransformationSchema[] = [ label: "Sharpen Overlay", name: "sharpenEnabled", fieldType: "switch", - isTransformation: true, + isTransformation: false, transformationKey: "sharpenEnabled", transformationGroup: "imageLayer", helpText: @@ -2988,15 +3006,14 @@ export const transformationSchema: TransformationSchema[] = [ label: "Sharpen Threshold", name: "sharpen", fieldType: "slider", - isTransformation: true, + isTransformation: false, transformationKey: "sharpen", transformationGroup: "imageLayer", helpText: - "Sharpen the overlay image. Use a threshold between 1 and 99 (or auto).", + "Sharpen the overlay image. Control the intensity of this effect using a threshold value between 1% and 99%.", fieldProps: { - autoOption: true, - defaultValue: "auto", min: 1, + defaultValue: 50, max: 99, step: 1, }, @@ -3370,9 +3387,8 @@ export const transformationFormatters: Record< if (values.rotation) { overlayTransform.rotation = values.rotation } - - if (typeof values.trim === "boolean") { - overlayTransform.trim = values.trim + if (values.trimEnabled === true && typeof values.trimThreshold === "number") { + overlayTransform.t = values.trimThreshold } if (values.quality) { @@ -3383,11 +3399,10 @@ export const transformationFormatters: Record< overlayTransform.blur = values.blur } - // Sharpen overlay (same semantics as base-image sharpen: auto => empty string) if (values.sharpenEnabled === true) { - if (values.sharpen === "auto") { + if (values.sharpen === 50) { overlayTransform.sharpen = "" - } else if (typeof values.sharpen === "number") { + } else { overlayTransform.sharpen = values.sharpen } } @@ -3474,7 +3489,7 @@ export const transformationFormatters: Record< if (!toColor || toColor === "") return const params: string[] = [] - + // Remove # from colors if present const cleanToColor = (toColor as string).replace(/^#/, "") params.push(cleanToColor) @@ -3486,8 +3501,8 @@ export const transformationFormatters: Record< const cleanFromColor = (fromColor as string).replace(/^#/, "") params.push(cleanFromColor) } - - + + transforms.cr = params.join("_") }, border: (values, transforms) => { @@ -3495,17 +3510,17 @@ export const transformationFormatters: Record< borderWidth?: number borderColor?: string } - if(!borderWidth || !borderColor) return + if (!borderWidth || !borderColor) return const cleanBorderColor = borderColor.replace(/^#/, "") transforms.b = `${borderWidth}_${cleanBorderColor}` }, sharpen: (values, transforms) => { const { sharpenEnabled, sharpen } = values as { sharpenEnabled?: boolean - sharpen?: "auto" | number + sharpen: number } - if(!sharpenEnabled) return - if(sharpen === "auto") { + if (!sharpenEnabled) return + if (sharpen === 50) { transforms.sharpen = "" } else { transforms.sharpen = sharpen From a5e7fefc3b7b4a6797b8f1345d906d331bb6b19e Mon Sep 17 00:00:00 2001 From: Piyush Aryan <105715267+piyushryn@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:54:08 +0530 Subject: [PATCH 06/11] Update packages/imagekit-editor-dev/src/schema/index.ts Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- packages/imagekit-editor-dev/src/schema/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/imagekit-editor-dev/src/schema/index.ts b/packages/imagekit-editor-dev/src/schema/index.ts index d404cd6..0e3b477 100644 --- a/packages/imagekit-editor-dev/src/schema/index.ts +++ b/packages/imagekit-editor-dev/src/schema/index.ts @@ -1750,7 +1750,7 @@ export const transformationSchema: TransformationSchema[] = [ helpText: "Select the source color you want to replace (optional - if not specified, dominant color will be replaced).", }, - { + { label: "Tolerance", name: "tolerance", fieldType: "slider", From cc2aeb3b3c15cbeabf323d59bf3235f8d336eaa1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 11:24:31 +0000 Subject: [PATCH 07/11] Initial plan From 87a681a39b8e09e04d646d7453ff3beaae0e94ea Mon Sep 17 00:00:00 2001 From: Piyush Aryan <105715267+piyushryn@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:55:30 +0530 Subject: [PATCH 08/11] Update packages/imagekit-editor-dev/src/components/common/ColorPickerField.tsx Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .../src/components/common/ColorPickerField.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/imagekit-editor-dev/src/components/common/ColorPickerField.tsx b/packages/imagekit-editor-dev/src/components/common/ColorPickerField.tsx index afb84c4..278de52 100644 --- a/packages/imagekit-editor-dev/src/components/common/ColorPickerField.tsx +++ b/packages/imagekit-editor-dev/src/components/common/ColorPickerField.tsx @@ -109,7 +109,7 @@ const ColorPickerField = ({ hideInputs hideAdvancedSliders hideColorGuide - // pass color picker props @ts-ignore + // @ts-expect-error - fieldProps may include props not declared in ColorPickerProps, but they are intentionally forwarded {...fieldProps} /> From d04269d25c04d14beb57462662e254563052d173 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 27 Jan 2026 11:27:19 +0000 Subject: [PATCH 09/11] fix: correct formatting for items array opening bracket at line 1855 Co-authored-by: piyushryn <105715267+piyushryn@users.noreply.github.com> --- .../imagekit-editor-dev/src/schema/index.ts | 806 +++++++++--------- 1 file changed, 406 insertions(+), 400 deletions(-) diff --git a/packages/imagekit-editor-dev/src/schema/index.ts b/packages/imagekit-editor-dev/src/schema/index.ts index 0e3b477..c8885bb 100644 --- a/packages/imagekit-editor-dev/src/schema/index.ts +++ b/packages/imagekit-editor-dev/src/schema/index.ts @@ -1580,7 +1580,8 @@ export const transformationSchema: TransformationSchema[] = [ name: "Border", description: "Add a border to the image. Specify a border width and color.", - docsLink: "https://imagekit.io/docs/effects-and-enhancements#border---b", + docsLink: + "https://imagekit.io/docs/effects-and-enhancements#border---b", defaultTransformation: {}, schema: z .object({ @@ -1591,7 +1592,7 @@ export const transformationSchema: TransformationSchema[] = [ .int() .min(1) .optional(), - borderColor: colorValidator + borderColor: colorValidator, }) .refine( (val) => { @@ -1614,8 +1615,7 @@ export const transformationSchema: TransformationSchema[] = [ fieldType: "input", isTransformation: false, transformationGroup: "border", - helpText: - "Enter a border width", + helpText: "Enter a border width", fieldProps: { defaultValue: 1, min: 1, @@ -1629,8 +1629,7 @@ export const transformationSchema: TransformationSchema[] = [ fieldType: "color-picker", isTransformation: false, transformationGroup: "border", - helpText: - "Select the color of the border.", + helpText: "Select the color of the border.", fieldProps: { hideOpacity: true, showHexAlpha: false, @@ -1644,7 +1643,8 @@ export const transformationSchema: TransformationSchema[] = [ name: "Trim", description: "Trim solid or nearly solid backgrounds from the edges of the image, leaving only the central object.", - docsLink: "https://imagekit.io/docs/effects-and-enhancements#trim-edges---t", + docsLink: + "https://imagekit.io/docs/effects-and-enhancements#trim-edges---t", defaultTransformation: {}, schema: z .object({ @@ -1653,15 +1653,14 @@ export const transformationSchema: TransformationSchema[] = [ invalid_type_error: "Should be a boolean.", }) .optional(), - trim: - z.coerce - .number({ - invalid_type_error: "Should be a number.", - }) - .int() - .min(1) - .max(99) - .optional(), + trim: z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(1) + .max(99) + .optional(), }) .refine( (val) => { @@ -1710,7 +1709,8 @@ export const transformationSchema: TransformationSchema[] = [ name: "Color Replace", description: "Replace specific colors in the image with a new color, while preserving the original image's luminance and chroma relationships.", - docsLink: "https://imagekit.io/docs/effects-and-enhancements#color-replace---cr", + docsLink: + "https://imagekit.io/docs/effects-and-enhancements#color-replace---cr", defaultTransformation: {}, schema: z .object({ @@ -1776,8 +1776,7 @@ export const transformationSchema: TransformationSchema[] = [ }, isTransformation: false, transformationGroup: "colorReplace", - helpText: - "Select the target color to replace with.", + helpText: "Select the target color to replace with.", }, ], }, @@ -1786,7 +1785,8 @@ export const transformationSchema: TransformationSchema[] = [ name: "Sharpen", description: "Sharpen the image to highlight the edges and finer details within an image.", - docsLink: "https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen", + docsLink: + "https://imagekit.io/docs/effects-and-enhancements#sharpen---e-sharpen", defaultTransformation: {}, schema: z .object({ @@ -1795,15 +1795,14 @@ export const transformationSchema: TransformationSchema[] = [ invalid_type_error: "Should be a boolean.", }) .optional(), - sharpen: - z.coerce - .number({ - invalid_type_error: "Should be a number.", - }) - .int() - .min(1) - .max(99) - .optional(), + sharpen: z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(1) + .max(99) + .optional(), }) .refine( (val) => { @@ -1852,374 +1851,375 @@ export const transformationSchema: TransformationSchema[] = [ { key: "ai", name: "AI Transformations", - items: [{ - key: "ai-removedotbg", - name: "Remove Background using Remove.bg", - description: - "Remove the background of the image using Remove.bg (external service). This isolates the subject and makes the background transparent.", - docsLink: - "https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg", - defaultTransformation: {}, - schema: z - .object({ - removedotbg: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ - { - label: "Remove Background using Remove.bg", - name: "removedotbg", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiRemoveBackgroundExternal", - helpText: - "Toggle to remove the background using Remove.bg. Processing may take a few seconds depending on image complexity.", + items: [ + { + key: "ai-removedotbg", + name: "Remove Background using Remove.bg", + description: + "Remove the background of the image using Remove.bg (external service). This isolates the subject and makes the background transparent.", + docsLink: + "https://imagekit.io/docs/ai-transformations#background-removal-e-removedotbg", + defaultTransformation: {}, + schema: z + .object({ + removedotbg: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, + { + message: "At least one value is required", + path: [], + }, + ), + transformations: [ + { + label: "Remove Background using Remove.bg", + name: "removedotbg", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiRemoveBackgroundExternal", + helpText: + "Toggle to remove the background using Remove.bg. Processing may take a few seconds depending on image complexity.", + }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Remove Background using Remove.bg to {imageList.length} items. ", }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Remove Background using Remove.bg to {imageList.length} items. ", }, - }, - { - key: "ai-bgremove", - name: "Remove Background using ImageKit AI", - description: - "Remove the background using ImageKit's built-in background removal model. This method is cost-effective compared to Remove.bg.", - docsLink: - "https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove", - defaultTransformation: {}, - schema: z - .object({ - bgremove: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ - { - label: "Remove Background using ImageKit AI", - name: "bgremove", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiRemoveBackground", - helpText: - "Toggle to remove the background using ImageKit's own background removal.", + { + key: "ai-bgremove", + name: "Remove Background using ImageKit AI", + description: + "Remove the background using ImageKit's built-in background removal model. This method is cost-effective compared to Remove.bg.", + docsLink: + "https://imagekit.io/docs/ai-transformations#imagekit-background-removal-e-bgremove", + defaultTransformation: {}, + schema: z + .object({ + bgremove: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, + { + message: "At least one value is required", + path: [], + }, + ), + transformations: [ + { + label: "Remove Background using ImageKit AI", + name: "bgremove", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiRemoveBackground", + helpText: + "Toggle to remove the background using ImageKit's own background removal.", + }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Remove Background using ImageKit AI to {imageList.length} items. ", }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Remove Background using ImageKit AI to {imageList.length} items. ", }, - }, - { - key: "ai-changebg", - name: "Change Background", - description: - "Replace the background of the image with a new scene described by a text prompt. Use AI to generate a new background.", - docsLink: - "https://imagekit.io/docs/ai-transformations#change-background-e-changebg", - defaultTransformation: {}, - schema: z - .object({ - changebg: z.string().optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ - { - label: "Change Background", - name: "changebg", - fieldType: "input", - isTransformation: true, - transformationKey: "aiChangeBackground", - transformationGroup: "aiChangeBackground", - helpText: "Enter a descriptive prompt for the new background.", - examples: ["snowy mountains", "sunset beach"], + { + key: "ai-changebg", + name: "Change Background", + description: + "Replace the background of the image with a new scene described by a text prompt. Use AI to generate a new background.", + docsLink: + "https://imagekit.io/docs/ai-transformations#change-background-e-changebg", + defaultTransformation: {}, + schema: z + .object({ + changebg: z.string().optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, + { + message: "At least one value is required", + path: [], + }, + ), + transformations: [ + { + label: "Change Background", + name: "changebg", + fieldType: "input", + isTransformation: true, + transformationKey: "aiChangeBackground", + transformationGroup: "aiChangeBackground", + helpText: "Enter a descriptive prompt for the new background.", + examples: ["snowy mountains", "sunset beach"], + }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Change Background to {imageList.length} items. ", }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Change Background to {imageList.length} items. ", }, - }, - { - key: "ai-edit", - name: "Edit Image using AI", - description: - "Use AI to modify the image based on a descriptive prompt. Add or remove objects or alter colors and textures.", - docsLink: - "https://imagekit.io/docs/ai-transformations#edit-image-e-edit", - defaultTransformation: {}, - schema: z - .object({ - edit: z.string().optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ - { - label: "Edit Image using AI", - name: "edit", - fieldType: "input", - isTransformation: true, - transformationKey: "e-edit-prompt", - helpText: "Enter a prompt describing how to edit the image.", - examples: ["add sunglasses", "make the sky blue"], + { + key: "ai-edit", + name: "Edit Image using AI", + description: + "Use AI to modify the image based on a descriptive prompt. Add or remove objects or alter colors and textures.", + docsLink: + "https://imagekit.io/docs/ai-transformations#edit-image-e-edit", + defaultTransformation: {}, + schema: z + .object({ + edit: z.string().optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, + { + message: "At least one value is required", + path: [], + }, + ), + transformations: [ + { + label: "Edit Image using AI", + name: "edit", + fieldType: "input", + isTransformation: true, + transformationKey: "e-edit-prompt", + helpText: "Enter a prompt describing how to edit the image.", + examples: ["add sunglasses", "make the sky blue"], + }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Edit Image using AI to {imageList.length} items. ", }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Edit Image using AI to {imageList.length} items. ", }, - }, - { - key: "ai-dropshadow", - name: "Drop Shadow", - description: - "Add a realistic AI-generated drop shadow around the object. Requires a transparent background; remove the background first for best results.", - docsLink: - "https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow", - defaultTransformation: {}, - schema: z - .object({ - dropshadow: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ - { - label: "Drop Shadow", - name: "dropshadow", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiDropShadow", - helpText: - "Toggle to add an AI-generated drop shadow. Requires transparent background.", + { + key: "ai-dropshadow", + name: "Drop Shadow", + description: + "Add a realistic AI-generated drop shadow around the object. Requires a transparent background; remove the background first for best results.", + docsLink: + "https://imagekit.io/docs/ai-transformations#ai-drop-shadow-e-dropshadow", + defaultTransformation: {}, + schema: z + .object({ + dropshadow: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, + { + message: "At least one value is required", + path: [], + }, + ), + transformations: [ + { + label: "Drop Shadow", + name: "dropshadow", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiDropShadow", + helpText: + "Toggle to add an AI-generated drop shadow. Requires transparent background.", + }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Drop Shadow to {imageList.length} items. ", }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Drop Shadow to {imageList.length} items. ", }, - }, - { - key: "ai-retouch", - name: "Retouch", - description: "Improve the quality of the image using AI retouching.", - docsLink: - "https://imagekit.io/docs/ai-transformations#retouch-e-retouch", - defaultTransformation: {}, - schema: z - .object({ - retouch: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ - { - label: "Retouch", - name: "retouch", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiRetouch", - helpText: - "Toggle to apply AI retouching and enhance image quality.", + { + key: "ai-retouch", + name: "Retouch", + description: "Improve the quality of the image using AI retouching.", + docsLink: + "https://imagekit.io/docs/ai-transformations#retouch-e-retouch", + defaultTransformation: {}, + schema: z + .object({ + retouch: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, + { + message: "At least one value is required", + path: [], + }, + ), + transformations: [ + { + label: "Retouch", + name: "retouch", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiRetouch", + helpText: + "Toggle to apply AI retouching and enhance image quality.", + }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Retouch to {imageList.length} items. ", }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Retouch to {imageList.length} items. ", }, - }, - { - key: "ai-upscale", - name: "Upscale", - description: - "Increase the resolution of low-resolution images using AI upscaling. The output can be up to 16 MP.", - docsLink: - "https://imagekit.io/docs/ai-transformations#upscale-e-upscale", - defaultTransformation: {}, - schema: z - .object({ - upscale: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ - { - label: "Upscale", - name: "upscale", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiUpscale", - helpText: - "Toggle to increase resolution of the image using AI upscaling (max 16 MP input).", + { + key: "ai-upscale", + name: "Upscale", + description: + "Increase the resolution of low-resolution images using AI upscaling. The output can be up to 16 MP.", + docsLink: + "https://imagekit.io/docs/ai-transformations#upscale-e-upscale", + defaultTransformation: {}, + schema: z + .object({ + upscale: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, + { + message: "At least one value is required", + path: [], + }, + ), + transformations: [ + { + label: "Upscale", + name: "upscale", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiUpscale", + helpText: + "Toggle to increase resolution of the image using AI upscaling (max 16 MP input).", + }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to apply Upscale to {imageList.length} items. ", }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to apply Upscale to {imageList.length} items. ", }, - }, - { - key: "ai-genvar", - name: "Generate Variations", - description: - "Create a new variation of the original image using AI, altering colors and textures while preserving the structure.", - docsLink: - "https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar", - defaultTransformation: {}, - schema: z - .object({ - genvar: z.coerce - .boolean({ - invalid_type_error: "Should be a boolean.", - }) - .optional(), - }) - .refine( - (val) => { - if ( - Object.values(val).some((v) => v !== undefined && v !== null) - ) { - return true - } - return false - }, - { - message: "At least one value is required", - path: [], - }, - ), - transformations: [ - { - label: "Generate Variations", - name: "genvar", - fieldType: "switch", - isTransformation: true, - transformationKey: "aiVariation", - helpText: - "Toggle to generate a new variation of the image using AI.", + { + key: "ai-genvar", + name: "Generate Variations", + description: + "Create a new variation of the original image using AI, altering colors and textures while preserving the structure.", + docsLink: + "https://imagekit.io/docs/ai-transformations#generate-variations-of-an-image-e-genvar", + defaultTransformation: {}, + schema: z + .object({ + genvar: z.coerce + .boolean({ + invalid_type_error: "Should be a boolean.", + }) + .optional(), + }) + .refine( + (val) => { + if ( + Object.values(val).some((v) => v !== undefined && v !== null) + ) { + return true + } + return false + }, + { + message: "At least one value is required", + path: [], + }, + ), + transformations: [ + { + label: "Generate Variations", + name: "genvar", + fieldType: "switch", + isTransformation: true, + transformationKey: "aiVariation", + helpText: + "Toggle to generate a new variation of the image using AI.", + }, + ], + warning: { + heading: "This action consumes AI credits.", + message: + "You are about to generate variations of {imageList.length} items. ", }, - ], - warning: { - heading: "This action consumes AI credits.", - message: - "You are about to generate variations of {imageList.length} items. ", }, - }, ], }, { @@ -2735,15 +2735,14 @@ export const transformationSchema: TransformationSchema[] = [ invalid_type_error: "Should be a boolean.", }) .optional(), - sharpen: - z.coerce - .number({ - invalid_type_error: "Should be a number.", - }) - .int() - .min(1) - .max(99) - .optional(), + sharpen: z.coerce + .number({ + invalid_type_error: "Should be a number.", + }) + .int() + .min(1) + .max(99) + .optional(), }) .refine( (val) => { @@ -2921,7 +2920,8 @@ export const transformationSchema: TransformationSchema[] = [ isTransformation: true, transformationKey: "trimThreshold", transformationGroup: "imageLayer", - helpText: "Control the intensity of this effect using a threshold value between 1% and 99%.", + helpText: + "Control the intensity of this effect using a threshold value between 1% and 99%.", fieldProps: { min: 1, max: 99, @@ -2970,8 +2970,7 @@ export const transformationSchema: TransformationSchema[] = [ fieldProps: { defaultValue: 0, }, - helpText: - "Enter the width of the border of the overlay image.", + helpText: "Enter the width of the border of the overlay image.", }, { label: "Border Color", @@ -2980,9 +2979,8 @@ export const transformationSchema: TransformationSchema[] = [ isTransformation: false, transformationKey: "borderColor", transformationGroup: "imageLayer", - isVisible: ({ borderWidth }) => borderWidth as number > 0, - helpText: - "Select the color of the border of the overlay image.", + isVisible: ({ borderWidth }) => (borderWidth as number) > 0, + helpText: "Select the color of the border of the overlay image.", fieldProps: { hideOpacity: true, showHexAlpha: false, @@ -3018,7 +3016,7 @@ export const transformationSchema: TransformationSchema[] = [ step: 1, }, isVisible: ({ sharpenEnabled }) => sharpenEnabled === true, - } + }, ], }, ], @@ -3387,7 +3385,10 @@ export const transformationFormatters: Record< if (values.rotation) { overlayTransform.rotation = values.rotation } - if (values.trimEnabled === true && typeof values.trimThreshold === "number") { + if ( + values.trimEnabled === true && + typeof values.trimThreshold === "number" + ) { overlayTransform.t = values.trimThreshold } @@ -3406,7 +3407,13 @@ export const transformationFormatters: Record< overlayTransform.sharpen = values.sharpen } } - if ((values.borderWidth && typeof values.borderWidth === "number" && values.borderWidth > 0) && (values.borderColor && typeof values.borderColor === "string")) { + if ( + values.borderWidth && + typeof values.borderWidth === "number" && + values.borderWidth > 0 && + values.borderColor && + typeof values.borderColor === "string" + ) { overlayTransform.b = `${values.borderWidth}_${values.borderColor.replace(/^#/, "")}` } @@ -3502,7 +3509,6 @@ export const transformationFormatters: Record< params.push(cleanFromColor) } - transforms.cr = params.join("_") }, border: (values, transforms) => { From 16ec40aa2d7fef6fd5610470225b531bb97c3397 Mon Sep 17 00:00:00 2001 From: Piyush Aryan Date: Wed, 28 Jan 2026 23:17:53 +0530 Subject: [PATCH 10/11] refactor: update borderWidth validation in transformation schema to use union type and improve visibility logic --- .../imagekit-editor-dev/src/schema/index.ts | 31 ++++++------------- .../src/schema/transformation.ts | 2 +- 2 files changed, 11 insertions(+), 22 deletions(-) diff --git a/packages/imagekit-editor-dev/src/schema/index.ts b/packages/imagekit-editor-dev/src/schema/index.ts index c8885bb..908c441 100644 --- a/packages/imagekit-editor-dev/src/schema/index.ts +++ b/packages/imagekit-editor-dev/src/schema/index.ts @@ -1585,13 +1585,7 @@ export const transformationSchema: TransformationSchema[] = [ defaultTransformation: {}, schema: z .object({ - borderWidth: z.coerce - .number({ - invalid_type_error: "Should be a number.", - }) - .int() - .min(1) - .optional(), + borderWidth: z.union([widthValidator, heightValidator]).optional(), borderColor: colorValidator, }) .refine( @@ -1608,6 +1602,7 @@ export const transformationSchema: TransformationSchema[] = [ path: [], }, ), + transformations: [ { label: "Border Width", @@ -2724,11 +2719,7 @@ export const transformationSchema: TransformationSchema[] = [ invalid_type_error: "Should be a number.", }) .optional(), - borderWidth: z.coerce - .number({ - invalid_type_error: "Should be a number.", - }) - .optional(), + borderWidth: z.union([widthValidator, heightValidator]).optional(), borderColor: colorValidator.optional(), sharpenEnabled: z.coerce .boolean({ @@ -2968,9 +2959,10 @@ export const transformationSchema: TransformationSchema[] = [ transformationKey: "borderWidth", transformationGroup: "imageLayer", fieldProps: { - defaultValue: 0, + defaultValue: "", }, - helpText: "Enter the width of the border of the overlay image.", + helpText: "Enter the width of the border or expression of the overlay image.", + examples: ["10", "ch_div_2"], }, { label: "Border Color", @@ -2979,7 +2971,7 @@ export const transformationSchema: TransformationSchema[] = [ isTransformation: false, transformationKey: "borderColor", transformationGroup: "imageLayer", - isVisible: ({ borderWidth }) => (borderWidth as number) > 0, + isVisible: ({ borderWidth }) => (borderWidth as string) !== "", helpText: "Select the color of the border of the overlay image.", fieldProps: { hideOpacity: true, @@ -3408,11 +3400,8 @@ export const transformationFormatters: Record< } } if ( - values.borderWidth && - typeof values.borderWidth === "number" && - values.borderWidth > 0 && - values.borderColor && - typeof values.borderColor === "string" + values.borderWidth && + values.borderColor && typeof values.borderColor === "string" ) { overlayTransform.b = `${values.borderWidth}_${values.borderColor.replace(/^#/, "")}` } @@ -3513,7 +3502,7 @@ export const transformationFormatters: Record< }, border: (values, transforms) => { const { borderWidth, borderColor } = values as { - borderWidth?: number + borderWidth?: string borderColor?: string } if (!borderWidth || !borderColor) return diff --git a/packages/imagekit-editor-dev/src/schema/transformation.ts b/packages/imagekit-editor-dev/src/schema/transformation.ts index 78eb69c..7c1ca8e 100644 --- a/packages/imagekit-editor-dev/src/schema/transformation.ts +++ b/packages/imagekit-editor-dev/src/schema/transformation.ts @@ -123,4 +123,4 @@ export const layerYValidator = z.any().superRefine((val, ctx) => { code: z.ZodIssueCode.custom, message: "Layer Y must be a positive number or a valid expression string.", }) -}) +}) \ No newline at end of file From 825b3c4b8183bade07c77ad2b06b6e4d1a7bfff6 Mon Sep 17 00:00:00 2001 From: Piyush Aryan Date: Thu, 29 Jan 2026 04:30:01 +0530 Subject: [PATCH 11/11] feat: add unsharpen mask transformation to image processing schema with validation and integration --- .../imagekit-editor-dev/src/schema/index.ts | 172 +++++++++++++++++- .../src/schema/transformation.ts | 41 ++++- 2 files changed, 209 insertions(+), 4 deletions(-) diff --git a/packages/imagekit-editor-dev/src/schema/index.ts b/packages/imagekit-editor-dev/src/schema/index.ts index 908c441..392109a 100644 --- a/packages/imagekit-editor-dev/src/schema/index.ts +++ b/packages/imagekit-editor-dev/src/schema/index.ts @@ -19,6 +19,8 @@ import { heightValidator, layerXValidator, layerYValidator, + optionalPositiveFloatNumberValidator, + refineUnsharpenMask, widthValidator, } from "./transformation" @@ -1602,7 +1604,7 @@ export const transformationSchema: TransformationSchema[] = [ path: [], }, ), - + transformations: [ { label: "Border Width", @@ -1841,6 +1843,82 @@ export const transformationSchema: TransformationSchema[] = [ }, ], }, + { + key: "adjust-unsharpen-mask", + name: "Unsharpen Mask", + description: + "Image sharpening technique that enhances edge contrast to make details appear clearer. Amplifies differences between neighboring pixels without significantly affecting smooth areas.", + docsLink: + "https://imagekit.io/docs/effects-and-enhancements#unsharp-mask---e-usm", + defaultTransformation: {}, + schema: z.object({ + unsharpenMaskRadius: z.coerce.number().positive({ message: "Should be a positive floating point number." }), + unsharpenMaskSigma: z.coerce.number().positive({ message: "Should be a positive floating point number." }), + unsharpenMaskAmount: z.coerce.number().positive({ message: "Should be a positive floating point number." }), + unsharpenMaskThreshold: z.coerce.number().positive({ message: "Should be a positive floating point number." }), + }) + .refine( + (val) => { + if (Object.values(val).some((v) => v !== undefined && v !== null)) { + return true + } + return false + }), + transformations: [ + { + name: "unsharpenMaskRadius", + fieldType: "input", + label: "Radius", + isTransformation: false, + transformationGroup: "unsharpenMask", + helpText: + "Controls how wide the sharpening effect spreads from each edge. Larger values affect broader areas; smaller values focus on fine details.", + fieldProps: { + defaultValue: "", + }, + examples: ["1", "8", "15"], + }, + { + name: "unsharpenMaskSigma", + fieldType: "input", + label: "Sigma", + isTransformation: false, + transformationGroup: "unsharpenMask", + helpText: + "Defines the amount of blur used to detect edges before sharpening. Higher values smooth more before sharpening; lower values preserve fine textures.", + fieldProps: { + defaultValue: "", + }, + examples: ["1", "5", "6"], + }, + { + name: "unsharpenMaskAmount", + fieldType: "input", + label: "Amount", + isTransformation: false, + transformationGroup: "unsharpenMask", + helpText: + "Sets the strength of the sharpening effect.", + fieldProps: { + defaultValue: "", + }, + examples: ["0.1", "2", "0.8"], + }, + { + name: "unsharpenMaskThreshold", + fieldType: "input", + label: "Threshold", + isTransformation: false, + transformationGroup: "unsharpenMask", + helpText: + "Set the threshold value for the unsharpen mask.", + fieldProps: { + defaultValue: "", + }, + examples: ["0.1", "2", "0.8"], + }, + ] + } ], }, { @@ -2734,7 +2812,12 @@ export const transformationSchema: TransformationSchema[] = [ .min(1) .max(99) .optional(), - }) + unsharpenMask: z.coerce.boolean().optional(), + unsharpenMaskRadius: optionalPositiveFloatNumberValidator.optional(), + unsharpenMaskSigma: optionalPositiveFloatNumberValidator.optional(), + unsharpenMaskAmount: optionalPositiveFloatNumberValidator.optional(), + unsharpenMaskThreshold: optionalPositiveFloatNumberValidator.optional(), + }).superRefine(refineUnsharpenMask) .refine( (val) => { return Object.values(val).some( @@ -3009,6 +3092,75 @@ export const transformationSchema: TransformationSchema[] = [ }, isVisible: ({ sharpenEnabled }) => sharpenEnabled === true, }, + { + name: "unsharpenMask", + fieldType: "switch", + isTransformation: false, + transformationGroup: "imageLayer", + helpText: + "Toggle to unsharpen the overlay image to remove the edges and finer details within an image.", + fieldProps: { + defaultValue: false, + }, + label: "Unsharpen Mask", + }, + { + name: "unsharpenMaskRadius", + fieldType: "input", + label: "Radius", + isTransformation: false, + transformationGroup: "imageLayer", + helpText: + "Controls how wide the sharpening effect spreads from each edge. Larger values affect broader areas; smaller values focus on fine details.", + fieldProps: { + defaultValue: "", + }, + examples: ["1", "8", "15"], + isVisible: ({ unsharpenMask }) => unsharpenMask === true, + }, + { + name: "unsharpenMaskSigma", + fieldType: "input", + label: "Sigma", + isTransformation: false, + transformationGroup: "imageLayer", + helpText: + "Defines the amount of blur used to detect edges before sharpening. Higher values smooth more before sharpening; lower values preserve fine textures.", + fieldProps: { + defaultValue: "", + }, + examples: ["1", "5", "6"], + isVisible: ({ unsharpenMask }) => unsharpenMask === true, + }, + { + name: "unsharpenMaskAmount", + fieldType: "input", + label: "Amount", + isTransformation: false, + transformationGroup: "imageLayer", + helpText: + "Sets the strength of the sharpening effect.", + fieldProps: { + defaultValue: "", + }, + examples: ["0.1", "2", "0.8"], + isVisible: ({ unsharpenMask }) => unsharpenMask === true, + }, + { + name: "unsharpenMaskThreshold", + fieldType: "input", + label: "Threshold", + isTransformation: false, + transformationGroup: "imageLayer", + helpText: + "Set the threshold value for the unsharpen mask.", + fieldProps: { + defaultValue: "", + }, + examples: ["0.1", "2", "0.8"], + isVisible: ({ unsharpenMask }) => unsharpenMask === true, + }, + ], }, ], @@ -3400,7 +3552,7 @@ export const transformationFormatters: Record< } } if ( - values.borderWidth && + values.borderWidth && values.borderColor && typeof values.borderColor === "string" ) { overlayTransform.b = `${values.borderWidth}_${values.borderColor.replace(/^#/, "")}` @@ -3425,6 +3577,11 @@ export const transformationFormatters: Record< // Assign overlay to transforms transforms.overlay = overlay + if (values.unsharpenMask === true) { + overlayTransform["e-usm"] = `${values.unsharpenMaskRadius}-${values.unsharpenMaskSigma}-${values.unsharpenMaskAmount}-${values.unsharpenMaskThreshold}` + } + + }, flip: (values, transforms) => { if ((values.flip as Array)?.length) { @@ -3521,4 +3678,13 @@ export const transformationFormatters: Record< transforms.sharpen = sharpen } }, + unsharpenMask: (values, transforms) => { + const { unsharpenMaskRadius, unsharpenMaskSigma, unsharpenMaskAmount, unsharpenMaskThreshold } = values as { + unsharpenMaskRadius: number + unsharpenMaskSigma: number + unsharpenMaskAmount: number + unsharpenMaskThreshold: number + } + transforms["e-usm"] = `${unsharpenMaskRadius}-${unsharpenMaskSigma}-${unsharpenMaskAmount}-${unsharpenMaskThreshold}` + }, } diff --git a/packages/imagekit-editor-dev/src/schema/transformation.ts b/packages/imagekit-editor-dev/src/schema/transformation.ts index 7c1ca8e..745c75d 100644 --- a/packages/imagekit-editor-dev/src/schema/transformation.ts +++ b/packages/imagekit-editor-dev/src/schema/transformation.ts @@ -123,4 +123,43 @@ export const layerYValidator = z.any().superRefine((val, ctx) => { code: z.ZodIssueCode.custom, message: "Layer Y must be a positive number or a valid expression string.", }) -}) \ No newline at end of file +}) + + +export const optionalPositiveFloatNumberValidator = z.preprocess( + (val) => (val === "" || val === undefined || val === null) ? undefined : val, + z.coerce.number().positive({ message: "Should be a positive floating point number." }).optional() +) + +export const refineUnsharpenMask = (val: any, ctx: z.RefinementCtx) => { + if (val.unsharpenMask === true) { + if (!val.unsharpenMaskRadius) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Radius is required when Unsharpen Mask is enabled", + path: ["unsharpenMaskRadius"], + }) + } + if (!val.unsharpenMaskSigma) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Sigma is required when Unsharpen Mask is enabled", + path: ["unsharpenMaskSigma"], + }) + } + if (!val.unsharpenMaskAmount) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Amount is required when Unsharpen Mask is enabled", + path: ["unsharpenMaskAmount"], + }) + } + if (!val.unsharpenMaskThreshold) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + message: "Threshold is required when Unsharpen Mask is enabled", + path: ["unsharpenMaskThreshold"], + }) + } + } +} \ No newline at end of file