ai-debug-local-mcp
Version:
🎯 ENHANCED AI GUIDANCE v4.1.2: Dramatically improved tool descriptions help AI users choose the right tools instead of 'close enough' options. Ultra-fast keyboard automation (10x speed), universal recording, multi-ecosystem debugging support, and compreh
127 lines • 5.15 kB
JavaScript
export class ScreenshotOptimizer {
static MAX_SIZE_BYTES = 1.5 * 1024 * 1024; // 1.5MB max for Claude
static MAX_DIMENSION = 2048; // Max width or height
static TARGET_SIZE_BYTES = 1 * 1024 * 1024; // Target 1MB
static async optimizeScreenshot(buffer, fullPage) {
const originalSize = buffer.length;
// Check if optimization is needed
if (originalSize <= this.TARGET_SIZE_BYTES) {
return {
buffer,
mimeType: 'image/png',
wasOptimized: false,
originalSize,
optimizedSize: originalSize
};
}
// Dynamic import sharp to handle potential loading issues
let sharp;
try {
sharp = (await import('sharp')).default;
}
catch (error) {
console.error('Failed to load sharp:', error);
// Fallback to original buffer if sharp fails
return {
buffer,
mimeType: 'image/png',
wasOptimized: false,
originalSize,
optimizedSize: originalSize
};
}
try {
// Get image metadata
const metadata = await sharp(buffer).metadata();
const { width = 0, height = 0 } = metadata;
// Calculate resize if dimensions are too large
let resizeOptions;
if (width > this.MAX_DIMENSION || height > this.MAX_DIMENSION) {
const scaleFactor = Math.min(this.MAX_DIMENSION / width, this.MAX_DIMENSION / height);
resizeOptions = {
width: Math.round(width * scaleFactor),
height: Math.round(height * scaleFactor)
};
}
// Start with high quality and reduce if needed
let quality = 90;
let format = 'jpeg'; // JPEG is generally smaller
let optimizedBuffer = buffer;
// Try optimization with current settings
while (quality >= 50) {
let sharpInstance = sharp(buffer);
// Apply resize if needed
if (resizeOptions) {
sharpInstance = sharpInstance.resize(resizeOptions);
}
// Try JPEG first (usually smaller)
if (format === 'jpeg') {
optimizedBuffer = await sharpInstance
.jpeg({
quality,
progressive: true,
mozjpeg: true // Use mozjpeg encoder for better compression
})
.toBuffer();
}
else {
optimizedBuffer = await sharpInstance
.png({
quality,
compressionLevel: 9,
palette: true // Use palette mode for smaller file size
})
.toBuffer();
}
// Check if we're under the limit
if (optimizedBuffer.length <= this.MAX_SIZE_BYTES) {
break;
}
// Try more aggressive optimization
quality -= 10;
// If still too large and we haven't resized yet, resize more aggressively
if (!resizeOptions && quality < 70) {
const newDimension = Math.round(this.MAX_DIMENSION * 0.75);
const scaleFactor = Math.min(newDimension / width, newDimension / height);
resizeOptions = {
width: Math.round(width * scaleFactor),
height: Math.round(height * scaleFactor)
};
}
}
// Last resort: aggressive resize and compression
if (optimizedBuffer.length > this.MAX_SIZE_BYTES) {
const aggressiveSize = Math.round(this.MAX_DIMENSION * 0.5);
optimizedBuffer = await sharp(buffer)
.resize({
width: aggressiveSize,
height: aggressiveSize,
fit: 'inside',
withoutEnlargement: true
})
.jpeg({ quality: 60, mozjpeg: true })
.toBuffer();
format = 'jpeg';
}
return {
buffer: optimizedBuffer,
mimeType: format === 'jpeg' ? 'image/jpeg' : 'image/png',
wasOptimized: true,
originalSize,
optimizedSize: optimizedBuffer.length
};
}
catch (error) {
console.error('Error optimizing screenshot:', error);
// Return original on error
return {
buffer,
mimeType: 'image/png',
wasOptimized: false,
originalSize,
optimizedSize: originalSize
};
}
}
}
//# sourceMappingURL=screenshot-optimizer.js.map