The missing PR review experience on GitHub
Full-screen diffs, searchable file tree, inline comments, split/unified layout, 100+ languages, and 12 themes.
diffy adds a View Diff button to GitHub pull requests so you can review the entire change set in one fast, full-screen view - with a searchable file tree, inline review comments, split or unified layout, syntax highlighting for 100+ languages, and 12 themes. Powered by Pierre Trees and Pierre Diffs.
diffy.mp4
Launching soon on the Chrome Web Store and Firefox Add-ons. Install from source below in the meantime.
Inspired by Linear View Diff by Carter McAlister - a Chrome extension that adds a View Diff button on Linear review pages and renders linked GitHub PRs with Pierre Trees and Pierre Diffs.
- One-click access - a View Diff button on every GitHub pull request
- Full-screen diffs - scroll through the entire PR in one continuous view
- Fast and smooth - opens quickly and remembers where you left off
- File tree with search - jump to any changed file, even in huge PRs
- Review comments inline - read existing threads right in the diff
- Comment from the diff - leave inline comments and reply without leaving the viewer
- Split or unified layout - switch between side-by-side and stacked views
- Syntax highlighting - 100+ languages with clear, colorized diffs and sticky file headers
- 12 themes - Pierre dark/light, GitHub, Catppuccin, Dracula, Nord, Tokyo Night, and more
- Private repo support - add a GitHub token in the extension popup when needed
GitHub's Files changed tab works well for most pull requests. It starts to break down on PRs with large code changes — hundreds of files, big diffs, or heavy review threads - where you hit documented diff limits and long-standing UX friction.
| The GitHub problem | How diffy helps |
|---|---|
| "Diff too large to display" - GitHub caps total PR diffs at 20,000 lines / 1 MB and refuses to render beyond that | Fetches changes through the GitHub API and renders them in a dedicated viewer, bypassing the web UI diff renderer |
| 300+ changed files — the unified diff endpoint returns a 406 error; GitHub tells you to use the files API instead | Uses the paginated files API to load every changed file, then assembles the full diff |
| "Load diff" on every large file - GitHub only auto-loads the first 400 lines / 20 KB per file; you click to load the rest one file at a time | Shows full file patches in one continuous scroll - no per-file load buttons |
| File-by-file review - expand, collapse, and jump between files; easy to lose context across a big PR | One continuous full-PR view with a searchable file tree to jump anywhere instantly |
| Slow, freezing Files changed tab - reviewers report UI lag, high memory use, and multi-second freezes even on medium PRs | A lightweight overlay with fast rendering - stays responsive where GitHub's tab struggles |
| Comments scattered across tabs - unresolved threads are hard to track in Conversation vs Files changed | Inline review comment threads rendered directly on the lines you're reading |
- Open any pull request on GitHub - diffy adds a View Diff button to the page.
- Click it to open a full-screen diff viewer with every changed file in one scrollable view.
- Use the file tree on the left to search and jump between files.
- Read and leave review comments directly on the lines you're looking at.
diffy/
├── src/
│ ├── assets/ # Extension icon source (logo.png)
│ ├── components/ # React UI for the diff overlay
│ │ ├── app/ # App shell: loading, error, and error-modal states
│ │ ├── diff/ # Main viewer, file tree, header controls
│ │ └── review/ # Inline comment threads, composers, reply UI
│ ├── entrypoints/
│ │ ├── github-pr.content/ # Content script entry (button injection, prefetch)
│ │ │ ├── index.tsx # Page lifecycle, overlay mount/unmount
│ │ │ └── overlay.tsx # React root for the overlay (ESM bundle target)
│ │ └── popup/ # Extension popup (GitHub token settings)
│ ├── hooks/ # React hooks (CodeView, themes, worker pool readiness)
│ ├── lib/ # Core logic
│ │ ├── github/ # GitHub API, page integration, comment rendering
│ │ ├── overlay/ # Content script ↔ iframe messaging
│ │ ├── diff/ # Worker, languages, themes, layout prefs
│ │ ├── code-view/ # PR patches → Pierre CodeView items
│ │ ├── review/ # Review threads, formatting, reply UI
│ │ ├── file-tree/ # File tree panel helpers
│ │ └── theming/ # Tree chrome theme resolution
│ ├── modules/
│ │ └── shiki-pruner.ts # Prunes unused Shiki chunks after production builds
│ ├── providers/ # React context and providers (themes, auth, worker pool, sidebar)
│ └── types/ # Ambient type declarations
└── wxt.config.ts # Manifest, permissions, Vite plugins
git clone git@github.com:suveshmoza/diffy.git
cd diffy
pnpm install
pnpm devpnpm dev watches for changes and rebuilds the extension. Reload the unpacked extension in your browser after each rebuild.
pnpm build # Chrome
pnpm build:firefox # Firefox
pnpm zip # Packaged Chrome zip
pnpm zip:firefox # Packaged Firefox zip-
Build the extension (or keep
pnpm devrunning):pnpm build
-
Open
chrome://extensions. -
Enable Developer mode.
-
Click Load unpacked.
-
Select the
.output/chrome-mv3folder.
-
Build for Firefox:
pnpm build:firefox
-
Open
about:debugging#/runtime/this-firefox. -
Click Load Temporary Add-on….
-
Select any file inside
.output/firefox-mv2(for examplemanifest.json).
To remove the extension, use Remove on the extension card (Chrome) or reload the temporary add-on page (Firefox).
The extension popup lets you save an optional personal access token. A token is required for:
- Private repositories.
- Higher API rate limits.
- Posting review comments.
After saving a token, reload any open PR tabs for it to take effect.
Syntax highlighting is powered by Shiki via Pierre Diffs. The extension currently ships 107 Shiki grammars (100+ languages, including common aliases like js/javascript and py/python). Which languages are included is controlled by a blocklist, every Shiki bundled language is enabled except those listed in blocklist.
| File | Editable? | Role |
|---|---|---|
src/lib/diff/blocked-lang-ids.json |
Yes | Language ids to exclude from the worker and production bundle |
src/lib/diff/lang-ids.json |
No — auto-generated | Language ids the diff worker loads at runtime |
src/lib/diff/lang-ids.ts |
No | Typed re-export of lang-ids.json for app code |
src/modules/shiki-pruner.ts |
No | Regenerates lang-ids.json and prunes unused Shiki chunks on pnpm build |
On every production build, shiki-pruner reads the blocklist, computes all Shiki bundled languages minus blocked, writes the result to src/lib/diff/lang-ids.json, and removes unneeded grammar chunks from the output zip.
Do not edit src/lib/diff/lang-ids.json by hand — changes will be overwritten on the next build.
Language ids are Shiki language ids (e.g. python, typescript, shell, dockerfile).
-
Open
src/lib/diff/blocked-lang-ids.json. -
Remove the language id you want to support (keep the JSON array valid and sorted if you like).
-
Rebuild:
pnpm build
-
Reload the unpacked extension.
The new language is included in src/lib/diff/lang-ids.json, loaded by the diff worker, and its grammar chunk is kept in the bundle.
- Open
src/lib/diff/blocked-lang-ids.json. - Add the language id to the array.
- Rebuild with
pnpm buildand reload the extension.
Removing a language shrinks the zip; keeping the blocklist lean helps stay under the Chrome Web Store size limit (2MB).
Syntax themes are configured separately in src/lib/diff/themes/ids.json. The theme picker currently exposes 12 themes:
pierre-dark, pierre-light, github-dark, github-light, catppuccin-mocha, catppuccin-latte, dracula, one-dark-pro, nord, tokyo-night, everforest-dark, everforest-light
To add or remove a theme, edit src/lib/diff/themes/ids.json and rebuild. Theme changes do not go through the language blocklist.
| Script | Description |
|---|---|
pnpm dev |
Watch mode - rebuild on file changes |
pnpm build |
Production build for Chrome |
pnpm build:firefox |
Production build for Firefox |
pnpm compile |
Type-check with tsc --noEmit |
pnpm lint |
Lint with Oxlint |
pnpm lint:fix |
Lint and auto-fix |
pnpm fmt |
Format with Oxfmt |
pnpm fmt:check |
Check formatting |