Repository navigation
How the Editor Works
Every file you open gets a tab with a CodeMirror 6 editor, with change markers against the last commit, inline conflict actions, blame, and next or previous change navigation. The user side is in Editor and Tabs. Commands, keys and the look of the text are in How editing code works, the bar above the code in How the path bar works, and the Markdown preview in How the Markdown Editor Works.
Fixing a conflict or a typo should not mean switching editors. People expect editor tabs where a single click previews, a double click or an edit keeps the tab, and every tab remembers its cursor, undo history and unsaved text. CodeMirror 6 is small, renders only visible lines and loads languages on demand.
The tab rules are pure functions in src/lib/stores/tabs.ts; repoStore holds tabs and openFilePath (absolute paths) and applies each result with applyTabs.
stateDiagram-v2
[*] --> Preview: single click (openTab, pin false)
[*] --> Pinned: double click or Open
Preview --> Replaced: single click on another file
Preview --> Pinned: edit, double click tab, Keep Open
Replaced --> [*]
Pinned --> Pinned: switch tabs, state kept
Preview --> Closed: close
Pinned --> Closed: close, confirm if unsaved
Closed --> [*]
openTab reuses the preview tab only when it has no unsaved edits. setTabDirty pins a preview tab when you type. closeTabs moves focus to the right neighbour, else the left, and repoStore.closeTabs asks before discarding unsaved work. tabLabels adds the folder name when two tabs share a file name, and adjacentTab serves Next Tab and Previous Tab.
A tab can also hold a commit, a terminal, a Git history or compare view, a shelved file, a branch comparison or a branch against the working tree. Each has a pseudo path that never starts with /, checked by isPseudoTab (stores/pseudoTabs.ts). See How commit tabs work and How the terminal works.
EditorTabs.svelte draws the strip: the Diff tab (while a change is selected), then the tabs. EditorGroupContent.svelte renders one FileView per file tab and hides the inactive ones (unused ones sleep).
Right under the strip, each FileView draws one slim bar: the breadcrumbs and badges, then icon buttons for the change arrows, Blame, Copy relative path and the Markdown view switch. How it collapses when narrow, and why it replaced two rows, is in How the path bar works.
sequenceDiagram
participant View as FileView.svelte
participant Api as api.ts
participant Rust as Tauri commands
View->>Api: readWorktreeFile(folder root, relative path)
Api->>Rust: read_worktree_file
Rust-->>View: content, eol, binary, tooLarge
par loadHead
View->>Api: getFileDiff(repo, path, origPath, "staged")
Rust-->>View: original = HEAD text
and createEditor
View->>View: languageFor, extensions
View->>Api: blameFile (see How Blame Works)
end
View->>View: changeMarks(HEAD lines, editor lines)
View->>View: dispatch setChangeMarks
read_file flags files over 4 MB (MAX_OPEN_BYTES) and binary content. Line endings become LF, and the original eol is kept for saving. Paths go through the workspace folder holding the file (folderFor), so files outside any repository work too.
The editor is built from baseExtensions in src/lib/editor/setup.ts (the theme, the find bar, the Code menu keys, render whitespace and the current line highlight) plus:
-
conflictMarkersfor the inline conflict actions (see How conflict resolution works), -
changeMarkField,changeGutterandscrollMarkersfor the colored gutter bars and the overview ruler beside the scrollbar, -
blameExtensionfor blame (see How blame works).
The backend's line_change_marks computes the marks 200 ms after you stop typing (FileGitInfo); markSource lets conflicts win over plain changes. A click on a bar reverts it (How reverting a change works). Previous and Next (Shift+F7 and F7) use sectionTarget from navigation.ts, which wraps around.
There are no Save or Revert buttons. Each FileView registers save and revert with fileCommands (stores/fileCommands.svelte.ts), keyed by its path, and the File menu handlers in menuActions.ts call the active tab's entry: Save (Cmd+S, also in the editor's keymap), Save All (Option+Cmd+S, every dirty tab, one toast) and Revert File, which asks first when there are unsaved edits.
Saving writes through writeWorktreeFile with the remembered eol and refreshes the repository's status. A tab without unsaved edits reloads when that status changes.
| File | What it does |
|---|---|
src/lib/views/files/FileView.svelte |
One editor tab |
src/lib/views/files/fileEditor.svelte.ts |
The CodeMirror editor |
src/lib/views/files/fileSave.svelte.ts |
Save, auto save |
src/lib/views/EditorTabs.svelte, editorTabMenu.ts
|
The tab strip and its menu |
src/lib/views/EmptyMain.svelte |
The empty editor area with the Navigation Bar; buttons reuse openQuickOpen, openFileSearch, openNavigationBar
|
src/lib/views/workspaceShortcuts.ts |
Which window shortcut a key means |
src/lib/stores/tabs.ts |
Pure tab rules |
src/lib/stores/pseudoTabs.ts |
isPseudoTab for tabs that are not files |
src/lib/stores/fileCommands.svelte.ts |
Save and Revert of each open editor, for the menus |
src/lib/stores/repo.svelte.ts |
openFile, pinFile, setDirty, closeTabs
|
src/lib/editor/setup.ts |
Theme, baseExtensions (with the editor features) |
src/lib/editor/languages.ts |
Language names and lazy languageFor
|
src/lib/editor/activeLine.ts |
The current line highlight |
src/lib/editor/lineDiff.ts |
diffLines and changeMarks
|
src/lib/editor/scrollMarkers.ts, navigation.ts
|
Ruler, gutter, next and previous section |
src-tauri/src/git/files.rs |
read_file |
Keep one live editor per tab. Undo history and cursor survive tab switches. The cost is memory per tab; closing a tab destroys its view.
Diff against HEAD in the UI. The committed text is fetched once and a small line diff runs as you type, instead of asking git on every keystroke.
Save and Revert in the File menu. The native menu bar has Save, Save All and Revert File, like other Mac editors, so the path bar keeps only the actions that live nowhere else.
Word wrap only in the file editor. In diffs and the merge tool, wrapping would misalign the panes. editor/wordWrap.ts keeps lineWrapping in a compartment, so View > Word Wrap and Option+Z (toggleWordWrap, matched by event.code) reach open editors at once.
A selection inside one line was invisible.
- The issue: selecting text inside one line showed no selection at all.
- Why it happened: CodeMirror draws the selection behind the text, and the opaque current line background on the cursor's line covered it.
-
The fix and why we chose it:
activeLine.tshighlights the current line only while nothing is selected, tested inactiveLine.test.ts. It was found with the MCPinspect_elementstool in the real app.
The toolbar and breadcrumb bugs moved with the bar to How the path bar works.
The Diff tab could not be closed.
- The issue: the Diff tab always showed a file and had no close button.
-
Why it happened: it was a fixed tab, and
changesSelection.syncpicked the first changed file on its own. -
The fix and why we chose it: the tab shows only while a change is selected, with a close button, and
syncwaits for the user to pick a file.
Find previous also opened Changes.
- The issue: Shift+Cmd+G in the editor (find previous) also showed Changes, and Shift+Cmd+L also toggled the Log.
-
Why it happened: the window handler in
Workspace.svelteran too, without checking whether the editor had handled the key. -
The fix and why we chose it: the pure
workspaceShortcutskips a key whenevent.defaultPreventedis set, like every window key handler, so inside an editor the editor's action wins.
-
src/lib/stores/tabs.test.ts: preview replacement, pinning, dirty tabs, closing, labels andadjacentTab. -
src/lib/editor/lineDiff.test.ts,navigation.test.ts,activeLine.test.tsandlanguageName.test.ts. -
src/lib/views/workspaceShortcuts.test.ts: the window shortcuts and the keys they skip. -
src-tauri/src/commands/tests.rs:read_worktree_file_normalizes_crlf_and_detects_binary,write_worktree_file_applies_eolandworktree_file_commands_work_in_a_plain_folder.
Put new rules in the pure modules, with tests. The path bar needs a visual check at narrow widths (How the path bar works). See Testing.
- Update this page and Editor and Tabs when tabs, shortcuts, extensions, markers or saving change. Dragging, pinning and wrapping tabs are in How tabs are arranged.
- A new pseudo tab kind goes into
isPseudoTaband the table in How commit tabs work. - Retake
editor-tabs.png,editor-change-markers.pngandempty-main.png. See Docs and Screenshots.
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