Overview
Deep audit (2026-03-27) identified 5 new high-priority feature/reliability findings not covered by existing issues.
Finding 1: Watermark Always Outputs PNG, Ignoring User JPEG Setting
File: src/offscreen/offscreen.ts, line 166
addWatermark() always calls canvas.toDataURL('image/png') regardless of user's screenshotFormat setting. If user chose JPEG, they still get PNG output (3-10x larger for photos). cropImage() at line 117 has the same issue.
return { dataUrl: canvas.toDataURL('image/png') }; // Always PNG
Impact: Silently inflates file sizes, increases storage consumption and upload times.
Fix: Pass screenshotFormat and screenshotQuality through to the offscreen document message payload. Use canvas.toDataURL(mimeType, quality).
Finding 2: ImageBitmap Resource Leak on Every Capture
File: src/background/service-worker.ts, line 434
createImageBitmap() creates a GPU-backed bitmap to measure dimensions but .close() is never called. Each capture leaks ~8MB of uncompressed bitmap memory (1920x1080 @ 32bpp).
const img = await createImageBitmap(await (await fetch(dataUrl)).blob());
let width = img.width;
let height = img.height;
// img.close() never called
Fix: Call img.close() after reading dimensions. Or have the offscreen document return dimensions alongside the watermarked data URL.
Finding 3: Selection Timeout setTimeout Never Cleared
File: src/background/service-worker.ts, lines 187-199
The 60-second selection timeout return value is never stored and never cleared on completion. If user completes selection quickly, the old timeout fires 55s later. If user starts a second selection within 60s, the old timeout may interfere.
setTimeout(() => { // Return value not stored
if (pendingSelectionReject) {
pendingSelectionReject(new Error('Selection timed out'));
}
}, 60000);
Fix: Store the timeout ID; call clearTimeout() in handleSelectionComplete() and the catch block.
Finding 4: Race Condition in getNumbersApi() Singleton Init
File: src/services/NumbersApiManager.ts, lines 150-159
After instance = new NumbersApiManager() on line 155, instance is non-null but initialize() is still running. A concurrent caller sees instance !== null and returns a partially-initialized instance with no restored token.
export async function getNumbersApi(): Promise<NumbersApiManager> {
if (!instance) {
instance = new NumbersApiManager();
await instance.initialize(); // Yields here; concurrent callers skip
}
return instance;
}
Fix: Use a promise-based lock:
let initPromise: Promise<NumbersApiManager> | null = null;
export function getNumbersApi(): Promise<NumbersApiManager> {
if (!initPromise) {
initPromise = (async () => {
const inst = new NumbersApiManager();
await inst.initialize();
return inst;
})();
}
return initPromise;
}
Finding 5: ScreenshotService.ts is 278 Lines of Dead Code
File: src/services/ScreenshotService.ts (entire file)
ScreenshotService and its exported singleton are never imported by any other file. All actual capture logic lives in service-worker.ts lines 404-581. 278 lines of dead code increasing bundle size and cognitive load.
Fix: Remove ScreenshotService.ts entirely, or refactor service-worker capture logic to actually use it.
Generated by Heart Beat with Omni
Overview
Deep audit (2026-03-27) identified 5 new high-priority feature/reliability findings not covered by existing issues.
Finding 1: Watermark Always Outputs PNG, Ignoring User JPEG Setting
File:
src/offscreen/offscreen.ts, line 166addWatermark()always callscanvas.toDataURL('image/png')regardless of user'sscreenshotFormatsetting. If user chose JPEG, they still get PNG output (3-10x larger for photos).cropImage()at line 117 has the same issue.Impact: Silently inflates file sizes, increases storage consumption and upload times.
Fix: Pass
screenshotFormatandscreenshotQualitythrough to the offscreen document message payload. Usecanvas.toDataURL(mimeType, quality).Finding 2:
ImageBitmapResource Leak on Every CaptureFile:
src/background/service-worker.ts, line 434createImageBitmap()creates a GPU-backed bitmap to measure dimensions but.close()is never called. Each capture leaks ~8MB of uncompressed bitmap memory (1920x1080 @ 32bpp).Fix: Call
img.close()after reading dimensions. Or have the offscreen document return dimensions alongside the watermarked data URL.Finding 3: Selection Timeout
setTimeoutNever ClearedFile:
src/background/service-worker.ts, lines 187-199The 60-second selection timeout return value is never stored and never cleared on completion. If user completes selection quickly, the old timeout fires 55s later. If user starts a second selection within 60s, the old timeout may interfere.
Fix: Store the timeout ID; call
clearTimeout()inhandleSelectionComplete()and the catch block.Finding 4: Race Condition in
getNumbersApi()Singleton InitFile:
src/services/NumbersApiManager.ts, lines 150-159After
instance = new NumbersApiManager()on line 155,instanceis non-null butinitialize()is still running. A concurrent caller seesinstance !== nulland returns a partially-initialized instance with no restored token.Fix: Use a promise-based lock:
Finding 5:
ScreenshotService.tsis 278 Lines of Dead CodeFile:
src/services/ScreenshotService.ts(entire file)ScreenshotServiceand its exported singleton are never imported by any other file. All actual capture logic lives inservice-worker.tslines 404-581. 278 lines of dead code increasing bundle size and cognitive load.Fix: Remove
ScreenshotService.tsentirely, or refactor service-worker capture logic to actually use it.Generated by Heart Beat with Omni