Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
aaf2e58
Restore diffr import snapshot
sidkmenon Oct 8, 2026
077d1bc
Restore Whiteboard 0.2.1 release version
sidkmenon Oct 8, 2026
c328793
bench: add reproducible engine and context shaping workload
milanb17 Oct 8, 2026
adf86b3
diffr: give each leaf the id of its partner on the other side
sidkmenon Oct 8, 2026
9f2d370
diffr: read leaf partners instead of rebuilding alignment sets
sidkmenon Oct 8, 2026
49d3759
diffr: index plugin cursor regions by id
sidkmenon Oct 8, 2026
e547ef9
diffr: index plugin cursor fold states
sidkmenon Oct 8, 2026
df5f436
bench: time one diffed file from configuration
sidkmenon Oct 8, 2026
b9fd005
diffr: compile each language's queries on first use
sidkmenon Oct 8, 2026
b11b522
Build and test diffr with Bun 1.3.14 (AI-assisted)
sidkmenon Oct 8, 2026
f07f6b6
Delete the TUI pprint script and its wrapper (AI-assisted)
sidkmenon Oct 8, 2026
16311bc
Split the TUI's renderer-free theme and stream code out (AI-assisted)
sidkmenon Oct 8, 2026
c0369d0
TUI: light closed folds under the pointer and keep pseudocode beside …
sidkmenon Oct 8, 2026
04e0cbd
Move the TUI under diffr/packages (AI-assisted)
sidkmenon Oct 8, 2026
8b92692
TUI: parse theme files with smol-toml instead of Bun.TOML (AI-assisted)
sidkmenon Oct 8, 2026
36337fc
Move the TUI's renderer-free code into @diffr/viewer (AI-assisted)
sidkmenon Oct 8, 2026
36c0e04
Plan the TUI's code cells in @diffr/viewer (AI-assisted)
sidkmenon Oct 8, 2026
b41ae77
Click the rail the pointer arms, even beside a fold's label (AI-assis…
sidkmenon Oct 8, 2026
3b85d8d
Move the TUI's viewer state and keys into @diffr/viewer (AI-assisted)
sidkmenon Oct 8, 2026
b7833f8
Add /diffr, a Claude Code mod that reviews diffs in a pane (AI-assisted)
sidkmenon Oct 8, 2026
7647f59
Build the /diffr plugin and list it in the marketplace (AI-assisted)
sidkmenon Oct 8, 2026
8f94092
Test diffr's TypeScript frontends in CI (AI-assisted)
sidkmenon Oct 8, 2026
38a4897
Move foldBackground from the theme to the document rows (AI-assisted)
sidkmenon Oct 8, 2026
148224c
Make the terminal span helpers generic over any styled span (AI-assis…
sidkmenon Oct 8, 2026
96b3b47
Find visible rows with a direct search instead of Hunk's row windowin…
sidkmenon Oct 8, 2026
04713c8
Delete terminal helpers vendored from Hunk that nothing calls (AI-ass…
sidkmenon Oct 8, 2026
bdc4aa9
Name a comparison's ends once, in the protocol (AI-assisted)
sidkmenon Oct 8, 2026
3f65913
Copy a selection for an agent with Y (AI-assisted)
sidkmenon Oct 8, 2026
08a6ae9
Name where copied lines live, not diffr's sides (AI-assisted)
sidkmenon Oct 8, 2026
73b17eb
Mark files and folds viewed, in memory, in the TUI and the /diffr pan…
sidkmenon Oct 8, 2026
72d9265
Hint beside viewed boxes; drag selects lines in the /diffr pane, y an…
sidkmenon Oct 8, 2026
9b9b9b6
Keep viewed marks to whole files; drop scope marks and line tracking …
sidkmenon Oct 8, 2026
0efb1be
Search with / and walk matches with n and N, in the TUI and the /diff…
sidkmenon Oct 8, 2026
060061e
Narrow /diffr panes: a status line that says where you are, and the t…
sidkmenon Oct 8, 2026
01b6d94
Go to a changed file with Ctrl-P, ranked by fuzzysort, in the TUI and…
sidkmenon Oct 8, 2026
88c25b4
Keep ? keys on the status line, cut the hints before it, and open eve…
sidkmenon Oct 8, 2026
fdb216c
Give the model an open tool that shows a comparison in the /diffr pan…
sidkmenon Oct 8, 2026
556fe29
Return from the open tool once diffr names the comparison, not when i…
sidkmenon Oct 8, 2026
b66dd34
Search: one hue for matches, another for the one you're on, and only …
sidkmenon Oct 8, 2026
7b16a86
Open the file picker with Cmd-P too, where the terminal passes Cmd th…
sidkmenon Oct 8, 2026
8a3d7f4
Say [ ] changes, not [/] hunks, now that / searches (AI-assisted)
sidkmenon Oct 8, 2026
6f8ab85
Spell out [ and ] as previous and next change in the status line (AI-…
sidkmenon Oct 8, 2026
b3a2a5e
Go to the next and previous change with ]c and [c, as vim does; keep …
sidkmenon Oct 8, 2026
6ab28bc
Close search, the picker and other open views when the pane loses foc…
sidkmenon Oct 8, 2026
9a55b79
Lay out files of a million lines without overflowing the stack (AI-as…
sidkmenon Oct 8, 2026
271a601
Show a selection bar, and add selected lines to Claude Code's prompt …
sidkmenon Oct 8, 2026
f3d1300
Offer one action on the selection bar (AI-assisted)
sidkmenon Oct 8, 2026
4a457a4
Keep the selection bar inside the pane: spell out enter, and name a l…
sidkmenon Oct 8, 2026
fb1bf32
Select both sides of a diff, as GitLab positions a comment, and hand …
sidkmenon Oct 8, 2026
c6a55e9
Number a selection's patch from the diff's own order, so folds never …
sidkmenon Oct 8, 2026
d7575a8
Share the Diffr pane and comparison lifecycle across agent consumers
sidkmenon Oct 8, 2026
1897ea2
Preserve producer diagnostics and resolve review-relative paths
sidkmenon Oct 8, 2026
22382f6
Normalize shifted terminal symbols for agent consumers
sidkmenon Oct 8, 2026
082b382
Acknowledge model opens only after a valid comparison starts
sidkmenon Oct 8, 2026
f56ee68
Send selected code to agents as standard Git patches
sidkmenon Oct 8, 2026
5a43b9e
Add Pi split and fullscreen Diffr extension
sidkmenon Oct 8, 2026
afcf750
Document case-sensitive scope folding keys
sidkmenon Oct 8, 2026
27364b0
Preserve printable Pi punctuation keys
sidkmenon Oct 8, 2026
9e8ac15
Use public Pi overlays and register the Diffr model tool
sidkmenon Oct 8, 2026
9fb818f
Verify Pi tool draft flow and handle overlay mount cancellation
sidkmenon Oct 8, 2026
2510c3c
Close only the owned Pi overlay and validate nested cleanup
sidkmenon Oct 8, 2026
40b77bf
Verify Pi region selection across sides and redraws
sidkmenon Oct 8, 2026
3f1779c
Add OpenCode sidebar and fullscreen Diffr plugin
sidkmenon Oct 8, 2026
e5ae501
Use typed symbols for OpenCode viewer keys
sidkmenon Oct 8, 2026
dd8ee06
Redraw OpenCode selections and pane-local controls immediately
sidkmenon Oct 8, 2026
d11b962
Add acknowledged session-bound OpenCode model opens
sidkmenon Oct 8, 2026
07b71fe
Publish custom OpenCode commands without built-in command translation
sidkmenon Oct 8, 2026
e3d5723
Focus Diffr after OpenCode closes its command palette
sidkmenon Oct 8, 2026
90b2124
Use OpenCode v2 native session panels for a resizable review split
sidkmenon Oct 8, 2026
ca8aa3d
Preserve OpenCode mouse capture across selection redraws
sidkmenon Oct 8, 2026
d89f050
Make OpenCode v2-only support explicit
sidkmenon Oct 8, 2026
3bb5be6
Add Herdr split review pane with agent draft handoff
sidkmenon Oct 8, 2026
caa08ee
Use typed symbols for Herdr viewer keys
sidkmenon Oct 8, 2026
e0c04ae
Redraw Herdr selections and pane-local controls immediately
sidkmenon Oct 8, 2026
97ea8dc
Document consumer scope and add frontend CI checks
sidkmenon Oct 8, 2026
7775307
Document public host APIs and validate the OpenCode v2 panel
sidkmenon Oct 8, 2026
3f36d9f
Record full Pi Ghostty workflow and nested-overlay verification
sidkmenon Oct 8, 2026
ddc7fe8
Document snapshot identities in agent patch context
sidkmenon Oct 8, 2026
6f8821f
Record multi-row selection parity and OpenCode v2-only validation
sidkmenon Oct 8, 2026
63e1bd8
Use Pi-provided TypeBox in the installed extension
sidkmenon Oct 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
5 changes: 5 additions & 0 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
"name": "whiteboard",
"source": "./packages/agent-plugins/claude",
"description": "Connect your agent to dev.fast Whiteboard"
},
{
"name": "diffr",
"source": "./diffr/packages/claude-code/plugin",
"description": "Review diffs with diffr in a pane"
}
]
}
15 changes: 15 additions & 0 deletions .github/ISSUE_TEMPLATE/diffr_bug_report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
name: Diffr Bug Report
about: Report an issue with diffr
title: ''
labels: ''
assignees: ''
---

Thanks for reporting a bug! Please include all of the following:

(1) A description of the issue. A screenshot is often helpful too.

(2) A copy of what you're diffing. If you're diffing files, include the before and after files. If you're using diffr with a Git repository, include the URL and commit hash.

(3) The version of diffr you're using (see `diffr --version`) and your operating system.
37 changes: 37 additions & 0 deletions .github/workflows/diffr-coverage.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Diffr Coverage

on:
pull_request:
paths:
- "diffr/**"
push:
paths:
- "diffr/**"
workflow_dispatch:

jobs:
coverage:
runs-on: ubuntu-22.04
defaults:
run:
working-directory: diffr
steps:
# v4.2.2
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683

- name: Install Rust
run: rustup toolchain install stable --component llvm-tools-preview --target wasm32-wasip2
- name: Install cargo-llvm-cov
uses: taiki-e/install-action@ff869c26e4a7acc3087d9d6c1163a80d3957bca7 # cargo-llvm-cov

# WASI guests cannot use the host LLVM coverage runtime.
- name: Build WASM plugins
run: cargo +stable xtask build-plugins

- name: Generate code coverage
run: cargo +stable llvm-cov --all-features --workspace --lcov --output-path lcov.info
- name: Upload coverage to Codecov
uses: codecov/codecov-action@75cd11691c0faa626561e295848008c8a7dddffe # v5
with:
files: diffr/lcov.info
fail_ci_if_error: false
255 changes: 255 additions & 0 deletions .github/workflows/diffr-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
name: Diffr Release

on:
push:
tags:
- "diffr-*"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: diffr-release-${{ github.ref }}
cancel-in-progress: false

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- target: aarch64-apple-darwin
os: macos-14
- target: x86_64-apple-darwin
os: macos-15-intel
- target: x86_64-unknown-linux-gnu
os: ubuntu-22.04
- target: aarch64-unknown-linux-gnu
os: ubuntu-22.04-arm
- target: x86_64-pc-windows-msvc
os: windows-latest
runs-on: ${{ matrix.os }}
timeout-minutes: 45
defaults:
run:
shell: bash
working-directory: diffr
env:
TARGET: ${{ matrix.target }}
PYTHON: ${{ startsWith(matrix.os, 'windows') && 'python' || 'python3' }}
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.14
- uses: dtolnay/rust-toolchain@stable
- name: Build and package
env:
RELEASE_TAG: ${{ github.ref_type == 'tag' && github.ref_name || '' }}
run: |
version=$(cargo metadata --no-deps --format-version 1 | "$PYTHON" -c 'import json, sys; print(next(p["version"] for p in json.load(sys.stdin)["packages"] if p["name"] == "diffr-cli"), end="")')
if [ -n "$RELEASE_TAG" ] && [ "$RELEASE_TAG" != "diffr-$version" ]; then
echo "Tag $RELEASE_TAG does not match Cargo version $version" >&2
exit 1
fi
echo "VERSION=$version" >> "$GITHUB_ENV"
cargo xtask install --root "$RUNNER_TEMP/install"
"$PYTHON" scripts/release.py pack --version "$version" --target "$TARGET" --install "$RUNNER_TEMP/install" --output dist
- name: Verify extracted archives without Bun or checkout assets
run: |
# The TUI smoke test drives a Unix PTY.
if [ "$RUNNER_OS" != Windows ]; then
mkdir -p "$RUNNER_TEMP/extracted/bin"
tar -xzf "dist/diffr-$VERSION-$TARGET.tar.gz" -C "$RUNNER_TEMP/extracted/bin"
"$PYTHON" xtask/tests/smoke_install.py "$RUNNER_TEMP/extracted"
fi
"$PYTHON" scripts/smoke_cli_archive.py "dist/diffr-cli-$VERSION-$TARGET.tar.gz" "$VERSION"
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: ${{ matrix.target }}
path: diffr/dist/*.tar.gz
if-no-files-found: error

prepare:
needs: build
runs-on: ubuntu-22.04
defaults:
run:
working-directory: diffr
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
path: diffr/dist
merge-multiple: true
- name: Generate checksums and Homebrew formula
run: |
version=$(sed -n 's/^version = "\([^"]*\)"/\1/p' Cargo.toml | head -1)
python3 scripts/release.py formula --version "$version" --output dist
(cd dist && sha256sum --check SHA256SUMS)
ruby -c dist/diffr.rb
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: release
path: diffr/dist/*
if-no-files-found: error

homebrew:
needs: prepare
defaults:
run:
working-directory: diffr
strategy:
fail-fast: false
matrix:
os: [macos-14, macos-15-intel, ubuntu-22.04, ubuntu-22.04-arm]
runs-on: ${{ matrix.os }}
timeout-minutes: 15
env:
HOMEBREW_NO_AUTO_UPDATE: '1'
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release
path: diffr/dist
- name: Install and test the formula against staged archives
shell: bash
run: |
if [ -x /home/linuxbrew/.linuxbrew/bin/brew ]; then
eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)"
fi
tap="$(brew --repository)/Library/Taps/devdotfast/homebrew-tap"
mkdir -p "$tap/Formula"
python3 - "$tap/Formula/diffr.rb" <<'PYTHON'
from pathlib import Path
import re
import sys
formula = Path("dist/diffr.rb").read_text()
formula = re.sub(r'https://github.com/devdotfast/whiteboard/releases/download/[^/]+/', Path("dist").resolve().as_uri() + "/", formula)
Path(sys.argv[1]).write_text(formula)
PYTHON
brew install devdotfast/tap/diffr
brew test devdotfast/tap/diffr
python3 - "$tap/Formula/diffr.rb" <<'PYTHON'
from pathlib import Path
import sys
path = Path(sys.argv[1])
path.write_text(path.read_text().replace(' license ', ' revision 1\n license ', 1))
PYTHON
brew upgrade devdotfast/tap/diffr
brew test devdotfast/tap/diffr
python3 xtask/tests/smoke_install.py "$(brew --prefix devdotfast/tap/diffr)"
brew uninstall devdotfast/tap/diffr

packaging:
name: Release packaging
if: always()
needs: [build, prepare, homebrew]
runs-on: ubuntu-22.04
steps:
- name: Require all packaging checks to pass
env:
BUILD: ${{ needs.build.result }}
PREPARE: ${{ needs.prepare.result }}
HOMEBREW: ${{ needs.homebrew.result }}
run: test "$BUILD" = success && test "$PREPARE" = success && test "$HOMEBREW" = success

publish:
if: github.event_name == 'push' && github.ref_type == 'tag'
needs: packaging
permissions:
contents: write
runs-on: ubuntu-22.04
steps:
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release
path: dist
- name: Publish complete release
env:
GH_TOKEN: ${{ github.token }}
GH_REPO: ${{ github.repository }}
RELEASE_TAG: ${{ github.ref_name }}
run: |
gh release create "$RELEASE_TAG" dist/* --verify-tag --draft --latest=false --title "$RELEASE_TAG" --generate-notes
gh release edit "$RELEASE_TAG" --draft=false --latest=false

npm:
needs: publish
defaults:
run:
working-directory: diffr
permissions:
contents: read
id-token: write
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version: 1.3.14
- uses: pnpm/action-setup@0977fd99725f1db4007ccb2928dbb4e90d06cc86 # v6
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 24
registry-url: https://registry.npmjs.org
cache: pnpm
- working-directory: .
run: pnpm install --frozen-lockfile
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
with:
name: release
path: diffr/dist
- name: Publish platform packages, then @dev.fast/diffr
env:
VERSION: ${{ github.ref_name }}
# Unset once npm trusted publishing is configured for every package.
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
VERSION="${VERSION#diffr-}"
python3 scripts/release.py npm --version "$VERSION" --dist dist --output npm
for package in npm/*/ diffr-ts/; do
name=$(node -p "require('./${package}package.json').name")
if npm view "$name@$VERSION" version >/dev/null 2>&1; then
echo "$name@$VERSION is already published"
else
npm publish "$package" --provenance --access public
fi
done

npm-smoke:
needs: npm
strategy:
fail-fast: false
matrix:
os: [macos-14, macos-15-intel, ubuntu-22.04, ubuntu-22.04-arm, windows-latest]
runs-on: ${{ matrix.os }}
defaults:
run:
shell: bash
steps:
- uses: actions/setup-node@249970729cb0ef3589644e2896645e5dc5ba9c38 # v6
with:
node-version: 24
- name: Install from npm and run the platform binary
env:
VERSION: ${{ github.ref_name }}
run: |
VERSION="${VERSION#diffr-}"
cd "$RUNNER_TEMP"
npm init -y >/dev/null
for _ in 1 2 3 4 5 6; do
npm install --no-audit --no-fund "@dev.fast/diffr@$VERSION" zod && break
sleep 20
done
node --input-type=module -e "
import { execFileSync } from 'node:child_process';
import { diffrBinaryPath } from '@dev.fast/diffr';
const binary = diffrBinaryPath();
if (!binary) throw new Error('no platform package installed');
const version = execFileSync(binary, ['--version'], { encoding: 'utf8' }).trim();
if (version !== 'diffr $VERSION') throw new Error(version);
console.log(binary, version);
"
Loading
Loading