termd
A terminal markdown reader with a split-pane outline.

termd renders a markdown file the way a document viewer would, not the way a
pager does. Headings become a navigable outline in the left pane; the document
renders in the right. Tables get real box borders sized to their content, code
blocks are framed and syntax highlighted, and the file reloads as you save it.
termd README.md

Install
Install script (Linux, macOS, FreeBSD)
curl -fsSL https://raw.githubusercontent.com/PatchMon/termd/main/install.sh | sh
Installs to /usr/local/bin. Override with TERMD_INSTALL_DIR:
curl -fsSL https://raw.githubusercontent.com/PatchMon/termd/main/install.sh \
| TERMD_INSTALL_DIR="$HOME/.local/bin" sh
Go
go install github.com/PatchMon/termd@latest
Linux packages
.deb, .rpm, .apk and Arch packages are attached to every
release.
# Debian / Ubuntu
sudo dpkg -i termd_*_amd64.deb
# Fedora / RHEL
sudo rpm -i termd_*_amd64.rpm
# Alpine
sudo apk add --allow-untrusted termd_*_amd64.apk
Manual download
Grab the archive for your platform from the
releases page, then:
tar -xzf termd-linux-amd64.tar.gz
sudo install -m755 termd /usr/local/bin/termd
Verify it first if you like — every release ships a checksums.txt:
sha256sum -c checksums.txt --ignore-missing
Windows
In PowerShell:
irm https://raw.githubusercontent.com/PatchMon/termd/main/install.ps1 | iex
Installs to %LOCALAPPDATA%\Programs\termd and adds it to your user PATH, so
no administrator rights are needed. Downloads are checked against the release
checksums.txt before anything is extracted.
To pin a version or change the location, set these before running:
$env:TERMD_VERSION = "v1.2.0"
$env:TERMD_INSTALL_DIR = "D:\tools\termd"
irm https://raw.githubusercontent.com/PatchMon/termd/main/install.ps1 | iex
Windows Terminal is recommended; the legacy console host renders box drawing
poorly.
From source
Requires Go 1.26 or newer.
git clone https://github.com/PatchMon/termd.git
cd termd
make build # produces ./termd with version metadata baked in
make install # or install into $GOBIN
Usage
termd <file.md> Open markdown in a split-pane viewer
termd -v Version and update check
termd -h Help
Keys
| Key |
Action |
j / ↓, k / ↑ |
Move between headings (outline pane) |
Enter |
Jump to section and focus the document |
g / G |
First / last section |
Tab / Esc |
Switch panes |
PgUp / PgDn |
Scroll the document |
/ |
Search; Enter to confirm |
n / N |
Next / previous match |
c |
Copy the visible code block |
o |
Open the visible image in your system viewer |
q |
Quit (clears the search first, if one is active) |
The mouse wheel scrolls the document, and clicking the outline jumps to a
section. Shift+drag selects text, bypassing the app's mouse capture.
The file is watched for changes and reloads on save, keeping your scroll
position.
What it renders
| Feature |
Notes |
| Tables |
Box-drawn, sized to content, GFM column alignment |
| Code blocks |
Framed and labelled, syntax highlighted via Chroma |
| Alerts |
> [!NOTE], [!TIP], [!IMPORTANT], [!WARNING], [!CAUTION] |
| Task lists |
- [x] / - [ ] render as ✓ / ○ |
| Front matter |
Leading YAML becomes a metadata header |
| Links |
Clickable via OSC 8 where the terminal supports it |
| Images |
Inline where the terminal can, otherwise a callout |
| Emoji |
:rocket: shortcodes expand |
Blockquotes, nested lists, definition lists, strikethrough and horizontal rules
all render too.
Images in the terminal
Terminals cannot paint bitmaps as text, so termd uses a graphics protocol
where one is available and falls back to a one-line callout everywhere else.
| Protocol |
Terminals |
Inline images |
| Kitty |
kitty, Ghostty, WezTerm, Konsole |
Yes, automatically |
| None |
everything else |
One-line callout |
Only the Kitty protocol is used automatically, because only it draws images
into ordinary text cells — so a picture clips, scrolls and reflows exactly like
the text around it.
iTerm2 and sixel are implemented but not enabled automatically. Both paint a
bitmap at the cursor rather than into cells, which a scrolling document cannot
accommodate: the terminal advances past the image so following lines land in the
wrong place, and the rows reserved beneath it overwrite the picture. Doing it
properly needs a renderer that paints images after each frame at absolute
positions. Until then a clean callout beats a corrupted screen, so these are
reachable only by opting in:
TERMD_IMAGES=iterm2 termd README.md # expect display glitches while scrolling
Press o to open any image in your system viewer, in any terminal. Remote
images are never fetched while rendering.
Configuration
| Variable |
Values |
Default |
TERMD_THEME |
dark, light |
Detected from the terminal |
TERMD_HYPERLINKS |
1, 0 |
Detected from the terminal |
TERMD_IMAGES |
kitty, iterm2, sixel, off |
Detected from the terminal |
TERMD_INSTALL_DIR |
path |
/usr/local/bin (install script only) |
TERMD_GITHUB_REPO |
owner/repo |
PatchMon/termd (install script only) |
Set TERMD_IMAGES explicitly over SSH: the outer terminal's identity does not
reach the remote shell, so detection has nothing to work from.
Development
make build # build with version metadata
make test # run the test suite
make lint # golangci-lint, falling back to go vet
make help # list all targets
Binaries are published for:
- Linux — amd64, arm64, armv7, 386, riscv64
- macOS — amd64 (Intel), arm64 (Apple Silicon)
- Windows — amd64, arm64, 386
- FreeBSD — amd64, arm64, 386
All are static, CGO-free binaries with no runtime dependencies. Clipboard copy
(c) on X11 additionally wants xclip or xsel installed.
License
MIT