Watch file diffs live in your terminal.
flashdiff is a minimal TUI that monitors a directory and shows you a live diff the moment any file is written — ideal alongside code generators, formatters, migrations, codemods, and any tool that rewrites files while you watch.
It complements git diff: instead of a post-hoc report, you get an
instant feedback loop while changes are still happening.
Built with Bubble Tea, Bubbles, and Lip Gloss. The theme is Catppuccin Mocha via Chroma, which also provides syntax highlighting for unchanged lines in the diff view.
go install github.com/subosito/flashdiff@latestOr build from source:
git clone https://github.com/subosito/flashdiff.git
cd flashdiff
go build -o flashdiff .flashdiff [flags] [path]path defaults to the current directory. flashdiff snapshots every watched
file at startup, then diffs each subsequent write against the last-known
content.
| Flag | Description |
|---|---|
-i, --include |
Glob of files to include (repeatable), e.g. -i '**/*.go' |
-e, --exclude |
Glob of files to exclude (repeatable) |
--no-vcs |
Do not respect .gitignore / .ignore files |
--version |
Print version and exit |
-h, --help |
Show help |
# Watch the current directory
flashdiff
# Watch only Go files under a project
flashdiff -i '**/*.go' ~/code/myapp
# Watch but exclude generated output
flashdiff -e 'dist/**' -e '*.tmp' ./src FILES │ DIFF main.go +12 -3 compact · words
───────────────────────────┼──────────────────────────────────────────
● main.go M│ 12 │ func main() {
✚ internal/new.go A│ 13 │ - run()
✖ old.txt D│ 13 │ + run(ctx)
│ 14 │ }
│ ⋮ 96 unchanged lines
│
───────────────────────────┴──────────────────────────────────────────
▒ flashdiff /path 128 tracked · 3 changes · ⧗ 2s │ tab pane / filter ? help q quit
The layout is intentionally minimal: pane titles up top (the focused pane's
title is highlighted, no box borders), a rule separates the titles from the
content, and a single status bar at the bottom — set off by a thin top
rule, no background fill — with the brand and watched path on the left and
live stats plus a few key hints on the right. The vertical divider crosses
the title rule (┼) and meets the status bar in a single ┴ joint.
- Status bar — a single bottom line. On the left, a pulsing indicator
(the watcher is live) plus the brand and watched path; on the right, the
tracked-file count, total changes, time since the last change (
⧗), and key hints. - FILES — every changed file, newest first. Icons:
●modified,✚new,✖deleted,◆binary. - DIFF — the selected file's diff. Line numbers sit in their own gutter,
separated from the content by a thin
│rule. The diff mode and word granularity (compact · words) are right-aligned in the title. Additions are green, deletions red, with word-level highlighting and Catppuccin Mocha syntax highlighting on unchanged lines.
| Key | Action |
|---|---|
j / ↓, k / ↑ |
Move file selection |
tab |
Switch pane focus |
enter / l |
Focus the diff pane |
d |
Cycle diff mode: unified → compact → split |
u |
Toggle word-level highlighting |
/ |
Filter the file list |
g / G |
Jump to top / bottom |
r |
Rescan the watched tree |
c |
Clear change history |
? |
Toggle help |
q / ctrl+c |
Quit |
The divider between panes is draggable, files can be clicked, and the mouse wheel scrolls whichever pane is under the cursor.
- compact (default) — unified, but long runs of unchanged lines collapse
into a
⋮ N unchanged linesmarker, so you focus on what changed. - unified — classic inline
+/-diff with full context. - split — side-by-side old (left) vs new (right).
Press d to cycle (compact → unified → split). Press u to toggle word-level
highlighting, which marks the exact tokens that changed within a modified line.
- Baseline — at startup, flashdiff walks the tree (honoring
.gitignore/.ignore, skip-lists, and your include/exclude globs) and caches each file's content. - Watch — a recursive
fsnotifywatcher streams events; rapid writes are debounced and batched. - Diff — on each event the file is re-read (with a settle check so partial writes don't corrupt the baseline), compared against the cached content with a line-level diff, and the cache is updated.
- Render — the newest change floats to the top of the list and its diff is shown, with word-level segments computed for paired changed lines.
Binary files (NUL-byte heuristic) and files over 1 MiB are shown as a placeholder rather than diffed.
Requires Go 1.25+ — that's the only hard dependency.
go build ./... # build
go test ./... # tests
go vet ./... # vet
gofmt -l . # should print nothingdevenv.nix is provided for contributors who use devenv,
but it is entirely optional. Releases are built with
GoReleaser from git tags.
flashdiff pins github.com/sergi/go-diff
for its diff engine. That repository is no longer actively maintained, but it
is a pure-Go port of Google's well-tested diff-match-patch algorithm — a
closed, deterministic algorithm with no network, cgo, or parser attack surface
— so a frozen version is low-risk. flashdiff uses only its Diff API (no Match
or Patch). If it ever becomes a problem, the intended migration path is the
zero-dependency aymanbagabas/go-udiff.
Contributions are welcome — see CONTRIBUTING.md. The short
version: Go 1.25+, go build/go test/gofmt clean, Conventional Commits,
and focused PRs.
flashdiff is released under the MIT License.
