sanity opens a folder and shows every file in it at once, as read-only panels on one zoomable canvas, grouped by directory. Zoomed out you see the structure of the project, zoomed in you read the code. When a file changes on disk, its panel flashes and the changed lines are marked.
It is a monitor. It runs next to the agent interface while coding agents work in a repository, so you can see which files they touch, where those sit in the tree, and zoom in to read what changed. It also plays the repository's git history the same way, one commit at a time.
Try it in the browser with a few public repositories, no install: sanity.milanrother.com
Releases have a .dmg for
macOS (Intel and Apple Silicon), an installer for Windows and an .AppImage
for Linux. The builds are not signed yet. On macOS, right click the app and
choose Open the first time. On Windows, click More info, then Run anyway. On
Linux, chmod +x the file and run it; if it refuses to start, the system has
no libfuse2 and ./sanity.AppImage --appimage-extract-and-run runs it
without one.
If the window opens at the right size and stays blank on Linux, WebKitGTK
could not allocate its buffer through your driver. Start it with
WEBKIT_DISABLE_DMABUF_RENDERER=1 in the environment, which is what the app
already does for itself on NVIDIA; virtual machines, software rendering and
remote sessions hit the same thing and have to be told.
sanity /path/to/repoOr open a folder from the Project menu.
- Space or
ffits the whole project, a double click fits a panel. /jumps to search, over file names and contents. Enter goes to the next hit, Escape clears.- The left and right arrow keys, or
[and], step through the git history. - Clicking a panel header opens the file in
$SANITY_EDITOR,$VISUAL,$EDITORor the system default. - Right click exports the view or the whole project as a 4K PNG, and on a panel shows the file in its folder.
- The Files menu sets which file types are shown and whether files ignored by git are listed. The View menu sets whether files are coloured by language, whether directories are named over the canvas, whether documents show all their pages, and whether each step through the history fits the whole project. The Theme menu has eight themes.
The folder is watched while it is open. When a file is saved, its panel flashes, the lines that were removed flash red and disappear, and the lines that were added come in green. A new file fades in where the layout puts it, and a deleted one fades out in red. When several files change at once, a commit, a formatter or a save of all open files, they start one after another across the canvas, so you can follow where it went.
The bands are drawn at every zoom level, so even with the whole project on
screen you can see where in a file the change was. A file that is rewritten
with the same content, by touch or a formatter with nothing to do, does not
flash. Moving or renaming a directory is picked up as its files moving.
In a git repository a ticker appears next to the search field. Its arrows, the
arrow keys, or [ and ], step through the commits, newest first. Each step is played like
a save: the lines the commit removed flash red, the ones it added flash green,
files it deleted fade out and files it created fade in. Stepping back plays
the same commit in reverse. Clicking the commit id returns to the present.
Right click, Export history, renders the replay to an MP4: from the oldest commit to the newest in the length you set, several commits a step when there are more than fit, with each commit's date, id and subject along the bottom. The video is H.264 where the machine can encode it and VP9 where it cannot, which includes the Linux AppImage; both play in browsers, VLC and mpv.
Each commit is laid out for the files it has, so there are no empty places for files that come later or went earlier. When a step adds or removes files, the panels slide to their new places, and by default the view fits the whole project again. The working tree is never touched; the contents come out of git's object store, and the watcher keeps running in the background, so returning to the present shows the folder as it is.
- Source files with syntax highlighting for 21 languages (tree-sitter), plus Verilog-A and SPICE.
- Jupyter notebooks as their cells rather than as JSON.
- Images, SVGs and PDFs as panels in their own proportions. A PDF shows its first page, or all of its pages with Show every page in the View menu, rendered the same way on every platform.
- Files ignored by git are left out, or listed as placeholders if you switch them on.
It draws only when something changes, so it uses no CPU while idle, and it stays usable up to around 100,000 files.
Search covers file names and the text of every file. Matching panels stay lit, the rest dims, matching lines are banded, and Enter walks through the hits.
npm ci && npm ci --prefix web
npm run app # development build
npm run app:build # release bundlemacOS and Windows need nothing beyond Rust and Node. Linux links against the
system WebKitGTK, which has to be there to build at all. On Debian and Ubuntu
that is libwebkit2gtk-4.1-dev librsvg2-dev patchelf file, and on Arch
webkit2gtk-4.1 librsvg patchelf. Without them the build stops at a
pkg-config error rather than anything about this project.
The AppImage carries the few GStreamer plugins WebKitGTK encodes the history
video with, which scripts/appimage-gstreamer.sh picks. On Debian and Ubuntu
they come from
gstreamer1.0-plugins-base gstreamer1.0-plugins-good gstreamer1.0-plugins-bad,
on Arch from gst-plugins-base gst-plugins-good gst-plugins-bad.
npm run app:appimage # Linux release bundleIt sets NO_STRIP=true, since the strip inside linuxdeploy cannot read
Arch's libraries, and stops early when patchelf is missing, which
linuxdeploy's GStreamer step needs. When the build still ends in nothing more
than failed to run linuxdeploy, --verbose shows the real error.
npm run release -- 1.13.2 notes.md --dry-run # the release as it would read
npm run release -- 1.13.2 notes.mdFrom an up to date main, this sets the version, pushes it and publishes the
release with notes.md as its changes. Publishing starts the Release
workflow, which attaches the installers about fifteen minutes later.
The web demo is the same app reading pre-built dumps of public repositories over HTTP:
npm run demo # clone and dump the demo repositories
npm run dev # http://localhost:5183The pictures in this README are made by npm run readme-shots, against the
dev server.
npm run check-allType checks, clippy, the TypeScript and Rust tests, and a set of scripts in
web/scripts that open the app in a real browser and check what it draws:
layout, text, pictures, change cues, performance, themes and more.
crates/sanity-core scanning, tokenising, git history, the wire format
crates/sanity-watch watching the folder, debounced into batches
src-tauri the desktop shell
web/src/lib/canvas layout and the WebGL2 renderer
web/src/lib/ui interface components
web/scripts browser checks and the README pictures
Design decisions and measurements are in the issues.
MIT, see LICENSE.




