Goal
Extract the clipboard-history domain out of main.ts using the DI pattern with tests, and fix the fire-and-forget clipboard image write race (merged from #34).
Merged from
Why this matters
src/electron/main.ts is ~5,369 lines and the single highest-churn file in the repo. The clipboard domain is a well-bounded cluster of functions with module-level mutable state. Extracting it follows the established createX(deps) pattern used by app-icon-cache, app-index-service, etc. Additionally, persistClipboardImage returns a path synchronously while the write runs un-awaited — a correctness hazard that the extraction naturally fixes.
To do
Phase 1: Fix the image write race (was #34)
Phase 2: Extract clipboard domain (was #35)
Out of scope
- Other domains in main.ts (calculator, extensions, app index, AI)
- IPC handler names/signatures — behavior must be identical
src/electron/clipboard-utils.ts (already extracted)
Verification
mise exec -- pnpm typecheck → exit 0
mise exec -- pnpm test → exit 0
mise exec -- pnpm palette:debug → clipboard items still resolve
main.ts line count decreased (wc -l src/electron/main.ts)
Goal
Extract the clipboard-history domain out of main.ts using the DI pattern with tests, and fix the fire-and-forget clipboard image write race (merged from #34).
Merged from
Why this matters
src/electron/main.tsis ~5,369 lines and the single highest-churn file in the repo. The clipboard domain is a well-bounded cluster of functions with module-level mutable state. Extracting it follows the establishedcreateX(deps)pattern used by app-icon-cache, app-index-service, etc. Additionally,persistClipboardImagereturns a path synchronously while the write runs un-awaited — a correctness hazard that the extraction naturally fixes.To do
Phase 1: Fix the image write race (was #34)
persistClipboardImageasync — await mkdir + writeFilenullon write failure (instead of a path to a missing file)awaitand handle nullPhase 2: Extract clipboard domain (was #35)
src/electron/clipboard-history.ts—createClipboardHistory(deps)mirroringapp-icon-cache.tsdeps.getHistory()/deps.setHistory()main.ts, delete inline copiessrc/electron/clipboard-history.test.ts— characterization tests with fakespnpm typecheck+pnpm testexit 0, clipboard behavior unchanged viapnpm palette:debugOut of scope
src/electron/clipboard-utils.ts(already extracted)Verification
mise exec -- pnpm typecheck→ exit 0mise exec -- pnpm test→ exit 0mise exec -- pnpm palette:debug→ clipboard items still resolvemain.tsline count decreased (wc -l src/electron/main.ts)