Phase 3 of file peek. Phase 2 proves the interaction with a plain-text viewer; this phase makes it pleasant: syntax highlighting, images, and a cache. Design: docs/superpowers/specs/2026-08-11-file-peek-design.md, sections "Highlighting", "Images", and "Cache".
Scope
Shiki, on the JavaScript regex engine. @shikijs/engine-javascript, not oniguruma-wasm: the daemon serves its UI under script-src 'self' (internal/daemon/server.go, cspHead) and Chrome refuses WebAssembly under that policy without wasm-unsafe-eval. The feature bends around the CSP, not the CSP around the feature. Grammars load per language as separate chunks on first use. Tokenizing runs in a worker. Capped at 1 MB or 20,000 lines, whichever comes first; past the cap, plain text, still windowed. Theme is a light and dark pair chosen to sit with emulator/palette.ts.
Images. Chunks assemble into a data: URL. img-src 'self' data: already permits it; blob: would mean widening the policy. 4 MB cap, from phase 1's daemon.
Cache. In-memory LRU of a few megabytes keyed by machine, path, size, and mtime. Reopening a file you just closed costs nothing. Not IndexedDB: file contents on a paired phone's disk is a durability question this feature has no reason to open.
Tests
- Viewer: the image path renders from chunks; highlight cap falls back to plain text; LRU hit skips the wire.
- Bundle: grammars and the worker land as separate chunks, not in the entry.
styles.build.test.ts still passes; Shiki output and any prose in new sources feed the Tailwind scanner.
Commands: cd web && pnpm vitest run && pnpm run lint.
Phase 3 of file peek. Phase 2 proves the interaction with a plain-text viewer; this phase makes it pleasant: syntax highlighting, images, and a cache. Design:
docs/superpowers/specs/2026-08-11-file-peek-design.md, sections "Highlighting", "Images", and "Cache".Scope
Shiki, on the JavaScript regex engine.
@shikijs/engine-javascript, not oniguruma-wasm: the daemon serves its UI underscript-src 'self'(internal/daemon/server.go,cspHead) and Chrome refuses WebAssembly under that policy withoutwasm-unsafe-eval. The feature bends around the CSP, not the CSP around the feature. Grammars load per language as separate chunks on first use. Tokenizing runs in a worker. Capped at 1 MB or 20,000 lines, whichever comes first; past the cap, plain text, still windowed. Theme is a light and dark pair chosen to sit withemulator/palette.ts.Images. Chunks assemble into a
data:URL.img-src 'self' data:already permits it;blob:would mean widening the policy. 4 MB cap, from phase 1's daemon.Cache. In-memory LRU of a few megabytes keyed by machine, path, size, and mtime. Reopening a file you just closed costs nothing. Not IndexedDB: file contents on a paired phone's disk is a durability question this feature has no reason to open.
Tests
styles.build.test.tsstill passes; Shiki output and any prose in new sources feed the Tailwind scanner.Commands:
cd web && pnpm vitest run && pnpm run lint.