File Browser & Editor

Braide includes a built-in file browser and code editor so you can inspect and modify files in the session's worktree without switching to an external editor. The browser opens as a docked panel on the right, and opened files are presented as tabs alongside a non-closeable Session tab — so you can flip between the agent conversation and any number of files while keeping everything mounted and live.

Opening the File Browser

Click the folder icon in the session header toolbar (between the reset button and the GitHub panel button) to toggle the file browser panel. The icon is a single filled folder glyph — the same visual used for folders inside the browser tree itself — so the toggle reads as "show folders" at a glance.

When the panel is open, the toggle button is highlighted with the accent colour to make the active state obvious; when closed, it sits muted among the other toolbar buttons and picks up the standard hover treatment.

The panel slides in from the right, to the left of the GitHub panel. Both panels can be open at the same time.

Browsing Files

The tree is rooted at the session's worktree — you can only navigate within it; there is no way to traverse above the worktree root.

Files shown match what git ls-files --cached --others --exclude-standard returns, so you see tracked files plus untracked files that are not covered by .gitignore. Directories that contain only ignored files are hidden.

  • Click a directory to expand or collapse it
  • Click a file to open it in a new editor tab (or focus its existing tab if already open)
  • The refresh button in the panel header re-reads the tree (useful after the agent has created new files)

Resizing the Panel

Drag the vertical handle on the left edge of the file browser panel to resize it. The panel has a minimum width (matching its default size) and a maximum of 50% of the session view's width, so the session content always retains at least half the available space.

Tabs

When one or more files are open, a tab bar appears above the session content area. The bar contains:

  • A Session tab (always first, always present, cannot be closed) that shows the agent conversation, prompt input, and action buttons
  • One tab per open file, labelled by the file's basename, with the full path available as a hover tooltip
  • A small close (✕) button on each file tab, hidden by default and revealed on hover

Opening files

Clicking a file in the browser:

  • Opens a new tab if the file isn't already open, and activates it
  • Focuses the existing tab if the file is already open — files never open twice

Switching tabs

Click any tab to make it the active one. Each file editor is kept mounted while you switch between tabs, so unsaved edits, scroll position, and cursor location are preserved.

Closing tabs

Close a file tab via either:

  • The tab's ✕ button (revealed on hover, turns red on direct hover)
  • The ✕ button in the editor's own toolbar at the top of the editor view

Closing the active tab activates the next-right tab, falling back to the previous tab, and finally to the Session tab if no file tabs remain. Closing a file with unsaved changes triggers the unsaved-changes confirmation.

The Session tab has no close button — the agent session is the root of the view and cannot be dismissed from the tab bar.

Reordering tabs

Drag a file tab horizontally to reorder it. While dragging:

  • The original slot stays in place as a visible placeholder the same size as the tab
  • An opaque ghost of the tab follows the cursor
  • Neighbouring tabs animate smoothly into their new positions as the ghost crosses their centres
  • Releasing the ghost commits the reorder; releasing without crossing a neighbour leaves the order unchanged

The Session tab is fixed at position 0 and cannot be dragged or displaced by a file tab drop.

Overflow

When there are more tabs than can fit, the tab bar scrolls horizontally — tabs keep their natural width and the scroll container ensures none are squashed or ellipsised prematurely. A tab label is capped at 16rem to guard against pathologically long filenames.

Viewing and Editing a File

Switching to a file tab shows the editor for that file. The editor:

  • Uses Monaco (the same engine as VS Code) with syntax highlighting for common languages — TypeScript, JavaScript, JSON, Markdown, Python, Go, Rust, YAML, SQL, and many others. Language is detected from the file extension (or from the filename for Dockerfile and Makefile).
  • Is writable — type to edit, and save changes back to the worktree (see Saving Changes below).
  • Matches the app's light or dark theme, including when the theme is set to "System" and the OS preference changes.
  • Focuses automatically when you activate the tab, so you can start typing or searching (⌘F / Ctrl+F inside the editor) immediately.
  • Does not wrap long lines — use horizontal scroll for content wider than the pane. This keeps source position predictable and keeps the plain and diff views visually consistent.

Editor Toolbar

At the top of each file's editor there is a small toolbar showing:

  • The file's path (relative to the worktree root), preceded by a dirty indicator () when the editor has unsaved changes
  • A diff-view toggle (two-pane icon) that cycles between plain, split, and inline diff views — see Diff View Toggle
  • A save button (floppy-disk icon), disabled when the file has no unsaved changes
  • A close button (✕) — an alternative to the tab's close button, with the same behaviour (including the unsaved-changes guard)
  • Any save error (red text) appears to the left of the save button if a save fails

Because the Session tab and every file tab are mounted simultaneously, any in-progress agent activity, streaming output, and terminal state keeps running while you are viewing or editing a file, and is immediately up to date the moment you flip back to the Session tab.

Diff Status

The editor shows uncommitted changes relative to the last commit (HEAD) as coloured markers in the line gutter, overview ruler, and minimap, mirroring the pattern VS Code uses for its source-control indicators.

As you type — or as the agent writes to files that are open in a tab — the markers update against the HEAD baseline, giving you an at-a-glance sense of which lines differ from the committed version:

  • Green bar in the gutter marks lines that have been added relative to HEAD.
  • Amber bar in the gutter marks lines that have been modified relative to HEAD.
  • Red triangle caret in the gutter marks a block of lines that has been deleted. The caret points down from the top of the line that now sits where the deletion occurred, or up from the bottom of the last line when the deletion is at end of file.

The same colours appear in the overview ruler (the narrow strip on the right edge of the editor, next to the scrollbar) and minimap, so large changes are visible at a glance even in long files.

How the diff is computed

When a file tab opens, the editor fetches both the working-tree contents and the committed version at HEAD (via git show HEAD:<path>). The diff is then computed client-side every time the editor buffer changes, so the markers always reflect the current buffer — including unsaved edits — rather than what is on disk. This gives you a live preview of what git diff will show once you save.

If the file is untracked (not yet added to git) or the session's working directory is not a git repository (for example a scratch session not backed by one), the baseline is treated as empty and every line shows as an addition. That correctly represents the diff you would see once the file is first committed.

For pathologically large diffs (more than ~2.5 million cells in the line-diff table, roughly equivalent to a multi-thousand-line rewrite) the editor falls back to a single bulk "modified" hunk rather than per-region markers, so the editor stays responsive on huge files.

Relationship to unsaved edits

The dirty indicator in the editor toolbar and on the file tab tracks unsaved edits since the last load or save — it clears whenever you save. That is distinct from the gutter diff markers, which track differences against HEAD regardless of whether they are saved yet:

  • Editing a line that was unchanged on disk: a gutter marker appears and the dirty indicator lights up.
  • Saving the file: the indicator clears, but gutter markers remain as long as the saved content still differs from HEAD.
  • Committing the working tree and reopening the file: gutter markers clear, because the baseline now matches.

Note: the HEAD baseline is captured when the tab opens and is not refreshed while the tab is open. If you commit, amend, reset, or check out another branch from a terminal during an editing session, reopen the file to pick up the new baseline — the dirty indicator and the save flow continue to work correctly in the meantime, they just reflect the old baseline in the gutter.

Diff View Toggle

The gutter markers described above are always on while the plain editor is active, but sometimes you want a full diff view of the file against HEAD. The toolbar includes a diff-view toggle (two-pane icon, next to the save button) that cycles the editor between three states:

  • Plain (default) — the normal single-pane editor with gutter status markers. The toggle is shown muted.
  • Split — a side-by-side diff: the HEAD version on the left, the current buffer on the right. The toggle is shown in the accent colour.
  • Inline — a single-pane diff with added and removed lines interleaved, the way git diff presents it. The toggle icon changes to a single framed document.

Clicking the toggle advances to the next state; clicking again from inline returns to plain.

In both diff modes:

  • The right-hand (modified) pane remains editable. Typing, ⌘S / Ctrl+S, the unsaved-changes indicator, and conflict detection all continue to work exactly as in the plain editor.
  • The left-hand (original) pane is the HEAD baseline and is read-only.
  • The gutter status markers are suppressed because the diff editor already colour-codes every change in place.
  • Lines do not wrap in either pane (matching the plain editor). Each pane scrolls horizontally independently as needed, so rows on the left and right stay aligned with their diff partner and the hatched filler Monaco uses for height mismatches does not appear.

The baseline is fetched once when the tab opens (same as for the gutter markers), so the diff always compares against the HEAD snapshot at that point — not against what is currently on disk. Reopen the file to pick up a new HEAD.

For untracked files or files in scratch sessions (where there is no git baseline) the toggle is disabled with a tooltip explaining why — showing the entire file as "added" in a diff view is not useful.

The selected view mode is persisted in session metadata and applies to every file tab in the session, so reloading the app or switching sessions keeps your preferred view.

Saving Changes

Save the current file with either:

  • The save button in the editor toolbar
  • The ⌘S / Ctrl+S shortcut while the editor has focus

Saves are atomic writes of the full file content to the worktree path. The file on disk is replaced with exactly what you see in the editor — trailing whitespace, final newline, encoding (UTF-8), and all. There is no auto-save, no create/rename/delete, and no live reload if the agent modifies the open file (those behaviors are intentionally out of scope for this release).

Unsaved Changes on Close

Closing a file tab (via either close button) while that file has unsaved edits opens a confirmation dialog asking whether to discard the changes or keep editing. This prevents accidentally losing work. Switching away from a dirty tab does not prompt — the edits stay in memory and the dirty indicator stays on the tab, so you can return and save later.

Conflict Detection

The agent running in the same session can modify the file you are editing. To avoid silently overwriting the agent's work, the editor tracks the file's modification time (mtime) at load and re-checks it on save.

If the file on disk has changed since you opened it, the save is rejected and a conflict dialog opens with three choices:

  • Reload from disk — replaces the editor content with the latest version from disk. Your unsaved edits are lost. Use this when you want to see what the agent wrote and edit on top of it.
  • Overwrite — forces the save, replacing the agent's changes with your editor content. Use this when you are sure your version is correct.
  • Cancel — dismisses the dialog and leaves the editor untouched, so you can inspect or copy content before deciding.

The mtime check uses a ~1ms tolerance for filesystem precision, so ordinary saves of a file you are the only editor of never trigger a conflict.

Size and Binary Limits

  • Files larger than 4 MB are rejected with a "File too large" message rather than loaded into the editor.
  • Binary files (detected by the presence of a null byte) are not displayed; the editor shows a "Binary file" message instead.

Persistence

The file browser and tab state are persisted per session to the session's metadata file, so switching sessions in the sidebar — or reloading the app — restores everything as you left it:

  • Whether the file browser panel was open or closed
  • The list of open file tabs, in the order they were arranged by any drag reorders
  • Which tab was active (the Session tab, or a specific file)
  • The diff view mode (plain / split / inline) selected for the file tabs

When you click a session in the sidebar, the state is seeded synchronously from the sidebar's cached session data, so the tab bar and browser appear on the first paint rather than flickering in after a meta fetch completes.

Contents of unsaved edits are not persisted — only which files had tabs open. Reopening a session loads each file's current on-disk contents into its tab.

Relationship to Other Panels

  • The file browser panel docks to the right and sits to the left of the GitHub panel. Both can be open simultaneously.
  • The tab bar sits above the session content area, below the session header toolbar — the header buttons (reset, file-browser toggle, GitHub panel, close session) remain accessible no matter which tab is active.
  • The terminal pane, when open, docks at the bottom of the content area and is shared across all tabs — it stays visible and interactive whether the active tab is the Session or a file.