Repository navigation
How Navigation Works
Back and Forward take you through places you have been (a file line, a change's diff, a commit in the Log). The user side is in Navigation.
Reading code means jumping around and going back. The history must record real jumps, not every keystroke.
navHistory.ts is pure, tested logic; navigation.svelte.ts is the reactive store that connects it to the views.
classDiagram
class NavLocation {
<<union>>
}
class FileLocation {
+string kind
+string filePath
+number line
}
class DiffLocation {
+string kind
+string repoRoot
+string path
+string area
+number line
}
class LogLocation {
+string kind
+string repoRoot
+string commitId
+string filePath
+number line
+string lineText
}
NavLocation <|-- FileLocation
NavLocation <|-- DiffLocation
NavLocation <|-- LogLocation
kind is "file" (or missing), "diff" or "log". A Log step's filePath may be null and its line is optional. lineText is the text of the blamed line, used to find it in the commit when line is only a guess. FileLocation.filePath is absolute, like every file tab; DiffLocation.path and LogLocation.filePath are repo-relative.
NavigationHistory keeps a back stack, a current location and a forward stack. record(location) first refuses a file location whose path is a pseudo tab (isPseudoTab: a terminal, commit, Git or branch tab), then asks sameSpot(current, location):
- Same file within
JUMP_LINES(10) lines: onlycurrentmoves. - Same diff (repository, path, area) within 10 lines: same.
- Same commit in the same repository, with the same file or no file on either side: same.
refinekeeps the recorded file and line when the new step names no file. - Anything else is a jump:
currentgoes ontoback,forwardis cleared, and the new place becomescurrent.backholds at mostMAX_ENTRIES(50) places.
stateDiagram-v2
[*] --> Empty
Empty --> HasCurrent: record
HasCurrent --> HasCurrent: record, same spot, current updated
HasCurrent --> HasBack: record, a jump, old current pushed to back
HasBack --> HasBack: record, a jump, forward cleared
HasBack --> HasForward: goBack, current pushed to forward
HasBack --> HasBack: goBack, a gone stop is dropped, next tried
HasForward --> HasBack: goForward
HasForward --> HasBack: record, a jump, forward cleared
HasBack --> Empty: clear on a new workspace
HasForward --> Empty: clear on a new workspace
Places are recorded by the file editor, fileEditor.svelte.ts (selection or text changes, and when a file opens), ChangesView.svelte (a click in Changes, kind: "diff", line 0) and editor/blame.ts (blame clicks).
The history is not a list of recent files: Recent Files in Search Everywhere, Quick Open and the Cmd+E popup come from their own list, kept per workspace (see How Recent Files Works). Going back reorders the history and a restart empties it, which made it a poor source for that. A search result opens through navigation.openFileAt(filePath, line, column): it sets a reveal request with a 0-based line and column and focus: true, then opens the file. FileView applies it, focuses the editor, and records the new place as usual.
A blame click records two steps: the clicked line, built by the blame state's origin(line) callback (gutter clicks may not be on the cursor line), and the Log step.
sequenceDiagram
participant B as blame.ts
participant N as navigation store
participant H as NavigationHistory
participant RS as repoStore
participant LV as LogView
B->>N: openCommit(target, origin(line))
N->>H: record(from), the clicked line
N->>H: record(kind log, commit)
N->>RS: showCommit(repoRoot, commitId, filePath, line, lineText)
RS->>LV: logFocus with a new token, view log
LV->>LV: load up to 20 pages, select, open file diff
goBack() calls history.travel("back", go). go(target) returns a StopOutcome: "shown", "cancelled" or "gone".
-
file: checks the file exists (open tabs are trusted; other files are read once, and
isMissingFileErrorspots "not found"), setsrevealwith a token and callsrepoStore.openFile.FileViewcallstakeReveal(filePath)when ready; a request withline: nullonly focuses. -
diff: checks with
buildSectionsandfindFilethat the change still exists, setsdiffReveal, thenchangesSelection.pick.ChangesDiff.sveltecallstakeDiffReveal. -
log: gone when the repository left the workspace, else
repoStore.showCommit(...).
A cancelled step leaves the history as it was. A gone stop is dropped (for a file, forget drops all its stops) and the next one tried; if none is left, a toast says "Nothing left to go back to".
The triggers are the arrow buttons in Header.svelte, Ctrl+- and Ctrl+Shift+- (workspaceShortcut), and mouse buttons 3 and 4. A new workspace calls navigation.clear().
| File | What it does |
|---|---|
src/lib/stores/navHistory.ts |
Pure history: sameSpot, refine, record, travel, forget, isMissingFileError
|
src/lib/stores/navigation.svelte.ts |
Reactive store: openCommit, go, reveal requests |
src/lib/views/Header.svelte |
Back and Forward buttons |
src/lib/views/workspaceLife.svelte.ts |
Keys and mouse side buttons |
src/lib/views/files/fileEditor.svelte.ts, watchFile.svelte.ts
|
Records file places, applies takeReveal, calls forget when its file is gone |
src/lib/views/ChangesView.svelte |
Records diff places |
src/lib/views/changes/ChangesDiff.svelte |
Applies takeDiffReveal
|
src/lib/editor/blame.ts |
Blame clicks through navigation.openCommit
|
Pure history, thin store. All rules live in NavigationHistory, each with a unit test; the store only turns places into UI actions.
Small moves update, big moves record. Recording every cursor move would fill the 50 entries in seconds.
Reveal requests with tokens. The editor may not exist yet when Back opens a file, so the view takes the request when ready; the token lets repeat jumps fire.
Undo a cancelled step, skip a gone one. The history never points somewhere you are not, or somewhere that no longer exists.
Back and Forward were hard to find.
- The issue: the user asked for Back and Forward after they were built.
- Why it happened: the buttons sat in the file editor's title row.
- The fix and why we chose it: they moved to the top bar, always visible.
Back did not return to the clicked blame line.
- The issue: after clicking blame to reach the Log, Back skipped the clicked line.
- Why it happened: only file places were history steps.
-
The fix and why we chose it:
LogLocationwas added, and blame records the clicked line first. Changes diffs became steps too (DiffLocation).
Two Log entries for one commit.
- The issue: a Log step with no file no longer merged with a later click on the same commit that named a file.
-
Why it happened: the new rule compared
filePathstrictly. - The fix and why we chose it: a missing file on either side counts as the same commit.
Long branch names covered the buttons.
- The issue: long branch names overlapped the Back and Forward buttons.
- Why it happened: the buttons sat after the branch name, and the header's left side could not shrink.
- The fix and why we chose it: they moved to the far left, and long names now shorten with an ellipsis.
Back went to files that no longer exist.
- The issue: after deleting a file or committing a change, Back and Forward still stopped there, again and again.
-
Why it happened:
forgetexisted but nothing called it, and a failed step was undone with the dead stop still in the history. -
The fix and why we chose it: the file tab calls
navigation.forgetwhen its file is gone, andNavigationHistory.traveldrops a gone stop (with every other stop of a missing file) and tries the next at once, like a browser. Only a cancelled step leaves the history as it was.
A Log step lost its file.
- The issue: a later step for the same commit with no file replaced a Log step and forgot its file and line, and the test meant to catch it checked another case.
-
Why it happened:
recordreplaced the current entry wheneversameSpotmatched, and the test recorded a different file instead of no file. -
The fix and why we chose it:
refinekeeps the existing entry when the new one names no file, and a new test covers the real case.
src/lib/stores/navHistory.test.ts covers small moves versus jumps, back and forward, the cap and forget, Log and diff steps, refusing pseudo tabs, travel (skipping gone stops, cancelling) and isMissingFileError. Any new location kind or rule needs a case here first.
- Update this page when
navHistory.ts,navigation.svelte.tsor the views that record places change. - Update Navigation and Keyboard Shortcuts when keys or buttons change.
- Retake
back-forward.pngwhen the header buttons move. - Related: How Blame Works, How the Log Works.
These pages are generated from docs/wiki. To correct something, open a pull request against the source file; edits made here are overwritten.
Using Nibtree
- Getting Started
- Welcome Screen
- Workspaces
- Files Panel
- File Operations
- File Icons
- Rounded Panels
- Editor and Tabs
- Pin Reorder and Wrap Tabs
- Single Tab Title
- Split Editor
- New File and Unsaved Changes
- Unload Hidden Tabs
- Editing Code
- Code Appearance
- Markdown Editor
- Image and PDF Preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and Replace
- Changes and Commits
- Sign In Prompts
- Commit Box Layout
- Repository Actions
- Commit Options
- Ignoring Files
- Diffs
- Inline Diffs
- Staging View
- Revert a Change
- Resolving Conflicts
- Merge Tool
- Blame
- History and Log
- Merge Commits
- Branches and Tags
- Branches Popup
- Remotes
- Stashes
- Shelf
- Git Menu
- Git Dialogs
- Interactive Rebase
- Worktrees
- AI Agents
- AI Tasks
- AI Usage
- Keep Computer Awake
- Submodules
- Git LFS
- GitHub
- Git Console
- Terminal
- Terminal Features
- Scripts
- Navigation
- Menus
- Settings
- Settings Search
- Color Themes
- Settings Terminal and Automation
- Settings Files
- Updates
- Status Bar and Help
- Notifications
- Memory Use
- Clear Cache
- MCP and CLI
- Git Mergetool
- Keyboard Shortcuts
- Keyboard Shortcuts Editor
- Troubleshooting
- Troubleshooting Git
Developers
- Developer Guide
- Architecture
- Project Layout
- Backend
- How Folder Watching Works
- Folder Watching Bugs We Fixed
- How Workspace Files Work
- How the Preview Scheme Works
- Backend Services
- How Terminal Features Work
- Terminal Bugs We Fixed
- Frontend
- How the Rich Markdown Editor Works
- Rich Markdown Bugs We Fixed
- Git Menu Links
- Menu Keys and Routing
- Frontend Modules
- Blame Bugs We Fixed
- Project Color And Title Bar
- One Diff View
- Diff Bugs We Fixed
- Merge Tool Layouts
- Merge Tool Bugs We Fixed
- Color Theme Bugs We Fixed
- How the Connected Diff Works
- Rounded Panels Bugs We Fixed
- Update Bugs We Fixed
- Status Bar Bugs We Fixed
- MCP and CLI Bugs We Fixed
- Repository Actions Bugs We Fixed
- How the Changes Tab Works
- CodeMirror Patterns
- How the Cursor Works
- How Editor Features Work
- How the Path Bar Works
- Commands and Events
- Settings Reference
- Commands Workspace and Changes
- Commands Branches and Remotes
- Commands History and Extras
- Commands App and Tools
- Testing
- Test Suites
- Debugging
- Debugging with MCP
- Measuring Setting Memory
- How Terminal Memory Is Measured
- How Markdown Diagrams Work
- Releases and CI
- Publishing the Wiki
- Versioning and Changelog
- Browser Version Packages
- Browser Release Checks
- Docker Image
- Platforms and Signing
- Windows Support
- Windows Bugs We Fixed
- Windows MCP and CLI
- Docs and Screenshots
- UI Library
- UI Library MCP
- UI Library Bugs
- UI Library Migration
- Contributing
How features work
- Welcome screen
- Workspaces and folders
- Files panel
- File operations
- File icons
- Editor and tabs
- Pin, reorder and wrap tabs
- Single tab title
- Split editor
- New File and unsaved changes
- Unload hidden tabs
- Editing code
- Code appearance
- Markdown editor
- Image and PDF preview
- Search Everywhere
- Recent Files
- Navigation Bar
- Find and replace
- Changes and commits
- Sign-in prompts
- Commit box layout
- Repository actions
- Commit options
- Ignoring files
- Diffs
- Inline diffs
- Staging view
- Revert a change
- Resolving conflicts
- Merge tool
- Blame
- History and log
- Merge commits
- Commit tabs
- Branches and tags
- Branches popup
- Remotes
- Stashes
- Shelf
- Git menu
- Git dialogs
- Interactive rebase
- Worktrees
- AI agents
- AI usage
- Keep computer awake
- AI tasks
- Submodules
- Git LFS
- GitHub account
- Git Console
- Back and Forward
- Integrated terminal
- Scripts tool window
- Menu bar
- Settings
- Search in Settings
- Color themes
- Rounded panels
- Updates and release channels
- Status bar and help links
- Notifications
- Memory readout and memory log
- Clear Cache
- MCP server and command line tool
- git mergetool mode