Skip to content

Developer Guide

github-actions[bot] edited this page Oct 18, 2026 · 2 revisions

Welcome! This page gets you from an empty folder to a running Nibtree, and then points you to the page you need next.

Nibtree is a native macOS Git client. The backend is Rust on Tauri 2 in src-tauri/. The UI is Svelte 5 and TypeScript in src/, and every text pane is a CodeMirror 6 editor. The app uses the web view that macOS already ships, so it starts fast and stays small.

What you need

  • macOS. The app targets macOS today. Some backend code (the memory readout, the MCP screenshot) is macOS only.
  • Rust (stable), installed with rustup. You also get cargo and clippy.
  • Xcode Command Line Tools (xcode-select --install), for the linker and a system git.
  • Bun, the only JavaScript tool in this project.
  • git, any recent version. The app runs your own git binary for every write.

Why Bun and never npm, npx or node

The system Node on the main development machine is v16, and Vite 8 needs a much newer Node. Instead of asking everyone to manage Node versions, every script in package.json runs on Bun's own runtime with bun --bun. So please never type npm, npx or node: they would pick up the old Node and fail in confusing ways.

First run

git clone https://github.com/shibbirweb/nibtree.git
cd nibtree
bun install                    # dependencies (commit bun.lock if it changes)
bun tauri dev                  # run the app with hot reload
bun tauri dev -- -- /path/to/folder   # open a folder straight away

The first bun tauri dev compiles every Rust crate, so it takes a few minutes. After that, Svelte changes reload instantly and Rust changes rebuild only what changed.

flowchart LR
  A["git clone"] --> B["bun install"]
  B --> C["bun tauri dev"]
  C --> D["Make a change"]
  D --> E["Run the checks"]
  E --> F["Pull request into develop"]
  E -. "something fails" .-> D
Loading

Demo data

You rarely want to test on a real project. Three scripts build throwaway repositories for you. Each one refuses a folder that is not empty, so it can never overwrite your work.

Script What it builds
scripts/make-conflict-repo.sh <dir> [--rebase] A repository stopped in a merge (or a rebase) with every conflict type, long files and M, A, U, D and R samples. Use it for the merge tool and the conflicts dialog.
scripts/make-workspace-demo.sh <dir> A folder with several repositories: one nested, one mid-merge, one clean, plus a plain folder with no git. Use it for workspaces.
scripts/make-docs-demo.sh <dir> The workspace the wiki screenshots are taken in: a shop repository with four authors, branches, tags, a stash and a remote, a repository with conflicts, a plain notes/ folder, a second folder, and extras/ with a submodule and Git LFS images. See Docs and Screenshots.
scripts/make-conflict-repo.sh /tmp/conflict-demo
bun tauri dev -- -- /tmp/conflict-demo

The Rust tests run make-conflict-repo.sh and check its exact output. If you change that script, update src-tauri/src/git/tests.rs too.

The checks

Before you say a change is done, run the checks listed in Testing: bun run check, bun run test, cargo test and cargo clippy --all-targets -- -D warnings, plus the docs and version checks when you touch those. CI runs the same ones, so a clean local run means a green pull request. To build a release app locally, run bun tauri build --bundles app; it lands in src-tauri/target/release/bundle/macos.

Map of the developer pages

Platform pages

Page What it covers
Architecture The big picture: UI, IPC, commands, git reads and writes, events, memory, security and the dev IPC bridge.
Project Layout What lives in which folder and file.
Backend Tauri commands, errors, the git CLI runner, git2, the watcher and config files.
Backend Services Terminal, search, scripts, Git Console, GitHub, MCP and other Rust services.
Frontend The shell, stores, Svelte 5 runes, the API bridge, keys, menus and colors.
Menu Keys and Routing How a key reaches the editor, the window or the native menu.
Frontend Modules The feature folders in src/lib and what loads lazily.
CodeMirror Patterns State fields, compartments, widgets and keymaps in every text pane.
Commands and Events Every backend command and event, with its arguments and return type, over four pages.
Testing Rust tests with real repositories, Vitest and what "done" means.
Test Suites Every test file and what it covers.
Settings Reference Every key in settings.json and state.json.
Debugging Web view devtools, Rust panics, IPC errors, the memory log and common dead ends.
Releases and CI CI, the beta and stable release workflows and the wiki deploy.
Versioning and Changelog Where the version lives, scripts/version.ts and the changelog rules.
Platforms and Signing The universal macOS build, signing, and the Windows and Linux plan.
Windows Support Console windows, the home folder, PATH and paths on Windows.
Windows Bugs We Fixed Bugs the Windows CI job found, and the fixes.
Windows MCP and CLI The console program, the screenshot and quoting on Windows.
Docs and Screenshots How this wiki is checked, how screenshots are taken, and recipes for keeping docs in sync.
Contributing Code style, commits, pull requests and the docs checklist.

Feature chapters

Each chapter explains why a feature exists, how it works, the decisions behind it and the bugs we fixed.

If you want to know how the app looks to its users, start with Getting Started.

Lessons learned

Node v16 was too old for Vite 8. Early on, vite and svelte-kit crashed under the system Node. We could have required a newer Node, but Bun was already installed and can run these tools itself. So every script became bun --bun <tool>, which forces Bun's runtime even when a tool's first line asks for node.

bun --bun tauri called itself. The first tauri script in package.json was bun --bun tauri. Bun looks for a script named tauri before it looks for a binary, so the script ran itself again and again until macOS refused to start more processes (EAGAIN). The script is now bunx --bun tauri, which always runs the Tauri CLI binary.

Port 1420 was already in use. bun tauri dev starts Vite on port 1420, and Vite is set to strictPort, because Tauri loads the UI from that exact address. When a dev session dies badly, the old Vite process can keep the port. The fix is to stop the old bun or vite process before starting again, not to change the port.

Home

Using Nibtree

Developers

How features work

Clone this wiki locally