diff --git a/README.md b/README.md
index 04418161..661a6ac4 100644
--- a/README.md
+++ b/README.md
@@ -1,142 +1,124 @@
-# Git Diff Review
-
-Git Diff Review is a native macOS app for reviewing local Git changes before
-they are committed. It gives a GitHub-style review surface for a working tree:
-file navigation, staged/unstaged/untracked diffs, split or unified views,
-viewed-file tracking, review notes, and inline comments stored locally.
+
-The desktop app is built with Electron, Vue, and TypeScript. A Go backend reads
-repository state through Git, serves a local HTTP API over a Unix socket, and
-persists review sessions in SQLite.
+

-## What It Does
+# Git Diff Review
-- opens any local Git repository;
-- shows changed files with additions, deletions, status, and rename metadata;
-- renders staged, unstaged, and untracked patches;
-- supports split and unified diff modes;
-- can hide whitespace-only diff noise;
-- tracks viewed files per repository in local storage;
-- creates local review sessions with rich-text summaries;
-- stores inline review comments and timeline events in SQLite;
-- runs as a packaged macOS app or as a local development stack.
+**Review your local Git changes like a pull request — before you commit.**
+**On your machine. Nothing leaves it.**
-This is a local review tool. It does not publish comments to GitHub, create pull
-requests, or modify the repository contents.
+[](LICENSE)
+-black)
+[](https://github.com/oullin/git-diff/releases)
-## Workspace Layout
+
-| Path | Role |
-| --------------------- | ------------------------------------------------------------ |
-| `packages/ui` | Electron/Vue desktop app and renderer UI. |
-| `packages/bridge` | TypeScript HTTP client used by Electron. |
-| `packages/domain` | Shared DTOs + framework-agnostic diff logic, consumed by `ui` and `bridge`. |
-| `packages/api` | Go backend that reads Git diffs and stores reviews. |
-| `packages/tools` | Turbo cache wrapper and unsigned macOS release helper. |
+
-## Requirements
+Git Diff Review is a native macOS app that gives you a full GitHub-style review
+surface for your **local working tree**. Open any repository, walk every changed
+file, read the diff in split or unified view, mark files as you review them, leave
+yourself notes and inline comments — then commit with confidence. It never pushes
+your code anywhere.
-- macOS
-- Git
-- Go
-- Node.js with Corepack
-- pnpm `10.33.0`
+## Why you need it
-Enable pnpm through Corepack if needed:
+You commit changes you never actually re-read. `git diff` in the terminal is
+cramped and easy to skim past. Opening a real pull request just to review your own
+work is heavyweight — and it forces your code onto a server before it's ready.
-```sh
-corepack enable
-corepack prepare pnpm@10.33.0 --activate
-```
+Git Diff Review gives you the **pull-request review experience, locally**:
-## Quick Start
+- Catch the debug log, the stray `TODO`, the half-finished refactor *before* it
+ lands in history.
+- Review with the same muscle memory you already have from GitHub — file list,
+ viewed checkboxes, split diffs, inline comments.
+- Keep everything on your machine. No cloud, no sign-up, no telemetry.
-From the repository root:
+## What you get
-```sh
-pnpm install
-pnpm build
-pnpm --filter ui start
-```
+- **A real review surface.** Navigate changed files with status badges and
+ addition/deletion counts, jump between hunks, and search files across all your
+ repos at once.
+- **Staged, unstaged, and untracked** — see every kind of change, with rename
+ detection and whitespace noise you can hide.
+- **Split or unified diffs** with syntax highlighting, your choice per session.
+- **Viewed-file tracking.** Tick files off as you go (`0/14 viewed`) so you never
+ lose your place in a large change.
+- **Notes and inline comments.** Write a rich-text review summary and drop inline
+ comments on specific lines — stored locally and kept across sessions.
+- **Branch, commit, and PR context.** Switch branches, pin a commit to review
+ against, and browse pull requests and history without leaving the app.
+- **AI walkthroughs (optional).** Generate a guided walkthrough of a change to get
+ oriented fast.
+- **Keyboard-driven.** `j`/`k` to navigate, `v` to mark viewed, `c` to comment,
+ `⌘↵` to submit — review without touching the mouse.
+- **Native and local.** A fast Electron + Vue interface backed by a Go engine that
+ reads Git directly and stores your reviews in local SQLite.
-For active development:
+## See it in action
-```sh
-pnpm dev
-```
+| | |
+| --- | --- |
+|  |  |
+| **Walk every changed file** with status, counts, and viewed tracking. | **Read split or unified diffs** with syntax highlighting. |
+|  |  |
+| **Leave notes and inline comments** as you review. | **Generate an AI walkthrough** to get oriented fast. |
-The dev command starts the Vite renderer, compiles Electron, starts the Go
-backend, waits for the local socket API, and launches Electron.
+> Screenshots live in [`docs/images/`](docs/images/). See that folder's README for
+> what each capture should show.
-## Backend CLI
+## Install
-The Electron app normally starts the backend automatically. To inspect the
-backend directly:
+> **macOS on Apple Silicon.** The app is currently in beta and distributed
+> unsigned, so macOS Gatekeeper needs one extra click on first launch.
-```sh
-cd packages/api
-go run ./cmd help
-go run ./cmd serve-http --socket /tmp/git-diff.sock
-```
+**Download the app**
-To point the Electron app at an already-running backend, set
-`API_BRIDGE_SOCKET` to the socket path before launching it.
+1. Grab the latest `.dmg` from the
+ [Releases page](https://github.com/oullin/git-diff/releases).
+2. Open it and drag **Git Diff Review** to Applications.
+3. On first launch, **right-click the app → Open**, then confirm. (macOS blocks
+ unsigned apps on a normal double-click; right-click → Open only needs doing
+ once.)
-## Development Commands
+**Homebrew** _(planned)_
```sh
-pnpm build
-pnpm test
-pnpm lint
-pnpm format:check
-pnpm check
+brew install --cask oullin/tap/git-diff
```
-Useful package-level commands:
+## Your code stays yours
-```sh
-pnpm --filter ui build
-pnpm --filter ui test
-pnpm --filter ui dev
-go test ./packages/api/...
-```
-
-## Review Data
-
-Review sessions, comments, events, and preferences are local. By default the Go
-backend stores the review database under:
+Git Diff Review reads your repository state through Git and serves a local API over
+a Unix socket — it talks only to your machine. Review sessions, comments, events,
+and preferences are stored in a local SQLite database at:
```text
~/Library/Application Support/git-diff/reviews.sqlite3
```
-Viewed-file state is stored by the renderer per repository root.
+It **does not** publish comments to GitHub, create pull requests, or modify your
+repository contents. It's a review surface, nothing more.
-## Release
+## Build from source
-Releases are built with [Electron Forge](https://www.electronforge.io/). Tag a
-version to cut a release in CI:
-
-```sh
-git tag v0.1.1 && git push origin v0.1.1
-```
-
-The `Release` workflow builds the unsigned macOS arm64 `.dmg` + `.zip`, publishes
-a GitHub Release, and opens a Homebrew cask bump PR against `oullin/homebrew-tap`
-on final (non-prerelease) tags. See [docs/distribution.md](docs/distribution.md).
-
-To build the same artifacts locally:
-
-```sh
-pnpm release:mac:unsigned # builds the Go API + renderer, then `electron-forge make`
-```
+Git Diff Review is a pnpm + Turbo monorepo pairing an Electron/Vue/TypeScript
+desktop app with a Go backend.
-The Electron package is configured (in `packages/ui/forge.config.ts`) with:
+| Path | Role |
+| ----------------- | --------------------------------------------------------------------------- |
+| `packages/ui` | Electron/Vue desktop app and renderer UI. |
+| `packages/bridge` | TypeScript HTTP client used by Electron. |
+| `packages/domain` | Shared DTOs + framework-agnostic diff logic, consumed by `ui` and `bridge`. |
+| `packages/api` | Go backend that reads Git diffs and stores reviews. |
+| `packages/tools` | Turbo cache wrapper and unsigned macOS release helper. |
-- app id: `io.gocanto.git-diff`
-- product name: `Git Diff Review`
-- GitHub release target: `oullin/git-diff`
+To set up the toolchain, run it locally, and learn the development commands, see
+**[docs/development.md](docs/development.md)**. Releases are built with
+[Electron Forge](https://www.electronforge.io/) — for tagging, packaging, signing,
+and the release pipeline, see **[docs/distribution.md](docs/distribution.md)**.
## License
-See [LICENSE](LICENSE).
+[MIT](LICENSE) © Gus Canto
diff --git a/docs/development.md b/docs/development.md
new file mode 100644
index 00000000..2e1eb780
--- /dev/null
+++ b/docs/development.md
@@ -0,0 +1,123 @@
+# Development
+
+How to set up the toolchain, run Git Diff Review locally, and work in the
+monorepo. For the product overview and install instructions, see the
+[README](../README.md). For packaging and releases, see
+[distribution.md](distribution.md).
+
+## Requirements
+
+- macOS
+- Git
+- Go
+- Node.js with Corepack
+- pnpm `10.33.0`
+
+Enable pnpm through Corepack if needed:
+
+```sh
+corepack enable
+corepack prepare pnpm@10.33.0 --activate
+```
+
+## Quick start
+
+From the repository root:
+
+```sh
+pnpm install
+pnpm build
+pnpm --filter ui start
+```
+
+## Active development
+
+```sh
+pnpm dev
+```
+
+The dev command starts the Vite renderer, compiles Electron, starts the Go
+backend, waits for the local socket API, and launches Electron.
+
+## Backend CLI
+
+The Electron app normally starts the backend automatically. To inspect the
+backend directly:
+
+```sh
+cd packages/api
+go run ./cmd help
+go run ./cmd serve-http --socket /tmp/git-diff.sock
+```
+
+To point the Electron app at an already-running backend, set `API_BRIDGE_SOCKET`
+to the socket path before launching it.
+
+## Development commands
+
+```sh
+pnpm build
+pnpm test
+pnpm lint
+pnpm format:check
+pnpm check
+```
+
+Useful package-level commands:
+
+```sh
+pnpm --filter ui build
+pnpm --filter ui test
+pnpm --filter ui dev
+go test ./packages/api/...
+```
+
+## Formatting
+
+Format changed sources (Go + TS/Vue):
+
+```sh
+pnpm format # or: make format
+```
+
+To format the entire repo, use `pnpm format-all` (`make format-all`). Run the
+formatter after each change so commits stay clean.
+
+## Review data
+
+Review sessions, comments, events, and preferences are local. By default the Go
+backend stores the review database under:
+
+```text
+~/Library/Application Support/git-diff/reviews.sqlite3
+```
+
+Viewed-file state is stored by the renderer per repository root.
+
+## Release
+
+Releases are built with [Electron Forge](https://www.electronforge.io/). Tag a
+version to cut a release in CI:
+
+```sh
+git tag v0.1.1 && git push origin v0.1.1
+```
+
+The `Release` workflow builds the unsigned macOS arm64 `.dmg` + `.zip`, publishes
+a GitHub Release, and opens a Homebrew cask bump PR against `oullin/homebrew-tap`
+on final (non-prerelease) tags.
+
+To build the same artifacts locally:
+
+```sh
+pnpm release:mac:unsigned # builds the Go API + renderer, then `electron-forge make`
+```
+
+The Electron package is configured (in `packages/ui/forge.config.ts`) with:
+
+- app id: `io.gocanto.git-diff`
+- product name: `Git Diff Review`
+- GitHub release target: `oullin/git-diff`
+
+See [distribution.md](distribution.md) for the full signing, notarization, and
+Homebrew release flow.
diff --git a/docs/images/README.md b/docs/images/README.md
new file mode 100644
index 00000000..1311eaed
--- /dev/null
+++ b/docs/images/README.md
@@ -0,0 +1,17 @@
+# Screenshots
+
+Images referenced by the [project README](../../README.md). Drop the real
+captures here using the exact filenames below so the README renders them
+automatically. Use PNG, retina resolution (capture at 2x), and a light UI theme
+for consistency.
+
+| File | What it should show |
+| --------------------- | ------------------------------------------------------------------------------ |
+| `hero.png` | The main review window: file list, a split diff, branch/commit bar, toolbar. |
+| `file-navigation.png` | The changed-files sidebar with status badges, +/- counts, and viewed checkboxes. |
+| `split-view.png` | A diff in split (or unified) view with syntax highlighting. |
+| `review-notes.png` | A review summary and/or an inline comment on a line. |
+| `walkthrough.png` | The "Generate walkthrough" AI feature in use. |
+
+Until a file is added, its link in the README will show as a broken image — that
+is expected and resolves the moment the PNG lands here.
diff --git a/docs/images/file-navigation.png b/docs/images/file-navigation.png
new file mode 100644
index 00000000..d3a618ac
Binary files /dev/null and b/docs/images/file-navigation.png differ
diff --git a/docs/images/hero.png b/docs/images/hero.png
new file mode 100644
index 00000000..a7d40944
Binary files /dev/null and b/docs/images/hero.png differ
diff --git a/docs/images/review-notes.png b/docs/images/review-notes.png
new file mode 100644
index 00000000..a715ff2b
Binary files /dev/null and b/docs/images/review-notes.png differ
diff --git a/docs/images/split-view.png b/docs/images/split-view.png
new file mode 100644
index 00000000..80961966
Binary files /dev/null and b/docs/images/split-view.png differ
diff --git a/docs/images/walkthrough.png b/docs/images/walkthrough.png
new file mode 100644
index 00000000..2ee7cf9c
Binary files /dev/null and b/docs/images/walkthrough.png differ
diff --git a/packages/ui/scripts/collect-release-artifacts.sh b/packages/ui/scripts/collect-release-artifacts.sh
index 3a235e9c..b0cddf09 100755
--- a/packages/ui/scripts/collect-release-artifacts.sh
+++ b/packages/ui/scripts/collect-release-artifacts.sh
@@ -5,14 +5,15 @@
#
# Run from the repo root. Usage: collect-release-artifacts.sh
set -euo pipefail
-shopt -s nullglob globstar
version="${1:?usage: collect-release-artifacts.sh }"
+# macOS GitHub runners ship bash 3.2, which lacks `globstar`. Use `find` instead
+# of `**` so artifact discovery works regardless of the bash version.
out="packages/ui/out/make"
-dmgs=("$out"/**/*.dmg)
-zips=("$out"/**/*.zip)
-if [ "${#dmgs[@]}" -eq 0 ] || [ "${#zips[@]}" -eq 0 ]; then
+src_dmg="$(set +o pipefail; find "$out" -type f -name '*.dmg' | head -n1)"
+src_zip="$(set +o pipefail; find "$out" -type f -name '*.zip' | head -n1)"
+if [ -z "$src_dmg" ] || [ -z "$src_zip" ]; then
echo "Missing Forge artifacts under $out"
find "$out" -type f || true
exit 1
@@ -21,8 +22,8 @@ fi
mkdir -p dist-release
dmg="git-diff-review-${version}-arm64.dmg"
zip="git-diff-review-${version}-arm64.zip"
-cp "${dmgs[0]}" "dist-release/${dmg}"
-cp "${zips[0]}" "dist-release/${zip}"
+cp "$src_dmg" "dist-release/${dmg}"
+cp "$src_zip" "dist-release/${zip}"
cd dist-release
shasum -a 256 "$dmg" "$zip" > SHASUMS256.txt
diff --git a/packages/ui/src/components/diff/PierreDiffBody.vue b/packages/ui/src/components/diff/PierreDiffBody.vue
index ef5cb10c..f0248c9f 100644
--- a/packages/ui/src/components/diff/PierreDiffBody.vue
+++ b/packages/ui/src/components/diff/PierreDiffBody.vue
@@ -100,7 +100,6 @@ function buildOptions(section: DiffSection): FileDiffOptions {
// Pure-JS Shiki avoids wasm/worker bundling friction under Electron's
// file:// origin; revisit for perf in a later pass.
preferredHighlighter: 'shiki-js',
- useCSSClasses: true,
// Library renders its own "+" gutter affordance (single click or drag to
// select a range); we only handle the resulting selection.
enableGutterUtility: true,
@@ -161,7 +160,12 @@ async function renderAll(): Promise {
instances.set(section.id, fd);
}
- fd.render({ fileDiff: meta, fileContainer: el });
+ // Pass the wrapper as `containerWrapper` (not `fileContainer`): the library
+ // then creates its own custom element inside `el`, whose
+ // constructor adopts the core stylesheet that resolves Shiki's per-token
+ // CSS variables to actual colors. Handing it a plain element as
+ // `fileContainer` skips that element, so tokens render uncoloured.
+ fd.render({ fileDiff: meta, containerWrapper: el });
}
}
diff --git a/packages/ui/src/style.css b/packages/ui/src/style.css
index 4e2b2ccd..d3a571cd 100644
--- a/packages/ui/src/style.css
+++ b/packages/ui/src/style.css
@@ -631,171 +631,6 @@
color: var(--status-attention-fg);
}
- .diff-table {
- font-family: var(--diffs-font-family);
- font-size: var(--diffs-font-size);
- line-height: var(--diffs-line-height);
- background: var(--code-bg);
- color: var(--foreground);
- }
-
- .file-body {
- overflow-x: auto;
- }
-
- .diff-line {
- display: grid;
- width: 100%;
- min-width: max-content;
- grid-template-columns: 3.25rem 3.25rem minmax(0, 1fr) 2rem;
- text-align: left;
- border: 0;
- }
-
- .diff-line code {
- white-space: pre;
- padding: 0 0.875rem;
- color: var(--foreground);
- background: transparent;
- font-family: var(--font-code);
- }
-
- .line-no {
- color: var(--muted-foreground);
- padding: 0 0.625rem;
- text-align: right;
- user-select: none;
- font-variant-numeric: tabular-nums;
- font-weight: 500;
- }
-
- .diff-line:not(.diff-meta) .line-no:nth-child(1),
- .diff-line:not(.diff-meta) .line-no:nth-child(2) {
- border-right: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
- }
-
- .diff-meta {
- background: color-mix(in srgb, var(--muted-foreground) 8%, transparent);
- color: var(--muted-foreground);
- }
-
- .diff-meta code {
- color: var(--muted-foreground);
- }
-
- .diff-add {
- background: var(--diff-added-bg);
- }
-
- .diff-add .line-no {
- background: var(--diff-added-bg-strong);
- color: var(--diff-added);
- }
-
- .diff-del {
- background: var(--diff-removed-bg);
- }
-
- .diff-del .line-no {
- background: var(--diff-removed-bg-strong);
- color: var(--diff-removed);
- }
-
- .diff-line.diff-split-row {
- grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 2rem;
- align-items: stretch;
- min-width: 0;
- }
-
- .diff-line.diff-split-row .split-side code {
- white-space: pre-wrap;
- overflow-wrap: anywhere;
- }
-
- .diff-line.diff-split-meta {
- grid-template-columns: minmax(0, 1fr);
- min-width: 0;
- }
-
- .split-side {
- display: grid;
- grid-template-columns: 3.25rem minmax(0, 1fr);
- text-align: left;
- border: 0;
- background: transparent;
- padding: 0;
- color: inherit;
- font: inherit;
- cursor: pointer;
- min-width: 0;
- }
-
- .split-side:disabled {
- cursor: default;
- }
-
- .split-side + .split-side {
- border-left: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
- }
-
- .split-side .line-no {
- border-right: 1px solid color-mix(in srgb, var(--border) 50%, transparent);
- }
-
- .split-side.side-del {
- background: var(--diff-removed-bg);
- }
-
- .split-side.side-del .line-no {
- background: var(--diff-removed-bg-strong);
- color: var(--diff-removed);
- }
-
- .split-side.side-add {
- background: var(--diff-added-bg);
- }
-
- .split-side.side-add .line-no {
- background: var(--diff-added-bg-strong);
- color: var(--diff-added);
- }
-
- .split-side.side-empty {
- background: color-mix(in srgb, var(--muted-foreground) 6%, transparent);
- }
-
- .split-side.side-empty .line-no {
- background: transparent;
- }
-
- .comment-icon {
- align-self: center;
- color: var(--muted-foreground);
- opacity: 0;
- transition: opacity 160ms ease;
- }
-
- .diff-line:hover .comment-icon {
- opacity: 1;
- }
-
- .comment-row {
- position: sticky;
- left: 0;
- width: min(100%, 720px);
- margin: 0.5rem 1rem 0.75rem 5rem;
- border: 1px solid var(--file-border);
- border-left: 3px solid var(--accent-blue);
- border-radius: 13px;
- background: var(--file-bg);
- padding: 0.75rem 0.875rem;
- color: var(--foreground);
- corner-shape: squircle;
- -webkit-backdrop-filter: blur(24px) saturate(1.3);
- backdrop-filter: blur(24px) saturate(1.3);
- box-shadow: 0 16px 46px -34px rgb(0 0 0 / 0.32);
- }
-
/* window chrome — squircle + glass treatment */
.window-frame {
position: relative;