Repository navigation
How Commit Tabs Work
A commit from the Log can open in its own editor tab, so its diff gets the whole editor area instead of the small pane under the list. For the user side, see History and Log.
The details pane under the Log is good for a quick look, but it shares the height with the commit list. A big change is hard to read there. People expect to open a commit's changes as an editor tab: double-click, read it full size, close the tab when done, keep the Log where it was.
A commit tab lives in the same tab strip as file tabs, so it gets every tab behavior for free: it sits next to files, closes with the tab menus, and is reused when you open the same commit again. The tab rules in stores/tabs.ts only know a tab by its path, so a commit tab gets a pseudo path from commitTabPath(repoRoot, commitId), such as commit:0f32e433...@/work/acme/storefront. It never starts with /, so no folder or repository lookup can mistake it for a file.
sequenceDiagram
participant LV as LogView
participant RS as repoStore
participant WS as Workspace.svelte
participant CT as CommitTab.svelte
participant CD as CommitDetails.svelte
LV->>RS: openCommitTab(repoRoot, commitId, { summary, filePath })
RS->>RS: commitTabs[path] = summary and focus
RS->>RS: applyTabs(openTab(path, pinned))
WS->>CT: tab path starts with "commit:"
CT->>CD: repoPath, commitId, preferredFile
CD->>CD: getCommitDetails, getCommitFileDiff
The Log calls openCommitTab from four places: a double-click on a commit row, Enter in the list, Open in Tab in the details pane (and its right-click menu), and a double-click on a changed file. CommitDetails shows the button and the file double-click only when it gets an onOpenInTab callback, so inside the tab itself they are hidden.
EditorGroupContent.svelte renders CommitTab instead of FileView for a commit tab path. CommitTab parses the path back with parseCommitTabPath and renders the same CommitDetails the Log uses, at full size. A Parent link opens that commit in another tab. Because the tab can show any commit, not only loaded ones, every parent is clickable.
What the path cannot hold lives in repoStore.commitTabs: the subject for the tooltip, and focus, the file to show. focus carries a token that changes on every request, so asking for the same file again still switches to it. applyTabs drops the entry when its tab closes.
Code that treats a tab path as a file asks isPseudoTab (stores/pseudoTabs.ts), which knows every kind below, so a new kind is added in one place:
-
editorTabInfo.tsandeditorTabMenu.ts: icon, label (tabLabels), tooltip and menu per kind; a commit tab has Copy Commit Hash. -
StatusBar.svelteand the Files panel (FileExplorer.svelte): cursor items and file selection only for real files. -
navHistory.ts:recordrefuses pseudo tabs, and Recent Files skips them. -
repoStore.removeFolder:tabsInFoldercloses the folder's files and its repositories' commit, Git and branch tabs. Terminal tabs stay.
Later features reuse the idea. Each kind has a prefix and a pure module that builds and parses its path (parts URI-encoded, joined by |), and opens pinned through repoStore.openPseudoTab.
| Path | Shows | Opened from | View |
|---|---|---|---|
commit:<id>@<repo> |
A commit | The Log | log/CommitTab.svelte |
git-fileHistory:<repo>|<file> |
A file's history | Git > Current File > Show History | views/git/FileHistoryTab.svelte |
git-lineHistory:<repo>|<file>|<from>-<to> |
The history of some lines | Show History for Selection | views/git/LineHistoryTab.svelte |
git-compare:<repo>|<file>|<revision> |
A file against a revision or branch | Compare with Revision or Branch, a history row | views/git/CompareTab.svelte |
git-shelf:<repo>|<file>|<shelf> |
A file of a shelved change | Shelf > Show Diff | shelf/ShelfDiffTab.svelte |
branches-compare:<repo>|<branch>|<base> |
Two branches compared | Branches popup > Compare with 'current branch' | views/git/BranchCompareTab.svelte |
branches-worktree:<repo>|<revision> |
A branch against the working tree | Branches popup > Show Diff with Working Tree | views/git/WorktreeDiffTab.svelte |
terminal:<key> |
A terminal | Move Terminal into Editor Area, New Terminal in Editor Area | terminal/TerminalSlot.svelte |
The Git kinds live in stores/gitTabs.ts and render through views/git/GitTab.svelte, the branch kinds in stores/branchTabs.ts and views/git/BranchTab.svelte. A terminal tab's path holds only the terminal's key (terminal/terminalTabs.ts). Its TerminalSlot stays empty: the terminal store moves the live xterm element into it, so the shell keeps running between the panel and the editor. Closing the tab reaches the terminal store through repoStore.onTabsClosed and stops the shell. See How the terminal works.
| File | What it does |
|---|---|
src/lib/stores/commitTabs.ts |
Pseudo paths: commitTabPath, parseCommitTabPath, isCommitTab, commitTabsInFolder
|
src/lib/stores/pseudoTabs.ts |
isPseudoTab: every kind of tab that is not a file |
src/lib/stores/repo.svelte.ts |
openCommitTab, openPseudoTab, commitTabs, onTabsClosed
|
src/lib/stores/tabs.ts |
tabLabels for every kind, tabsInFolder
|
src/lib/log/CommitTab.svelte |
The tab's content: CommitDetails at full size |
src/lib/log/CommitDetails.svelte |
Open in Tab and the file double-click (onOpenInTab) |
src/lib/views/LogView.svelte |
Double-click, Enter and the menu item call openInTab
|
src/lib/views/EditorGroupContent.svelte |
Picks the view for each tab |
src/lib/views/editorTabInfo.ts, editorTabMenu.ts
|
Icon, label, tooltip and menu for each kind |
A pseudo path instead of a second kind of tab. Opening, preview rules, closing, Close Others and the scroll-into-view all work on paths; a path that can never be a file reused them all. The rejected alternative was a separate list of commit tabs with its own strip logic. Seven more kinds joined later without changing the tab rules.
One isPseudoTab check. Places that need a real file ask one helper, so a new kind cannot be forgotten in one of them.
Reuse CommitDetails. The tab and the Log pane show the same thing, so they share one component. A fix to one is a fix to both.
Commit tabs open pinned. A double-click or a button is a deliberate open, like a double-click in the Files panel, so the tab never replaces itself the way a preview tab does.
No Back and Forward steps for pseudo tabs. History stops are files, diffs and Log positions. A pseudo tab is never recorded (record refuses it), because a stop must be checkable when you go back to it, and that needs its own rule in navHistory.ts.
None are recorded yet. When you fix one, add it here with the issue, why it happened, and the fix and why you chose it.
-
src/lib/stores/commitTabs.test.ts: pseudo paths round-trip, files are never mistaken for commits,commitTabsInFolder, and the short hash label. -
src/lib/stores/gitTabs.test.ts,branchTabs.test.tsandsrc/lib/terminal/terminalTabs.test.ts: the other kinds round-trip and reject broken paths; Git and branch tabs close with their folder, terminal tabs stay. -
src/lib/stores/navHistory.test.ts: "never records a terminal or commit tab as a stop". - The tab views are Svelte glue; check them by hand: double-click a commit, Enter, Open in Tab, a file double-click, a parent link, and closing.
- Changing how a commit tab opens, looks or closes: update this page, History and Log, Keyboard Shortcuts and
commit-tab.png. - Adding Back and Forward steps for pseudo tabs: update the decision above and How navigation works.
- New code that walks
repoStore.tabsmust handle pseudo tabs (isPseudoTab). - A new kind: a prefix that never starts with
/, thenisPseudoTab,tabLabels,editorTabInfo.ts,EditorGroupContent.svelte,tabsInFolderif it belongs to a repository, and a row in the table above.
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