File Viewer
The file panel: read one file as source, rendered Markdown or HTML, inline media, or a diff, shown as a dialog, a grid panel, or a dock chip.
What the File Viewer is
The File Viewer is Daintree's file panel kind: a read-only view of one file. It renders source with syntax highlighting, Markdown and HTML as documents, images, video, audio and PDFs inline, and, when the file has local changes, its working-tree diff. Nothing in it edits anything; it exists so you can confirm what an agent claims to have done without switching to an editor.
To walk a whole directory rather than read one file, use the File Browser, which is a separate panel kind.
Opening a file
| Where | What you get |
|---|---|
| Click a file path in terminal output | A file viewer dialog, scrolled to the line if the path carried one (src/index.ts:42:7) |
| Click a changed file in the Review Hub or the worktree sidebar | The file's diff (see Review Hub) |
| File Browser, Enter or double-click a row | The file as its own grid panel |
| The panel launcher | An empty file panel with a search box (see below). Matches on file, md, markdown, readme, docs, spec, document, viewer |
| An agent or plugin | A dialog (ephemeral) or a grid panel (persistent), depending on which action it dispatches |
A file panel opened with no file shows a picker: type into Search files and it searches the project or worktree root, listing up to 50 matches. Pick one and the panel binds to it.
Dialog, grid, dock
Every panel kind in Daintree has three presentations, and the File Viewer is the kind that uses all three hardest.
- Dialog: what a terminal file link gives you. A dialog is deliberately ephemeral: it never counts toward the panel limit and is never restored on restart. Dialogs stack rather than replace, so opening a diff from a Review Hub dialog leaves the hub underneath.
- Grid: a persistent panel alongside your terminals, saved with the layout. Any dialog can become one: the topmost dialog carries an Open as panel button in its header that promotes it into the grid.
- Dock: the compact strip. File panels are dockable, and they keep both scroll position and view mode across the move, so docking a file mid-read and pulling it back does not reset it. A docked single file panel shows the inline Move to grid button in its header.
Closing a file panel, or moving it to the dock, returns focus to whatever you were last reading rather than to the first terminal in the grid: the viewer is a reading surface, and so is what you were doing before it.
Long files scroll inside the panel's own pane rather than growing the app layout, and the dialog scrolls in both Source and Rendered mode.
Source, Rendered, Diff
The toolbar's left segment is a three-way toggle labeled Source, Rendered and Diff. It only appears when there is more than one mode to choose, and the two optional modes are independent capabilities:
- Rendered exists for Markdown and HTML only. Every other file is source-only.
- Diff appears only while the file has a local change in a worktree Daintree knows about. If the change is committed or reverted while you are looking, the segment disappears and the panel falls back to Source.
Source mode carries a metadata bar above the content reading line count, byte size and encoding: 142 lines · 4.2 KB · UTF-8. Rendered mode omits it, because the document is the view.
Markdown
Markdown files get the full Source / Rendered pair. Rendered mode lays the document out with Daintree's own prose styling; Source mode shows the raw text with syntax highlighting.
- Wrap long lines: a toolbar toggle, offered in Markdown source mode. The choice is a preference, so it holds across files and panels.
- Local images referenced by a rendered document are re-requested when they change on disk. A Markdown file whose embedded diagram was regenerated shows the new diagram without you reopening the panel: the file's own bytes never changed, which is exactly the case a naive freshness check misses.
HTML
HTML files also get a Rendered mode, served into a sandboxed iframe over a nonce'd internal preview URL that the main process mints only when a read explicitly asks for it. A generated report, a coverage summary, a built page: an agent can produce one and you can look at it without giving the file the run of the app.
In Rendered mode the toolbar's last button changes from Open in editor to Open in browser, which hands the file to your real browser when the sandbox is too tight for what the page needs.
Images, video, audio and PDFs
Media does not go through the text read path at all. Images, video, audio and PDFs are served to the renderer over Daintree's own internal file protocols, which is why they are not subject to the 500 KB text limit: an image larger than that displays rather than failing.
| Kind | Plays or renders | Recognized, but refused with a reason |
|---|---|---|
| Images | png, apng, jpg, jpeg, gif, webp, avif, bmp, ico | None |
| SVG | Read as text, sanitized, then inlined | Content the sanitizer rejects |
| Video | mp4, m4v, webm, ogv, played inline | mov, mkv, avi, wmv |
| Audio | mp3, wav, flac, ogg, oga, opus, m4a, aac, played inline | wma, aiff, aif, mid, midi, amr |
| Framed into Chromium's built-in viewer, with its own page and zoom controls | None |
The refused formats are listed by name on purpose. Chromium cannot demux them even when the codec inside is one it knows, so the viewer says "Can't play this video format" rather than dropping through to the text path and claiming the file is binary or oversized.
Refresh re-requests the media surface, so a video an agent just re-encoded plays the new file. Background refreshes deliberately do not: a change tick from an unrelated write must not restart playback or throw away a PDF reader's page and zoom.
Diffs
When the file has a local change, the Diff segment opens its working-tree diff without leaving the panel: you do not have to go back to the Review Hub to see what moved. If the file changes underneath a loaded diff, an inline banner reads "File changed since this diff loaded" with a Refresh action.
The multi-file review surface (the changed-file sidebar, per-file viewed markers, full-file toggle, image compare) is a separate panel kind. See Diff Workspace.
The toolbar
| Control | What it does |
|---|---|
| Source / Rendered / Diff | View mode. Shown only when more than one mode applies. |
| Path pill | Shows the path relative to the project or worktree root, middle-truncated to fit while keeping the file name. Click it to copy the absolute path. |
| Wrap long lines | Markdown source mode only. |
| Refresh | Re-reads the file, or re-fetches the diff, depending on which is on screen. |
| Show in file browser | Opens the File Browser revealed at this file. Offered only when a live worktree contains it: the browser is scoped to a folder, so there is nothing to open for a file outside every worktree. |
| Reveal in Finder / Show in Explorer / Show in folder | The system file manager, named for your platform. Always offered, including for a file the viewer cannot render. That is exactly when it matters most. |
| Open in editor or Open in browser | Your external editor, or your real browser when you are looking at rendered HTML. |
When one of the external actions fails, an inline banner names the target that failed ("Couldn't open in editor", "Couldn't reveal in Finder") with a Retry that re-runs only that one.
Keyboard
| Shortcut | Action |
|---|---|
| Command-F Control-F Control-F | Find in the focused panel. In a file panel showing source, this opens the viewer's own find bar. |
| Command-L Control-L Control-L | Go to line, in source mode. |
| Escape | Closes the dialog presentation. A grid or docked panel has nothing for Escape to close. Use the panel's own close control. |
Command-F Control-F Control-F is one global binding routed to whichever panel has focus, so the same key finds in a terminal's scrollback, in a file, or in a web page depending on where you are.
Staying current with what agents write
Agents rewrite files while you are watching them, and the panel stays focused the whole time, so "refresh on focus" would never fire. The viewer instead re-reads on a live change signal, and where that signal comes from depends on where the file lives:
- Inside a worktree: the worktree's own filesystem watcher and its git-status poll, whichever moved most recently. Two signals rather than one, because git status never moves for a write into a gitignored folder.
- Outside every worktree: scratch folders, projects with no repository, unregistered repos. These previously showed whatever was on disk when the panel opened. They now get their own live signal, polled from the main process and only while the view is visible, so a file the panel is showing stays current wherever it sits.
- Returning to a project: a project view that was cached while you worked elsewhere re-reads its file panels on reveal, because everything that happened while it sat cached is precisely what the panel could not otherwise see.
Background re-reads are silent: no loading skeleton flashes, and a transient failure (catching a file mid-save) keeps the last good content rather than blanking the panel. A file that is genuinely gone (deleted, or permissions revoked) surfaces the error rather than showing stale text forever.
Limits and error states
Text reads are capped at 512 KB. The cap is not configurable, and it applies only to the text path: media served over the internal protocols is unaffected.
| State | Message |
|---|---|
| Binary | Binary file: cannot display |
| Too large | File too large to display as text (> 500 KB) |
| Git LFS pointer | Git LFS pointer: run git lfs pull to download the file contents |
| Missing | File no longer exists |
| Outside the root | File is outside the project root |
| Invalid path | Invalid file path |
| Permissions | Permission denied: you don't have access to this file |
| Unsupported media | Can't play this video format / Can't play this audio format, naming the formats that do work |
Every error state keeps a Retry button, except for an unsupported media format: retrying the same extension can never succeed, so the action would be dead. Reveal and Open in editor stay available in all of them.