Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

termrender

Terminal screenshots that replicate your real terminal — your shell prompt, your iTerm2 colors and font, and the program's true colored output. Dark or light mode follows your system automatically.

Basic demo

termrender exec --theme auto --window-bar rings -o demo-basic.png -- echo "Hello, termrender!"

Most terminal-to-image tools render text you hand them, with a generic theme and no prompt. termrender instead runs the command in a real PTY and asks your own shell to render its prompt, so the screenshot looks like what you'd actually see in your terminal. The image above was generated with a single command — your real fish prompt, iTerm2 colors, and the command's output, all captured automatically.

Built on termless for headless terminal emulation and PNG rendering.

Install

Requires Bun.

bun install
bun link        # registers the global `termrender` command (~/.bun/bin)

Usage

exec — run a command and screenshot it (highest fidelity)

termrender exec --theme auto -o ls.png \
  --window-bar rings -- eza -la

# Pin the prompt style and working directory
termrender exec --prompt fish --cwd ~/some/repo -o git.png -- git log --oneline -5
Option Default Description
--prompt <mode> auto Prompt source: auto, fish, zsh, none. auto resolves the terminal's real shell: iTerm2 profile command → login shell (dscl) → $SHELL
--cwd <dir> cwd Working dir for the command and the prompt's path/git segments
--timeout <ms> 30000 Max wait for the command to exit
--no-auto-rows Keep the terminal's full height instead of trimming empty rows
--no-trailing-prompt Don't repeat the prompt after the command exits

Commands must exit on their own — for servers, watch modes, and TUIs, record a .cast with asciinema and use render.

render — render a .cast recording or piped ANSI text

termrender render recording.cast -o screenshot.png   # cols/rows from the cast header

echo -e "\e[32mgreen\e[0m" | termrender render -o out.png

Common options

Available on both exec and render:

Option Default Description
-o, --output <path> output.png Output PNG path
--theme <path|auto> system theme .itermcolors file, auto (live iTerm2 profile), or omit for system dark/light
--profile <name> default profile iTerm2 profile name for --theme auto
--cols <n> / --rows <n> cast header, else 80×24 Terminal size
--font-family <family> profile or bundled CSS font family
--font-size <n> profile or 16 Font size in px; cell geometry scales with it
--font-system auto Match arbitrary system fonts (profile fonts, CJK) — ~5× slower. Auto-selected when the terminal text contains wide (CJK) characters
--padding <n> 12 Padding around terminal content
--border-radius <n> 8 Border radius for the terminal frame
--window-bar <style> none Window bar: rings, colorful, none
--margin <n> 0 Outer image margin
--margin-fill <color> Margin fill color (hex)

Theme sources

Source What you get
(default, no --theme) System theme — dark or light mode automatically follows your macOS appearance
--theme auto Your live iTerm2 profile: 16-color palette, fg/bg/cursor, profile font + size. Reads the preferences plist directly (macOS) — no export step
.itermcolors file Full exported iTerm2 color scheme

Features

🐟 Fish syntax highlighting

termrender uses fish shell's token-level algorithm to color commands, options, pipes, and redirections exactly as fish would:

Token Color Example
Command cyan git, echo
Option cyan --oneline, -la
Pipe/operator magenta |, &&
Redirection bold cyan >, >>

Syntax highlighting

termrender exec --theme auto --window-bar rings -o demo-highlight.png -- git log --oneline -5

🎨 System-aware themes

Without any --theme flag, termrender automatically picks dark or light based on your macOS system appearance. Here it shows a directory listing with your live iTerm2 palette:

iTerm2 live theme

termrender exec --theme auto --window-bar rings -o demo-theme.png -- ls -la

🪟 Window bar styles

Choose rings (macOS traffic lights), colorful (solid dots), or none:

Window bars

termrender exec --theme auto --window-bar colorful -o demo-windowbar.png -- echo "termrender"

🐚 Prompt styles

Use --prompt fish or --prompt zsh to match your shell's prompt. termrender invokes the actual shell to render the prompt line — path abbreviations, git branch detection, and color variables all work as they would in your real terminal:

Zsh prompt with Oh My Zsh

termrender exec --prompt zsh --theme auto --window-bar rings -o demo-omz.png -- git log --oneline -5

Performance

Rendering is a single pass: exec starts on a small screen, grows the terminal while output scrolls (so long output is never truncated), shrinks it to the used row count, and renders once. Each render loads only the bundled JetBrains Mono set instead of rescanning every system font.

Measured on macOS (Bun 1.3, Apple M-series):

Scenario Before After
exec echo (steady state) ~590ms ~210ms
200-row render (pinned --rows) ~2300ms ~330ms
1500-row output truncated at 200 rows complete
first render (cold start) ~2300ms ~215ms

MCP server

bin/termrender-mcp.ts exposes termrender to agents as two MCP tools — render_command and render_text — with the rendered PNG returned inline as an image content block, so the agent sees the result immediately and can self-correct (wrong theme, misaligned columns, …) in the same loop.

Tools

Tool Description Key arguments
render_command Run a command in a real PTY and screenshot it exactly as the user's terminal would show it command (argv array), cwd, prompt (auto/fish/zsh/none), timeoutMs, plus common options
render_text Render pre-captured terminal output (ANSI honored) as a styled screenshot text, plus common options

Common arguments: theme ("auto" — live iTerm2 profile, "default" — built-in dark, or a path to an .itermcolors file), cols, rows, fontSize, windowBar (none/rings/colorful), fontSystem, outputPath (defaults to a temp file; the image is returned either way).

Registration

Claude Code:

claude mcp add --scope user --transport stdio termrender -- bun run /path/to/termrender/bin/termrender-mcp.ts

Codex (OpenAI):

codex mcp add termrender -- bun run /path/to/termrender/bin/termrender-mcp.ts

Or in ~/.codex/config.toml:

[mcp_servers.termrender]
enabled = true
transport = { type = "stdio", command = "bun", args = ["run", "/path/to/termrender/bin/termrender-mcp.ts"] }

OpenCode:

Add to opencode.json (project root) or ~/.config/opencode/opencode.json:

{
  "mcp": {
    "termrender": {
      "type": "local",
      "command": ["bun", "run", "bin/termrender-mcp.ts"]
    }
  }
}

Note: OpenCode uses "type": "local" (not "stdio") and "command" as an array of strings — this avoids shell injection.

DeepSeek Harness (dsh)

The DeepSeek Harness ships an MCP client bridge (@deepseek-ai/dsh-mcp-client) that mounts one stdio server per config row. Add the entry to $DSH_HOME/config.yaml (or a --config overlay) — the TUI and Web surfaces hot-reload it:

- insert:
    - id: mcp-termrender
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: termrender
        transport: stdio
        command: /opt/homebrew/bin/bun
        args:
          - run
          - /path/to/termrender/bin/termrender-mcp.ts
        cwd: /path/to/termrender

The agent then sees mcp__termrender__render_command and mcp__termrender__render_text. Note the MCP child runs outside the harness sandbox (dsh's design), and image content blocks appear as placeholders in model context — the full JSON (including the base64 PNG) is preserved for the GUI.

How it works

  • Real output: exec spawns the command in a PTY (termless Terminal.spawn, Bun's native terminal support), so programs emit the same colors they would interactively.
  • Real prompt: the prompt is rendered by the actual shell — fish -i -c fish_prompt (interactive flag required, or fish skips its color variables), or zsh -ic 'print -rP "$PROMPT"' — with the right --cwd, so path and git segments are live.
  • Real theme: --theme auto extracts the default profile from com.googlecode.iterm2.plist key-by-key via plutil -extract (the plist contains binary blobs that break whole-file conversion). Supports both sRGB and Display P3 color spaces.
  • Auto-sized images: exec starts on a small screen and grows the terminal while output scrolls (up to 2000 rows — long output is no longer truncated), then shrinks it to the used row count and renders once. No replay pass.
  • Fast rendering: screenshots render with the bundled JetBrains Mono set (~130ms) instead of rescanning every system font per image (~600ms+). When the terminal text contains CJK or other wide characters, termrender automatically switches to a system-font scan so those glyphs draw at full width; --font-system / MCP fontSystem: true forces it, and fontSystem: false forces the bundled set.
  • System theme: defaults read -g AppleInterfaceStyle tells us whether macOS is in Dark or Light mode; the corresponding built-in theme is used automatically.

Known limitations

  • The fast rendering path uses the bundled JetBrains Mono set, which cannot draw CJK glyphs — wide-character text is auto-detected and rendered with system fonts, but an explicit --font-system off (or fontSystem: false) renders those characters narrow.
  • CJK wide characters render with visible gaps between cells on the fast path (system-font path draws them full width).
  • --theme auto and prompt detection are macOS-centric (iTerm2 plist, dscl).
  • exec output is capped at 2000 rows (the automatic-growth limit); for larger listings, pipe the raw output or use --rows.

License

MIT

About

Terminal screenshots that look like your real terminal — native shell prompts, live iTerm2 themes, fish highlighting. CLI tool and MCP server.

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages