Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/prd/conversion-engine.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ They do not know whether the implementation is image canvas, mediabunny video, S
- Always return a Converter; never throw for unknown types
- Reconstruct cross-realm `File` objects when `instanceof File` fails (iframes)
- Images: ImageConverter when MIME supported; prefer WebP when the browser can encode it; otherwise keep source-friendly format
- When `skipWebpOptimization` is enabled, `image/webp` returns NullConverter so Cimo does not re-compress or resize it
- Non-images: `applyFilters( 'cimo.getFileConverter', null, file )` then NullConverter
- Settings read from `window.cimoSettings` at construction (quality, max dimension vs WP scaling threshold, smart flag)

Expand Down
1 change: 1 addition & 0 deletions docs/prd/settings-and-freemium-ux.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ The settings page shell, `cimo_options` schema surfacing, free vs premium contro
- Disable WP big-image scaling
- Thumbnail generation / per-size toggles
- WebP quality
- Skip WebP optimization, including Cimo resizing
- Max image dimension
- Stats dashboard consumption

Expand Down
1 change: 1 addition & 0 deletions e2e/test-utils/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { waitForCimoReady } from './media'

export type CimoOptions = {
webp_quality?: number;
skip_webp_optimization?: number;
max_image_dimension?: number;
disable_wp_scaling?: number;
smart_optimization?: number;
Expand Down
28 changes: 28 additions & 0 deletions e2e/tests/quality-and-webp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ test.describe( 'WebP quality and already-WebP uploads', () => {
disable_wp_scaling: 1,
disable_thumbnail_generation: 1,
smart_optimization: 0,
skip_webp_optimization: 0,
} )
} )

Expand All @@ -31,6 +32,7 @@ test.describe( 'WebP quality and already-WebP uploads', () => {
webp_quality: 80,
max_image_dimension: 0,
disable_thumbnail_generation: 0,
skip_webp_optimization: 0,
} )
} )

Expand Down Expand Up @@ -110,4 +112,30 @@ test.describe( 'WebP quality and already-WebP uploads', () => {
// Re-encode may shrink or skip if larger; either way upload must succeed.
expect( uploadedSize ).toBeLessThanOrEqual( originalSize * 1.25 )
} )

test( 'can leave an oversized WebP unchanged', async ( {
page,
requestUtils,
} ) => {
await saveCimoOptions( requestUtils, {
max_image_dimension: 800,
skip_webp_optimization: 1,
} )
await reloadCimoRuntime( page )

const originalSize = fs.statSync( SAMPLE_LARGE_WEBP ).size
const media = await uploadSampleViaMediaNew(
page,
requestUtils,
SAMPLE_LARGE_WEBP,
'image/webp',
{
expectedMime: 'image/webp',
urlPattern: /\.webp(\?|$)/i,
}
)

expect( media.media_details?.width ?? 0 ).toBeGreaterThan( 800 )
expect( await getMediaFileByteLength( page, requestUtils, media.id ) ).toBe( originalSize )
} )
} )
5 changes: 5 additions & 0 deletions e2e/tests/settings.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ test.describe( 'Cimo settings', () => {
webp_quality: 80,
max_image_dimension: 0,
disable_wp_scaling: 1,
skip_webp_optimization: 0,
} )
} )

Expand All @@ -29,6 +30,7 @@ test.describe( 'Cimo settings', () => {
webp_quality: 80,
max_image_dimension: 0,
disable_wp_scaling: 1,
skip_webp_optimization: 0,
} )
} )

Expand All @@ -43,6 +45,7 @@ test.describe( 'Cimo settings', () => {
await expect( page.getByRole( 'heading', { name: 'General Settings' } ) ).toBeVisible()
await expect( page.getByRole( 'heading', { name: 'Image Optimization Settings' } ) ).toBeVisible()
await expect( page.locator( '.cimo-webp-quality-range-control' ) ).toBeVisible()
await expect( page.getByRole( 'checkbox', { name: 'Skip WebP Optimization' } ) ).not.toBeChecked()
await expect( page.getByLabel( 'Maximum Image Dimension' ) ).toBeVisible()
await expect( page.locator( '.cimo-save-button' ) ).toBeVisible()
} )
Expand All @@ -63,12 +66,14 @@ test.describe( 'Cimo settings', () => {

// UI save smoke: tweak max dimension and persist via Save Changes.
await page.getByLabel( 'Maximum Image Dimension' ).fill( '640' )
await page.getByRole( 'checkbox', { name: 'Skip WebP Optimization' } ).check()
await saveSettingsUi( page )

await reloadCimoRuntime( page )
const settings = await getCimoSettings( page )
expect( Number( settings.webpQuality ) ).toBe( 55 )
expect( Number( settings.maxImageDimension ) ).toBe( 640 )
expect( Number( settings.skipWebpOptimization ) ).toBe( 1 )

const media = await uploadSampleViaMediaNew(
page,
Expand Down
8 changes: 8 additions & 0 deletions src/admin/class-admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ public function register_settings() {
'webp_quality' => [
'type' => 'integer',
],
'skip_webp_optimization' => [
'type' => 'integer',
],
'max_image_dimension' => [
'type' => 'integer',
],
Expand Down Expand Up @@ -363,6 +366,11 @@ public function sanitize_options( $options ) {
$sanitized['webp_quality'] = $quality > 0 ? max( 1, min( 100, $quality ) ) : 0;
}

// Sanitize skip WebP optimization.
if ( isset( $options['skip_webp_optimization'] ) ) {
$sanitized['skip_webp_optimization'] = $options['skip_webp_optimization'] ? 1 : 0;
}

// Sanitize max image dimension
if ( isset( $options['max_image_dimension'] ) ) {
$dimension = absint( $options['max_image_dimension'] );
Expand Down
1 change: 1 addition & 0 deletions src/admin/class-script-loader.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public static function enqueue_cimo_assets() {
'smartOptimization' => CIMO_BUILD === 'premium'
? ( isset( $settings['smart_optimization'] ) ? (int) $settings['smart_optimization'] : 1 )
: 0,
'skipWebpOptimization' => isset( $settings['skip_webp_optimization'] ) ? (int) $settings['skip_webp_optimization'] : 0,
'webpQuality' => ! empty( $settings['webp_quality'] ) ? (int) $settings['webp_quality'] : 80,
'maxImageDimension' => ! empty( $settings['max_image_dimension'] ) ? (int) $settings['max_image_dimension'] : 0,
'videoOptimizationEnabled' => isset( $settings['video_optimization_enabled'] ) ? (int) $settings['video_optimization_enabled'] : 1,
Expand Down
16 changes: 16 additions & 0 deletions src/admin/js/page/admin-settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ const AdminSettings = () => {
// Image optimization settings
smartOptimization: 1,
webpQuality: 80,
skipWebpOptimization: 0,
maxImageDimension: '',

// LQIP settings
Expand Down Expand Up @@ -96,6 +97,7 @@ const AdminSettings = () => {
// Image Optimization settings
smartOptimization: cimoOptions.smart_optimization !== undefined ? cimoOptions.smart_optimization : 1,
webpQuality: cimoOptions.webp_quality !== undefined ? cimoOptions.webp_quality : 80,
skipWebpOptimization: cimoOptions.skip_webp_optimization !== undefined ? cimoOptions.skip_webp_optimization : 0,
maxImageDimension: cimoOptions.max_image_dimension || '',

// LQIP settings
Expand Down Expand Up @@ -192,6 +194,7 @@ const AdminSettings = () => {
...settings,
smartOptimization: 1,
webpQuality: 80,
skipWebpOptimization: 0,
maxImageDimension: 1920,
}
} )
Expand All @@ -203,6 +206,7 @@ const AdminSettings = () => {
...settings,
smartOptimization: 1,
webpQuality: '',
skipWebpOptimization: 0,
maxImageDimension: '',
}
} )
Expand Down Expand Up @@ -296,6 +300,7 @@ const AdminSettings = () => {
// Image Optimization settings
smart_optimization: settings.smartOptimization,
webp_quality: parseInt( settings.webpQuality ) || 0,
skip_webp_optimization: settings.skipWebpOptimization,
max_image_dimension: parseInt( settings.maxImageDimension ) || 0,

// LQIP settings
Expand Down Expand Up @@ -627,6 +632,17 @@ const AdminSettings = () => {
</div>
) }

{ /* Skip WebP Optimization */ }
<div className="cimo-setting-field">
<ToggleControl
__nextHasNoMarginBottom
label={ __( 'Skip WebP Optimization', 'cimo-image-optimizer' ) }
checked={ settings.skipWebpOptimization === 1 }
onChange={ checked => handleInputChange( 'skipWebpOptimization', checked ? 1 : 0 ) }
help={ __( 'Keep uploaded WebP images unchanged. Cimo will not re-compress or resize them, even when a maximum image dimension is set.', 'cimo-image-optimizer' ) }
/>
</div>

{ /* Maximum Image Dimension */ }
<div className="cimo-setting-field">
<TextControl
Expand Down
5 changes: 5 additions & 0 deletions src/shared/converters/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@ export const getFileConverter = _file => {
)
}

const skipWebpOptimization = String( window.cimoSettings?.skipWebpOptimization ?? '0' ) === '1'
if ( skipWebpOptimization && file.type.toLowerCase() === 'image/webp' ) {
return new NullConverter( file )
}

if ( file.type.startsWith( 'image/' ) ) {
const max = parseFloat( window.cimoSettings?.maxImageDimension || 0 )
const wp = parseFloat( window.cimoSettings?.wpScalingThreshold || 0 )
Expand Down
Loading