Skip to content

How the Path Bar Works

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

Every file tab has one slim bar between the tab strip and the code: the path and badges on the left, small icon buttons on the right. This page explains how it is built and how it collapses when the editor is narrow. The user side is in Editor and Tabs; the rest of the editor is in How the editor works.

Why we need it

The editor used to draw two rows above the code: a title row with the breadcrumbs, the badges and a copy button, and an actions row with the change arrows, the counter and a Blame button with a text label. Both rows wrapped, so together they took about 90 px, more in a narrow editor. On a laptop screen that is several lines of code lost on every tab.

One line can show the same information: the path, then a few icon buttons at the right edge. The user asked for that look, so the code starts right under the tabs.

How it works

flowchart TD
  Tabs["EditorTabs.svelte: the tab strip"] --> Bar
  subgraph Bar["File actions toolbar (29 px, measures its width)"]
    direction LR
    Crumbs["NavigationBar: folders and file"] --> Badges["badges: Unsaved, Modified, 2 conflicts"]
    Badges --> Actions["arrows, Blame, Copy path, view switch"]
  end
  Bar --> Conflict["conflict strip: only while the file has conflicts"]
  Conflict --> Format["MarkdownToolbar: only for Markdown files"]
  Format --> Code["CodeMirror editor"]
Loading

FileBar.svelte draws the bar with the UI library's Toolbar (variant file, toolbar File actions), 28 px plus a 1 px border. It holds three parts:

  • The crumbs (NavigationBar.svelte, drawn by the library's Breadcrumbs): the workspace folder, then each folder down to the file, built by crumbsFor. Repository roots get the folder-git icon and the file its type icon. The full absolute path is the tooltip. Each crumb is a button that opens the folder's popup; see How the Navigation Bar works.
  • Badges: library Pills for Unsaved, the git state (Modified, New file, Conflicted) and the number of conflict blocks. Each has a title with its text.
  • Buttons: previous and next section (Shift+F7 and F7) with the counter, a divider, Blame (a ToggleButton named Blame with aria-pressed), Copy relative path, and for Markdown files the view switch, a small Segmented (a radiogroup named Markdown view) with Editor Only, Editor and Preview, and Rich Editor.

All buttons are 24 by 22 px, with a tooltip and an accessible name. The keyboard shortcuts did not change: F7 and Shift+F7 live in the editor keymap, and the view modes are also in View > Markdown.

Settings > Appearance > File toolbar places the bar and switches its parts. fileToolbarParts in views/files/fileToolbar.ts (pure, tested) turns the placement, the seven fileToolbar* switches and the file (in a repository and editable, Markdown) into what to draw: Hidden or no part left drops the bar, the git buttons need a repository, and the Markdown formatting row follows its own switch whatever the placement. With Bottom, FileView.svelte draws the same bar after the code, with placement="bottom" so its line is on top. Dividers only show between parts that are on. See How the Navigation Bar works.

Below the bar come only rows that are tools, and only when they apply:

  • The conflict strip (a Toolbar of variant conflict, named Conflict actions) with Accept All Current, Accept All Incoming, Resolve in Merge Tool and Mark as Resolved. See How conflict resolution works.
  • The Markdown formatting row (MarkdownToolbar.svelte, toolbar Markdown), 28 px high. See How the Markdown editor works.
  • For images and PDFs, the zoom and Reveal row of MediaPreview.svelte, made just as low.

Collapsing when narrow

The bar never wraps and never overflows. The Toolbar is responsive: it measures its own width, the editor's, not the window's, and the parts inside step aside with hideAt and dotAt:

Bar width What changes
Under 600 px The counter (2 of 5) is hidden visually (hideAt="medium"). Screen readers still read it.
Under 480 px Badges become 8 px colored dots (dotAt="compact"); the text stays for screen readers and the tooltip.
Under 320 px The dividers between the buttons go (hideAt="narrow").

The breadcrumbs shorten all the time, as needed. The crumbs may shrink, the badges and buttons may not. Each crumb gets a flex-shrink from crumbShrink (logic/crumbs.ts in the UI library): 10 to the power of its distance from the file, so the outermost folder gives up its width first, then the next one. Each folder keeps room for an ellipsis (min-width: 1.4em, plus the icon when it has one), and the file name shrinks last. The crumbs are aligned to their end, so whatever still does not fit is cut on the left, never the file name on the right.

Where the code lives

File What it does
src/lib/views/files/FileBar.svelte The bar, the badges, the actions and the view switch
src/lib/navBar/NavigationBar.svelte The crumbs and the folder lists
src/lib/views/files/MarkdownToolbar.svelte The Markdown formatting row
src/lib/views/files/MediaPreview.svelte The image and PDF row
scripts/screenshots.ts editor-conflict-toolbar clips the File actions and Conflict actions toolbars; markdown-toolbar clips the formatting row, the view switch and the Heading menu

Design decisions

One bar. Text labels became icon buttons with tooltips, and the path shortens instead of wrapping. The code gains about 60 px on every tab, and the bar looks the same in every file.

Tools keep their own row. Conflict buttons and Markdown formatting are things you use, not information, and their text labels or many buttons would crowd the bar. They show only when they apply, and the conflict strip is tinted like an editor notification, so it reads as a temporary state.

The view switch moved into the bar. It changes how the whole tab looks, like a Markdown editor's switch at the top right, and it keeps the formatting row free for formatting.

Collapse in a fixed order. The counter is the least needed part (the change ticks beside the scrollbar show the same), so it goes first. Badges keep their color as dots. The file name and the buttons stay to the end, because they are what you came for.

The bar's own width, not the window's. The editor's width depends on the sidebars and the Files panel, so only the bar's own width tells what fits.

Bugs we fixed

Toolbar buttons overlapped, and the path got hidden.

  • The issue: with many buttons and the Files panel open, the buttons overlapped, and part of the path disappeared.
  • Why it happened: all actions shared one fixed row, and the crumbs scrolled sideways on one line.
  • The fix and why we chose it: at first both rows wrapped, since the user wanted every option visible. That cost up to 90 px above the code, so the slim bar replaced them: every option is still there as an icon button, and the path shortens from the left instead of hiding.

Tests

The bar is layout; crumbs.test.ts covers crumbShrink, the crumbs come from navBarModel.ts, and fileToolbar.test.ts covers which parts show. It needs a visual check in light and dark, and at narrow editor widths (drag the Files panel wider): the counter hides first, then the badges become dots, and the file name stays readable. See Testing.

Keeping this page in sync

  • Update this page, Editor and Tabs and How the editor works when a button, a badge or a breakpoint changes.
  • Keep the editor-conflict-toolbar and markdown-toolbar selectors in scripts/screenshots.ts working.
  • Retake editor-tabs.png, editor-change-markers.png, editor-conflict-toolbar.png and the markdown-*.png screenshots. See Docs and Screenshots.

Home

Using Nibtree

Developers

How features work

Clone this wiki locally