// lib/enhance/analyze.ts (updated)
function analyzeImage(
imageData: ImageData
): ImageAnalysis | { skip: true;
reason: string } {
const pixels = imageData.data;
const pixelCount = pixels.length / 4;
// Edge case: tiny image
if (pixelCount < 100) {
return {
skip: true,
reason: 'Image too small'
+ ' for meaningful analysis',
};
}
// Edge case: corrupt data
if (pixels.length % 4 !== 0) {
return {
skip: true,
reason: 'Invalid image data',
};
}
// ... normal analysis ...
// Edge case: already-perfect image
const totalAdjustment =
Math.abs(brightness.suggested)
+ Math.abs(contrast.suggested - 1)
* 20
+ Math.abs(saturation.suggested);
if (totalAdjustment < 8) {
return {
skip: true,
reason: 'Image looks great'
+ ' already! 👌',
};
}
// Edge case: pure B&W
if (saturation.isGrayscale) {
// Zero out saturation suggestion
saturation.suggested = 0;
}
return analysis;
}
// components/AutoEnhanceButton.tsx
const handleEnhance = async () => {
if (!image) return;
try {
setState('analyzing');
const result = await runAnalysis(
image);
// Handle skip case
if ('skip' in result) {
showToast({
type: 'info',
message: result.reason,
});
setState('idle');
return;
}
// Apply and show result
const enhanced =
applyEnhancement(image, {
brightness:
result.brightness.suggested,
contrast:
result.contrast.suggested,
saturation:
result.saturation.suggested,
});
setOriginalImage(image);
setPreviewImage(enhanced);
setAnalysis(result);
setState('done');
} catch (error) {
logger.error(
'Auto-enhance failed', { error });
showToast({
type: 'error',
message: 'Enhancement unavailable.'
+ ' You can still edit manually.',
});
setState('idle');
// Feature failure must NEVER
// break the editor.
}
};
// Enhance button:
<button
aria-label="Auto-enhance image"
aria-describedby="enhance-hint">
✨ Auto-Enhance
</button>
<span id="enhance-hint"
className="sr-only">
Analyzes image and suggests
brightness, contrast, and saturation
corrections automatically.
</span>
// Progress announcements:
<div aria-live="polite" role="status">
{stage === 'analyzing' &&
'Analyzing image...'}
{stage === 'done' &&
`Enhancement applied: brightness
${params.brightness > 0 ? '+' : ''}
${params.brightness}, contrast
×${params.contrast}`}
</div>
// Before/after slider (keyboard):
onKeyDown={(e) => {
if (e.key === 'ArrowLeft')
setPosition(p =>
Math.max(0, p - 5));
if (e.key === 'ArrowRight')
setPosition(p =>
Math.min(100, p + 5));
if (e.key === 'Home')
setPosition(0); // all original
if (e.key === 'End')
setPosition(100); // all enhanced
}}
// Focus management:
// After "Apply" → focus returns to
// the enhance button
// After "Revert" → focus returns to
// the enhance button
// Escape key → closes adjust panel
// workers/enhance.worker.ts
import { analyzeImage } from
'../lib/enhance/analyze';
self.onmessage = (
e: MessageEvent<ImageData>
) => {
const imageData = e.data;
// This runs off main thread.
// 33M pixel scan → no UI jank.
const result =
analyzeImage(imageData);
self.postMessage(result);
};
// Usage from main thread:
function runAnalysis(
image: ImageData
): Promise<ImageAnalysis> {
return new Promise((resolve) => {
const worker = new Worker(
new URL(
'../workers/enhance.worker.ts',
import.meta.url
));
worker.onmessage = (e) => {
resolve(e.data);
worker.terminate();
};
// Transfer the buffer (zero-copy)
worker.postMessage(image, [
image.data.buffer,
]);
});
}
// Transfer vs clone:
// Clone: copies 33MB (slow)
// Transfer: moves ownership (instant)
// After transfer, main thread can't
// access the buffer anymore.
// That's fine — Worker has it now.
// __tests__/enhance.integration.test.ts
import { describe, it, expect } from
'vitest';
import { render, screen, waitFor }
from '@testing-library/react';
import userEvent
from '@testing-library/user-event';
describe('Auto-Enhance feature',
() => {
it('full flow: click → apply',
async () => {
// Setup: render editor with image
render(<Editor
testImage={darkTestImage} />);
// Click enhance
const btn = screen.getByRole(
'button',
{ name: /auto-enhance/i });
await userEvent.click(btn);
// Wait for analysis
await waitFor(() => {
expect(screen.getByText(
/enhancement applied/i))
.toBeInTheDocument();
});
// Before/after slider visible
expect(screen.getByRole('slider',
{ name: /before and after/i }))
.toBeInTheDocument();
// Apply
await userEvent.click(
screen.getByText(/apply/i));
// Returns to normal toolbar
expect(screen.queryByText(
/adjust result/i))
.not.toBeInTheDocument();
});
it('shows message for perfect image',
async () => {
render(<Editor
testImage={perfectTestImage} />);
await userEvent.click(
screen.getByRole('button',
{ name: /auto-enhance/i }));
await waitFor(() => {
expect(screen.getByText(
/looks great already/i))
.toBeInTheDocument();
});
});
it('handles failure gracefully',
async () => {
render(<Editor
testImage={corruptImage} />);
await userEvent.click(
screen.getByRole('button',
{ name: /auto-enhance/i }));
await waitFor(() => {
expect(screen.getByText(
/unavailable/i))
.toBeInTheDocument();
});
// Editor still functional
expect(screen.getByRole('button',
{ name: /brightness/i }))
.toBeEnabled();
});
});
# docs/auto-enhance.md
## Auto-Enhance Feature
### For Users
Click ✨ Auto-Enhance to automatically
improve your photo's brightness,
contrast, and color balance.
- **One click**: instant improvement
- **Adjust**: fine-tune with sliders
- **Compare**: drag the before/after
slider to see the difference
- **Revert**: undo at any time
### For Developers
**Architecture:**
1. User clicks enhance button
2. Web Worker analyzes image pixels
3. Suggestion functions compute
optimal parameters
4. Client applies adjustments
5. Analytics recorded server-side
**Files:**
- `lib/enhance/analyze.ts` — engine
- `lib/enhance/apply.ts` — filters
- `lib/enhance/suggestions.ts` — algo
- `workers/enhance.worker.ts` — perf
- `components/AutoEnhanceButton.tsx`
- `components/BeforeAfterSlider.tsx`
- `components/AdjustPanel.tsx`
**API:**
POST /api/enhance/analytics
Records usage for algorithm iteration.
git switch -c feature/auto-enhance-polish
git add src/ docs/ __tests__/
git commit -m "Auto-enhance: edge cases, a11y, worker, tests, docs"
git push origin feature/auto-enhance-polish
# PR → Review → Merge ✅
# 🚀 FEATURE COMPLETE
The 80/20 rule of shipping: the last 20% takes 80% of the effort.
The happy path was session 120-121. Edge cases, accessibility, performance, testing, and documentation are this session. This is why senior engineers estimate generously.