markdown-reader
A terminal-based markdown file browser and viewer built with Rust and ratatui. Browse a repository, open multiple files as tabs, search across content, jump to lines, switch themes, and resume exactly where you left off.
Layout
+-----------------+--------------------------------------------------+
| | 1: README.md 2: guide.md 3: CHANGELOG.md |
| Files +--------------------------------------------------+
| | |
| ▼ docs/ | # Document Title |
| README.md | |
| guide.md | 1 │ Some rendered **markdown** content with |
| ▶ src/ | 2 │ headings, lists, tables, and code blocks |
| CHANGELOG.md | 3 │ rendered inline. |
| | |
| 30% | 70% |
+-----------------+--------------------------------------------------+
| Search [Files] (Tab to toggle) / query█ |
+---------------------------------------------------------------------+
| VIEWER [1/3] README.md (42%) Tab:panel t:new-tab T:picker ... |
+---------------------------------------------------------------------+
The window is split into three stacked sections: a main area, an optional search bar, and a single-line status bar. The main area holds the file tree on the left and the viewer on the right. When one or more files are open, a tab strip is rendered above the viewer. An optional line-number gutter is drawn on the left of the viewer content when enabled.
Features
- Multiple tabs — open many markdown files at once, navigate them with
vim-style
gt/gT, jump by number (1–9,0for last), close withx, and useTfor a full tab picker overlay. Duplicate opens focus the existing tab instead of piling up. - Themes — six built-in palettes (Default, Dracula, Solarized Dark, Nord, Gruvbox Dark, GitHub Light). Switch live from the settings modal; every open document re-renders with the new colors.
- Session resume — per-project: the last open tabs, active tab, and scroll positions are saved and restored automatically on the next launch.
- Line numbers — optional left gutter in the viewer, togglable from settings.
- Global search — by filename or by content, with result cycling. Confirming a result opens the file in a new tab without clobbering the current one.
- In-document find —
Ctrl+fto search within the active document, with highlighted matches andn/Nto cycle. Per-tab state — switching tabs preserves each tab's find state independently. - Go to line —
:opens a prompt; type a line number, Enter jumps. Clamped to document bounds and aligned with the gutter numbering. - Mouse support — click tabs to activate, click file tree items to open, scroll the viewer or tree with the wheel.
- Rendered markdown preview — headings, lists, code blocks, tables, links, blockquotes, task lists, and more, styled from the active theme.
- Live file watching — the tree and open tabs reload when files change on disk, preserving per-tab scroll positions.
- Respects .gitignore — uses the
ignorecrate to skip ignored files.
Installation
Prerequisites
- Rust toolchain (1.85+ recommended, edition 2024)
From crates.io
From GitHub
Building from source
The binary will be at target/release/markdown-reader.
Usage
# Browse the current directory
# Browse a specific directory
# Show help
Once inside the TUI, press ? at any time for the keyboard help overlay.
Press c to open the settings modal (themes and line numbers). Press q
to quit — the current tabs and scroll positions are saved before exit, so
reopening the same directory resumes where you left off.
Keyboard shortcuts
Navigation (tree)
| Key | Action |
|---|---|
j / Down |
Move down |
k / Up |
Move up |
Enter / l / Right |
Open file (current tab) / expand directory |
h / Left |
Collapse directory |
gg |
Jump to first item |
G |
Jump to last item |
Tab |
Switch focus to viewer |
t |
Open selected file in a new tab |
Viewer
| Key | Action |
|---|---|
j / Down |
Scroll down one line |
k / Up |
Scroll up one line |
d / u |
Half-page scroll down / up |
PageDown / PageUp |
Full-page scroll down / up |
gg |
Scroll to top |
G |
Scroll to bottom |
Ctrl+f |
Find in document |
n / N |
Next / previous match |
: |
Go to line |
Tab |
Switch focus to tree |
Tabs
| Key | Action |
|---|---|
gt |
Next tab |
gT |
Previous tab |
1–9 |
Jump to tab N (1-indexed) |
0 |
Jump to last tab |
` |
Jump to previously active tab |
x |
Close the active tab |
T |
Open the tab picker overlay |
The tab picker lists every open tab. Use j/k or arrows to navigate,
Enter to activate, x to close a tab from within the picker, and
Esc or T to dismiss.
A maximum of 32 tabs can be open at once. Attempting to open a 33rd tab is silently ignored; close an existing one first.
Panels
| Key | Action |
|---|---|
[ |
Shrink file tree |
] |
Grow file tree |
H |
Toggle file tree visibility |
Search
| Key | Action |
|---|---|
/ |
Open search |
| Any character | Append to query |
Backspace |
Delete last character |
Tab |
Toggle file-name vs content search |
Down / Ctrl+n |
Next result |
Up / Ctrl+p |
Previous result |
Enter |
Open selected result (in a new tab) |
Esc |
Close search |
Settings
| Key | Action |
|---|---|
c |
Open settings (theme, line numbers) |
Esc / c |
Close settings |
General
| Key | Action |
|---|---|
? |
Toggle help overlay |
q |
Quit (saves session) |
Mouse support
The terminal must forward mouse events. Most modern terminals (iTerm2, Alacritty, Kitty, WezTerm, GNOME Terminal, Windows Terminal) do so out of the box.
| Action | Effect |
|---|---|
| Click a tab | Activate that tab |
| Click a file-tree item | Select and open it |
| Click a directory | Toggle expand/collapse |
| Click inside the viewer | Focus the viewer |
| Scroll wheel in the viewer | Scroll the document (3 lines per tick) |
| Scroll wheel in the tree | Move the tree selection |
| Click a row in the tab picker | Activate that tab |
Themes
Six built-in themes, switchable live from the settings modal (c):
- Default — balanced palette that works on dark terminals.
- Dracula — the classic pink/purple dark theme.
- Solarized Dark — Ethan Schoonover's dark palette.
- Nord — cool blue-based Arctic palette.
- Gruvbox Dark — warm retro groove.
- GitHub Light — bright palette for light terminals.
Theme changes re-render every open tab immediately, so switching feels instantaneous. The choice is persisted and restored on next launch.
Configuration and state files
Both files are TOML. Missing or corrupt files are silently ignored — the app starts with defaults rather than refusing to launch.
config.toml — user preferences
- Linux:
$XDG_CONFIG_HOME/markdown-reader/config.toml(typically~/.config/markdown-reader/config.toml) - macOS:
~/Library/Application Support/markdown-reader/config.toml - Windows:
%APPDATA%\markdown-reader\config.toml
Fields:
= "dracula" # default | dracula | solarized_dark | nord | gruvbox_dark | github_light
= true
state.toml — per-project session
Holds a map of canonical root paths to their saved tab lists and active indices. Old (v0.1.0) single-file entries from prior versions are read transparently.
- Linux:
$XDG_STATE_HOME/markdown-reader/state.toml(typically~/.local/state/markdown-reader/state.toml) - macOS:
~/Library/Application Support/markdown-reader/state.toml - Windows:
%LOCALAPPDATA%\markdown-reader\state.toml
To reset a session (for example, if you want a fresh start on a project), delete the state file. Configuration is untouched.
Markdown rendering
Elements are rendered with styles from the active theme; the list below shows the default theme.
| Element | Rendering |
|---|---|
| H1 | Cyan, bold, underlined, with █ prefix |
| H2 | Blue, bold, with ▌ prefix |
| H3 | Magenta, bold, with ▎ prefix |
| H4–H6 | Bold |
| Bold / italic / strikethrough | Terminal modifiers |
| Inline code | Themed, wrapped in backticks |
| Code block | Box-drawn border, tinted background |
| Blockquote | Dim text with │ left border |
| Unordered list | Colored bullets •, ◦, ▪ by depth |
| Ordered list | Numbered, themed markers |
| Task list | Checked / unchecked boxes |
| Table | Box-drawn grid with bold header |
| Link | Underlined, themed |
| Horizontal rule | Full-width ─ line |
Dependencies
| Crate | Purpose |
|---|---|
| ratatui | Terminal UI framework |
| crossterm | Terminal backend, input and mouse events |
| pulldown-cmark | Markdown parsing |
| ignore | .gitignore-aware file discovery |
| tokio | Async runtime |
| notify-debouncer-mini | Filesystem change watching |
| clap | CLI argument parsing |
| anyhow | Error handling |
| serde | Config and state serialization |
| toml | TOML format for config and state files |
| dirs | Platform-native config/state directories |
License
MIT