Repository navigation
How the Inline Diff Works
This chapter explains the inline layout of the diff view: one column with each change's removed lines above the lines that replace them, and two line number columns. The user side is in Inline Diffs; the diff view itself (side by side, hunk staging, the split) is in How Diffs Work.
Two columns halve the width of each version. In a narrow window, or with long lines, a lot of the change scrolls out of sight. One column gives the text the whole width and still shows what was removed. It was asked for in the diff toolbar, next to Open File, and in Settings > Git.
The layout is the diffLayout preference in settings.json: "sideBySide" (the default) or "inline", read with pickOneOf against DIFF_LAYOUT_CHOICES in settingsData.ts, so any other value falls back to side by side. Two places set it with settings.setPreference("diffLayout", ...), so they always agree:
- Diff layout in Settings > Git, a segmented control like File toolbar, found by Settings search under words such as "unified" and "split".
- Two buttons in a
role="group"in the diff toolbar (DiffToolbar.svelte), with thesplit-viewandsplit-rowsicons. They are disabled when the diff has no text to show (binary, too large, LFS or identical). The build effect reads the setting, so a click tears the old view down and builds the other one; the layout is part of the scroll key.
buildInlineDoc in inlineDoc.ts walks the backend's line hunks and writes one text: unchanged lines once, then for each hunk its old lines followed by its new lines. Every line keeps its kind and its 0-based number on each side, and newToDoc maps new lines back. Removed lines are ordinary text, so selecting, copying and Cmd+F work on them with no extra code.
flowchart LR
FD["FileDiff<br/>original, modified, hunks"] --> B["buildInlineDoc"]
B --> Doc["InlineDoc<br/>text, lines (kind, old, new), blocks"]
Doc --> V["inlineDiffExtensions<br/>(inlineView.ts)"]
Doc --> Sel["inlineSelection"]
Doc --> Apply["applyBlock"]
Doc --> Map["docLineOf, newLineAt"]
Hunks that do not fit the texts (hunkRanges returns null), or a payload without them, are found again with fallbackHunks, which runs CodeMirror's Chunk.build and turns the chunks into line hunks.
inlineDiffExtensions adds to the read-only baseExtensions (built with lineNumbers: false):
- line classes by each line's own kind (removed
--diff-removedsoft red, added--diff-addedgreen) and changed-word marks frominlineWordMarks(--diff-removed-word,--diff-added-word), which diffs each modified block's old lines against its new lines, - two number gutters, old then new, that tint changed rows like the code,
- a gutter with a Stage (+) or Unstage (-) button on the first line of each change,
- a sign gutter right before the text:
lineSigngives-for a removed line and+for an added one, in--dangerred and--successgreen, likegit diff. Markers are shared per sign and kind, - folded unchanged runs from
collapsedRuns(3 lines of margin, at least 4 folded), drawn byinlineFoldsfromfoldField.ts, the same folds as the side by side layout. They are block widgets in aStateField, as block decorations must be. A run opens 10 lines at a time from either edge, fully when its label is clicked, and fully when the cursor lands in it, so a Find match or a reveal is never hidden.
Everything outside the editor speaks in new-text lines or old and new lines, so DiffView maps at the edges:
-
Reveal (Back and Forward) turns a new line into a document line with
docLineOf; Open File goes the other way withnewLineAt(a removed line counts as the line after it). -
Line actions:
inlineSelectionturns the selected document lines into exactly the removed and added lines they cover. The backend'spartial_textaccepts any mix of old and new lines, so a selection of only new lines adds them and keeps the old ones. -
Chunk buttons:
applyBlockbuilds the index's new full text (stage: the old text with the hunk's new lines; unstage: the new text with its old lines back) and hands it toonChange, the same path as the side by side buttons. -
Blame:
loadBlameblames the new text, thenexpandBlamespreads it over the document. Removed lines getREMOVED_LINE, which the gutter and the inline note skip. -
Navigation and the ruler use the blocks:
revealChangegoes to a block's first line,inlineChangeMarksgives one tick per block.
| File | What it does |
|---|---|
src/lib/diff/inlineDoc.ts |
The document, word marks, selection, applyBlock, folds, line mapping |
src/lib/diff/inlineView.ts |
inlineDiffExtensions: colors, gutters, signs, buttons, folds |
ui/web/src/lib/editor/inlineDiffTheme.ts |
The inline diff's look (the library) |
src/lib/diff/foldField.ts, foldModel.ts
|
The folds and their 10 line steps, shared with side by side |
ui/web/src/lib/editor/foldRow.ts |
The fold row the folds draw: its buttons, label and wave |
src/lib/diff/DiffToolbar.svelte, diffViews.ts
|
The toolbar buttons and building either layout |
src/lib/editor/blame.ts, blameModel.ts
|
loadBlame for an inline source, expandBlame, REMOVED_LINE
|
src/lib/editor/setup.ts |
The lineNumbers option of baseExtensions
|
src/lib/stores/settingsData.ts, settings.svelte.ts
|
The diffLayout preference and DIFF_LAYOUT_CHOICES
|
src/lib/views/settings/GitSettings.svelte, settings/settingsSearch.ts
|
The Diff layout row and its search words |
Our own document, not CodeMirror's unified view. unifiedMergeView from @codemirror/merge draws removed lines as widgets. Widgets have no line numbers, cannot be selected and are not searched, which is why the first version had all three limits. Putting both versions into the text fixes them at once and keeps one source of line facts for every feature.
Red and green per line. One column puts old and new lines next to each other, so each line shows its own side: soft red removed, green added, even inside a modified block. Side by side keeps its own colors (grey removed, blue modified), where the pane already says which side a line is on. Every color theme derives the red tints from its danger color.
Exact selections. Side by side, a selection on one side also takes the facing lines of the other. Inline, both are on screen, so a selection takes only what it covers.
One preference for all diffs. The layout is a choice people make once, so it is a preference in settings.json, shown in Settings > Git, and applies to every diff and window. The toolbar buttons are a shortcut to the same value.
-
src/lib/diff/inlineDoc.test.ts: document order and numbers, line mapping, new and unchanged files, exact selections, stage and unstage text, the - and + signs, word marks only in modified blocks, ruler ticks, folds with margins, the fallback hunks. -
src/lib/editor/blameModel.test.ts:expandBlameblanks removed lines and records new lines for Back and Forward. -
src/lib/stores/settingsData.test.ts: thediffLayoutpreference defaults to side by side and rejects unknown values. -
src/lib/views/settings/settingsSearch.test.ts: the Diff layout row is in the dialog and its search index. - The look needs a visual check:
diff-inlineinscripts/screenshots.ts.
- Update this page when
inlineDoc.ts,inlineView.tsor the inline parts ofdiffViews.tsandblame.tschange. - Update Inline Diffs and retake
diff-inline.pngfor visible changes. - Related: How Diffs Work, Settings Reference.
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