A desktop PDF viewer for macOS and Windows. The interface uses Bun, TypeScript, CSS, and Mozilla PDF.js; Tauri supplies the native window and file picker. The finished app uses the operating system webview and includes no Node or Bun runtime.
- Open PDFs, including password protected PDFs, from the file picker, drag and drop, or the operating system's PDF file association; unsupported files show a clear error
- Page thumbnails, nested table of contents, page navigation, zoom, fit width, and rotation
- Find every occurrence of readable text and select a result
- Light and dark mode for the app, PDF pages, and thumbnails, with an option to keep embedded image colors in dark mode
- Lazy rendering with offscreen canvas eviction and a page raster limit
- Restore open windows and their document tabs after restarting the app
PDF.js opens PDF documents, including PDFs whose names do not end in .pdf. The app accepts any file from the picker or a drop so PDF.js can validate it, then reports unsupported files without closing the current document.
Use View options in the toolbar for rotation and appearance settings. Dark mode changes only the display. The Invert images switch is available while dark mode is on. Turn it off to leave embedded images in their original colors while darkening the rest of each page.
Install Bun and the Tauri 2 prerequisites, including Rust and the platform's development tools. Node.js is not required.
bun install
bun run devbun run typecheck
bun run dist:mac # on macOS: .app
bun run dist:win # on Windows: NSIS installer and portable .exeThe native outputs are in src-tauri/target/release/bundle/. On Windows, src-tauri/target/release/preview-pro-max.exe is the portable executable and requires the system WebView2 runtime. The NSIS installer can download WebView2 if it is missing. The GitHub Actions workflow uploads a zipped macOS .app, the portable Windows .exe, and the Windows installer.
macOS builds use an ad-hoc signature. Public distribution without installation prompts requires an Apple Developer ID and notarization.
PDF rendering is provided by Mozilla PDF.js under the Apache 2.0 license.
Opening files adds document tabs. The picker and drag and drop accept multiple files. Each tab keeps its page position, zoom, rotation, and Find results. Use the + button for an empty tab and New window at the right of the tab bar for an independent window.
On launch, previously open windows reopen with their PDF tabs, selected tab, page, zoom, rotation, and scroll position. Empty tabs and new empty windows list saved windows; choose one to reopen its PDFs. Files that have moved or been deleted are skipped.
Pinch on a trackpad or use Ctrl/Cmd plus the mouse wheel over a page to zoom around the pointer. Ordinary wheel scrolling moves through the PDF.
| Action | macOS | Windows |
|---|---|---|
| Open files | ⌘O | Ctrl+O |
| New tab | ⌘T | Ctrl+T |
| New window | ⌘N | Ctrl+N |
| Close tab (last tab closes window) | ⌘W | Ctrl+W |
| Next / previous tab | Ctrl+Tab / Ctrl+Shift+Tab | Ctrl+Tab / Ctrl+Shift+Tab |
| Find | ⌘F | Ctrl+F |
| Next / previous match in Find | Enter / Shift+Enter | Enter / Shift+Enter |
New windows use the system's light or dark appearance and follow system changes until you change the theme in that window. Invert images is remembered between launches and shared across windows.
Renderer regression checks use development-only browser tooling, which is excluded from the app:
bunx playwright install webkit chromium
bun run test:uiThe browser checks use the production build and simulate older webview APIs. On macOS, also run the native webview check:
bun run build:web
bun run test:nativePDF.js's compatibility renderer and worker preserve embedded fonts on older system webviews. A small stream compatibility layer supports text extraction on macOS 15. Extraction errors are reported rather than shown as an empty result set.