Markdown · Code · Mermaid · Image · Audio · Video · Web — rendered in your terminal.
curl -fsSL https://raw.githubusercontent.com/eric8810/dlook/main/scripts/install.sh | bashLinux · macOS · Windows — single static binary, no runtime, starts instantly.
You cat a README and get a wall of raw markdown. You open a .ts file and see
plain text. You find a flowchart.mmd and have no idea what it draws. A .png
screenshot is binary noise in your terminal.
dlook renders all of them, in truecolor, in a pager you already know how to use.
| dlook | |
|---|---|
| 🎨 Markdown | colored headings (h1/h2 cyan, h3/h4 blue), task lists ☑/☐, rounded tables, styled links, quotes, strikethrough |
| 🎵 Audio | play in place from a document, or directly (dlook song.mp3) — media bar with progress, p play/pause, ←→ seek, -/+ volume |
| 🎬 Video | in-terminal playback via mpv (kitty / sixel graphics); media bar and key handling stay with dlook. Requires mpv installed |
| 🌐 Web | dlook https://… or a local .html renders as readable text with clickable links; no subresources are fetched |
| 🖼️ Images | in-markdown & standalone image viewing — kitty / sixel / iTerm2 graphics protocols with halfblocks truecolor fallback; local, http(s) and data: sources |
| 🖍️ Code | token-level 24-bit truecolor highlighting — 40+ languages incl. TypeScript, Vue, Svelte, TOML, GraphQL, Dockerfile, PowerShell |
| 📈 Mermaid | 28 diagram types rendered to truecolor ASCII art — no browser, no node; palette follows your terminal background (dark/light, auto-detected) and echoes the body text colors |
| 🖱️ Selection | drag to select (reversed highlight, edge auto-scroll), release to copy via OSC 52 — works over SSH |
| 🔥 Live reload | watches the file and re-renders on every save |
| 📦 Tiny & static | ~9.2 MB binary, zero runtime, instant startup |
$ dlook README.md # markdown: headings / tasks / tables / links / images
$ dlook src/main.rs # code: truecolor syntax highlight
$ dlook flow.mmd # mermaid: ASCII art diagram
$ dlook shot.png # image: terminal graphics protocol (kitty/sixel/iTerm2)
$ dlook plain.txt # plain textReal renders, captured from the binary (regenerate with python3 scripts/gen-promo.py):
curl -fsSL https://raw.githubusercontent.com/eric8810/dlook/main/scripts/install.sh | bashAuto-detects your platform (Linux/macOS, x86_64/aarch64) and installs to
~/.local/bin. Customize with env vars — note they must apply to the bash
on the right side of the pipe, not to curl:
curl -fsSL https://raw.githubusercontent.com/eric8810/dlook/main/scripts/install.sh \
| sudo env VERSION=v0.3.0 INSTALL_DIR=/usr/local/bin bashWindows: download dlook-x86_64-pc-windows-msvc.zip from Releases and unzip.
cd rs
cargo build --release
./target/release/dlook README.md| Key | Action |
|---|---|
q / Esc |
Quit (exit 0) |
j / k |
Scroll down / up one line |
Space / PageDown |
Scroll down one page |
PageUp |
Scroll up one page |
g / G |
Go to top / bottom |
↑ ↓ |
Scroll one line |
Home / End |
Go to top / bottom |
Ctrl+C |
Quit (exit 130) |
⌫ / Alt+← |
Go back to previous file (after link navigation) |
| Mouse wheel | Scroll up / down |
| Mouse click | Open local file link (↗) in dlook; open external URL via system opener |
| Mouse drag | Select text (reversed highlight); auto-scrolls at viewport edges |
| Release mouse | Copy selection to clipboard via OSC 52 (works over SSH) |
Shift + click |
Extend selection |
y / Enter |
Copy active selection (OSC 52) |
Esc |
Clear selection — quits only when no selection is active |
Tip: your terminal's native selection still works with
Shift+ drag (the app enables mouse reporting, so plain drag is captured by the app).
- Markdown (
.md/.markdown): rendered bytermimad— colored headings, nested lists, task lists, rounded-border tables with per-column alignment, quotes, strikethrough; inline links render as a blue underlined label + gray URL. Fenced code blocks are syntax-highlighted;```mermaidblocks render as diagrams. - Images (
.png/.jpg/.jpeg/.gif/.webp/.bmp/.ico/.tiff): rendered via terminal graphics protocols — kitty, sixel or iTerm2 when your terminal supports one (auto-detected), with a halfblocks truecolor fallback that works everywhere, over SSH included. In markdown, a standalone-paragraphrenders in place and scrolls with the document; sources may be local paths (resolved against the file's directory),http(s)://URLs (fetched in the background, size/timeout capped) ordata:URLs. Images inside a text line degrade to a clickable link. Clicking a link that points to an image file opens it in dlook;dlook shot.pngworks directly too. Force a protocol withDLOOK_IMAGE_PROTOCOL=halfblocks, disable images withDLOOK_IMAGE_PROTOCOL=off. - Local file links (e.g.
[guide](./docs/guide.md)): labeled with a↗marker; click to open the target inside dlook (mode/highlighting re-detected by extension),⌫/Alt+←returns to the previous file with scroll position restored. Relative paths resolve against the current file's directory;%XXand<path with spaces>forms are supported. Bad targets (missing/dir/binary) show a status message instead of navigating. - External links (
http://etc.): click to open with the system opener (xdg-open/open/start); anchor links show a hint. - Mermaid (
.mmd/.mermaid): rendered to truecolor ASCII art viamermansi. - Code / text: token-level truecolor highlighting via
syntectwith the two-face full syntax set; long lines are truncated (less -Sstyle). - Unknown extension: uncolored plain text.
- Binary files (NUL byte in first 8KB): refused, exit 1 — except image extensions, which open in image mode (TTY only; piping an image errors).
- Non-TTY (piped): raw content to stdout, exit 0 — no TUI
(mermaid files are rendered to ASCII first).
dlook x.md | grepjust works. - Live reload: re-renders on file change.
| Situation | Code |
|---|---|
Normal quit (q/Esc) |
0 |
| Bad arguments (none / too many) | 2 |
| File not found / unreadable / binary / directory | 1 |
Ctrl+C |
130 |
- Mode dispatch (
rs/src/main.rs): argv → binary check → mode detection (markdown / mermaid / image / code) → non-TTY passthrough → TUI loop. - One rendering pipeline: every content type converges to
Vec<StyledLine>— termimad + syntect + mermansi all emit ANSI, converted viaansi-to-tui, painted by a single ratatui viewport widget. - Selection (
rs/src/selection.rs): mouse drag builds a selection in content coordinates (stable across scrolling); the viewport renders selected spans reversed; releasing copies the text via OSC 52. - Link navigation (
rs/src/links.rs): local-file links carry aLinkSpan(content coordinates, like selection); a click hit-tests the span, resolves the path against the current file's directory and swaps theDocin place; a history stack powers⌫back. External links are handed to the system opener. - Images (
rs/src/images.rs): protocol picker (kitty/sixel/iTerm2 → halfblocks) queried once before the event loop; anImageCtxregistry loads and decodes sources (local/http(s)/data:) on background threads and bumps a dirty counter that triggers a re-layout — same path as hot reload. Images reserve blank rows inDoc.lines(scroll/selection math unchanged) and the viewport paints a scrollableSlicedImageinto the cell buffer after text. - Packaging:
cargo build --releasewith size-focused profile (opt-level=z, fat LTO, strip,panic=abort). CI builds per-target binaries on tag push and attaches them to the GitHub Release.
rs/src/
main.rs entry: argv + binary detection + mode dispatch
args.rs argv parsing + --help/--version
content.rs file reading + binary detection + hot reload + navigate reads
lang.rs extension → mode + syntax token mapping
links.rs link span model + target classification/path resolution
highlight.rs syntect (two-face) highlighter
images.rs image registry: protocol picker, background loading (local/http/data:), cache
markdown.rs termimad rendering + task checkboxes + link styling/spans + table frame + image segments
mermaid.rs mermaid → truecolor ASCII art (mermansi)
selection.rs text selection model (content coords, highlight, copy text)
doc.rs Doc.lines/links/images + scroll math
viewport.rs scroll viewport + selection highlight + protocol image painting
termio.rs crossterm setup + event loop + keys/wheel/click-links + resize + link navigation + image rebuilds
ansi_lines.rs ANSI → ratatui lines
Three layers, all green:
| Suite | Command | Coverage |
|---|---|---|
| Unit (Rust) | cd rs && cargo test |
link scanner/classifier, link spans, task checkboxes, table framing, selection model, image syntax/segmentation/data-URLs/registry |
| E2E (pty + pyte) | BIN=rs/target/release/dlook python3 test/e2e/run_acceptance.py |
138 checks: rendering, scrolling, resize, exit codes, non-TTY, markdown styling, selection, language coverage, link navigation/click/back/external-opener, images — local/remote/direct-open/fallback (A–N) |
| E2E (tmux, real terminal) | BIN=rs/target/release/dlook bash test/e2e/run-tmux.sh |
40 checks: incl. OSC 52 clipboard content verification, mouse injection, resize, exit codes, halfblocks truecolor images (T1–T36) |
- DESIGN.md — original Node/vue-tui design
- DESIGN-rust.md — Rust rewrite design
- GAP.md — vue-tui vs Rust rendering capability analysis
- DECISIONS.md — decision log for the feature set (D1–D15)

