pi-markdown-preview
Rendered markdown + LaTeX preview for pi, with terminal, browser, and PDF output
Package details
Install pi-markdown-preview from npm and Pi will load the resources declared by the package manifest.
$ pi install npm:pi-markdown-preview- Package
pi-markdown-preview- Version
0.17.2- Published
- Sep 19, 2026
- Downloads
- 9,463/mo · 1,743/wk
- Author
- omacl
- License
- MIT
- Types
- extension
- Size
- 2.6 MB
- Dependencies
- 3 dependencies · 3 peers
Pi manifest JSON
{
"image": "screenshots/dark-terminal.png",
"extensions": [
"./index.ts"
]
}Security note
Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.
README
pi-markdown-preview
Preview assistant responses and local Markdown, LaTeX, code, diff, and other text-based files from pi in the terminal, browser, or as PDF, with math rendering, syntax highlighting, Mermaid, and theme-aware styling.
Screenshots
Preview adapts to your pi theme. Examples with a custom theme and the built-in defaults:
Terminal preview (custom theme):

Terminal preview (default dark):

Terminal preview (default light):

Browser preview (default dark and light):
| Default dark | Default light |
|---|---|
![]() |
![]() |
Features
- Terminal preview (default) — renders markdown as PNG images displayed inline (Kitty, iTerm2, Ghostty, WezTerm). Long responses are split across navigable pages at block boundaries when possible, with a fixed-height fallback for oversized content.
- Browser preview — opens rendered HTML in your default browser as a single continuous scrollable document, with optional completion-level auto-refresh and response navigation via
--watch(-w) - PDF export — exports markdown to PDF via pandoc + LaTeX and opens it in your default PDF viewer
- LLM-callable artifact export — lets pi render the latest response, supplied Markdown/LaTeX, or a local file to PDF, HTML, or PNG files for remote/headless workflows such as Telegram delivery
- Mermaid diagrams — renders
```mermaidcode blocks as SVG diagrams in terminal/browser previews, and as high-quality vector diagrams in PDF export when Mermaid CLI is available - LaTeX/math support — renders
$inline$,$$display$$,\(...\), and\[...\]math via MathML with selective MathJax fallback for pandoc-unsupported browser/terminal equations, or native LaTeX (PDF) - Syntax highlighting — fenced code blocks in markdown and standalone code files are rendered with theme-aware syntax colouring via pandoc. Supports 50+ languages including TypeScript, Python, Rust, Go, C/C++, Julia, and more.
- Code wrapping controls — preserve fixed-width layout by default; toggle wrapping for the whole terminal preview with
w, or use global and per-block buttons in the browser. No special Markdown annotations are needed. - Annotation marker highlighting — inline
[an: ...]markers are highlighted in terminal/browser/PDF previews as note-only chips (..., without the[an: ]wrapper) outside code blocks; long notes wrap correctly in PDF instead of running off the page - Theme-aware — matches your pi theme (dark/light inference, export page/card colours, Markdown colours, accent colours, syntax colours)
- Response picker — select any past assistant response to preview, not just the latest
- File preview — preview arbitrary Markdown files (including
.md,.mdx,.rmd, and lightweight.qmddocuments), LaTeX.texfiles, diff/patch files, or code files (.py,.ts,.js,.rs, etc.) from the filesystem. Markdown HTML comments are omitted outside code, local image/PDF figures and basic Quarto/pandoc-crossref figure references are supported, and no Quarto computation is executed. LaTeX files are rendered as documents with full math and sectioning; diff files are rendered with coloured add/remove lines; code files are rendered with syntax highlighting. Use Quarto itself when full Quarto project, filter, subfigure, chapter-aware numbering, or execution semantics are required. - Caching — rendered pages are cached for instant re-display; refresh (
r) bypasses cache
Prerequisites
- Pandoc (
brew install pandocon macOS) - For terminal preview (
/previewdefault): a Chromium-based browser executable (Chrome, Brave, Edge, Chromium).puppeteer-coreis included as an extension dependency; no separate Puppeteer install is needed. - For terminal inline display: a terminal with image support (Ghostty, Kitty, iTerm2, WezTerm)
- For PDF export (optional): a LaTeX engine, e.g. TeX Live (
brew install --cask mactexon macOS,apt install texliveon Linux) - For Mermaid-in-PDF support (optional): Mermaid CLI (
npm install -g @mermaid-js/mermaid-cli) and a Chromium browser accessible to Mermaid CLI. PDF icon nodes require Mermaid CLI 11.6+.
Mermaid icons
Mermaid flowcharts support optional lucide:* and logos:* icons. Keep each icon metadata declaration on one source line:
flowchart LR
source@{ icon: "lucide:file-code-2", form: "rounded", label: "Source", pos: "b", h: 56 }
github@{ icon: "logos:github-icon", form: "rounded", label: "GitHub", pos: "b", h: 56 }
source -->|publish| github
classDef unchanged fill:#f8f9fa,stroke:#868e96,stroke-width:2px
classDef changed fill:#f3f0ff,stroke:#7950f2,stroke-width:2px
class source unchanged
class github changed
The browser renderer loads icon-pack JSON lazily from unpkg only when a diagram references that prefix, so first render requires network access. It contrast-corrects icon and shape labels against their rendered backgrounds while preserving semantic hues. PDF export forwards the same packs to Mermaid CLI 11.6+ only when a supported icon is present. Browser previews likewise load a pinned PDF.js module from jsDelivr only when a PDF figure is present; if it cannot load or render a figure, the native PDF embed remains available.
Install
pi install npm:pi-markdown-preview
Or from GitHub:
pi install https://github.com/omaclaren/pi-markdown-preview
Or try it without installing:
pi -e https://github.com/omaclaren/pi-markdown-preview
Usage
| Command | Description |
|---|---|
/preview |
Preview the latest assistant response in terminal |
/preview --pick |
Select from all assistant responses |
/preview <path/to/file> |
Preview a Markdown, LaTeX, diff, or code file |
/preview --file <path/to/file> |
Preview a file (explicit flag) |
/preview --browser (-b) |
Open preview in cmux when available, otherwise the system browser |
/preview --font-size 14 |
Preview with a custom terminal/browser font size in px (defaults: terminal 16, browser 15) |
/preview-browser |
Shortcut for a one-shot browser preview |
/preview-browser <path/to/file> |
Open a file preview in browser |
/preview-browser --watch (-w) |
Keep a browser preview updated after each completed assistant response |
/preview-browser --watch <path> |
Start or reopen a browser watcher for a file |
/preview-browser --list |
List active and starting browser preview watchers |
/preview-browser --stop <path> |
Stop one file watcher |
/preview-browser --stop --responses |
Stop the assistant-response watcher |
/preview-browser --stop --all |
Stop every browser preview watcher in this Pi session |
/preview-browser --stop |
Stop the watcher when zero or one is running; otherwise request a target |
/preview --pdf |
Export to PDF and open |
/preview-pdf |
Shortcut for --pdf |
/preview --pdf <path/to/file> |
Export a file to PDF |
/preview-clear-cache |
Clear rendered preview cache |
/preview --pick --browser |
Pick a response, open in browser |
Local images and Pandoc PDF figures are supported. In browser previews, a single-page PDF used with Markdown image syntax is rendered as a responsive page preview that links to the original PDF; multi-page PDFs retain the native browser viewer, as do single-page PDFs when page rendering is unavailable. Ordinary PDF links are unchanged, and PDF export continues to use the original vector file. File previews resolve relative paths against the previewed file’s directory; assistant-response previews resolve them against pi’s current working directory. Absolute paths, file:, http(s):, and data: image URLs work in one-shot previews. In watch mode, exact local media references—including parent-relative paths such as ../figures/plot.png—are rewritten to opaque authenticated routes. The general relative-resource route remains restricted beneath the preview resource directory; watch mode never exposes an arbitrary filesystem route and serves only allowlisted image types plus explicitly referenced PDF embeds.
Basic labelled-figure cross-references work consistently in terminal, browser, and PDF previews. Quarto syntax ({#fig-elephant} with @fig-elephant) and pandoc-crossref syntax ({#fig:elephant} with @fig:elephant) produce numbered captions and clickable Figure N references. This lightweight filter handles standalone captioned images and exact single references only; missing, duplicate, compound, or qualified references remain visibly unresolved. It does not emulate Quarto subfigures, project filters, chapter-aware numbering, or execution.
Bare <sup>…</sup> and <sub>…</sub> tags containing plain inline text are supported in terminal, browser, and PDF output, including affiliation markers such as Name<sup>1,2</sup>. Pandoc's native Name^1,2^ and H~2~O syntax also works. Code examples, escaped tags, and YAML front matter are preserved; attributed or nested HTML is not enabled by this compatibility support.
The short response-watch forms are /preview-browser -w and /preview -b -w. Use /preview-browser -w ./report.md or /preview -b -w --file ./report.md to watch a file. Quoted paths are supported; --file also makes reserved or dash-prefixed filenames explicit.
When pi is running inside cmux, browser previews automatically open as a focused cmux browser split in the caller’s workspace. If cmux is unavailable or declines the request, the normal system-browser opener is used instead.
The compact watch toolbar keeps Copy link visible beside Preview · current/total. Open Preview for the source filename, history navigation, and code wrapping. The panel overlays the document without moving it and stays open while you use Previous, Next, Latest, or wrapping controls. Use Tab to reach controls and Escape to close it, or click outside. Panel state is tab-local to each watcher; preserving it across page loads requires browser session storage. Touch targets are enlarged on touch devices.
The watch server removes its bootstrap token from the address bar after setting a browser-specific session cookie. Consequently, copying the cleaned address-bar URL into another browser is intentionally rejected. Use the watch toolbar’s Copy link control to request a fresh authenticated URL; it copies directly when browser permissions allow and otherwise selects the URL for manual copying.
Response watch mode is deliberately completion-level rather than token-streaming: it performs one canonical Pandoc render after each settled agent run, and only for a new or changed latest assistant response. File watch mode debounces source-file changes, hashes the file contents, and renders only genuine changes; temporary read/render failures leave the last good preview visible. Linked asset changes alone do not trigger a render. Both modes use token-protected servers bound to 127.0.0.1 and stop through the browser-watch lifecycle commands or when the session shuts down. One-shot browser previews remain unchanged and do not start this server.
Up to eight browser preview watchers can run per Pi session: multiple canonical file paths plus at most one assistant-response watcher. Each has an independent loopback server, authentication token, history, resource root, render state, and cleanup lifecycle. Repeating the same source reopens its existing watcher rather than duplicating it, and file and response watchers may coexist. Use --list to inspect them, a path or --responses to stop one, and --all to stop every watcher. Bare --stop retains the convenient old behaviour when zero or one watcher exists but makes no change when several are running.
Watch history starts with the initial preview and retains up to the latest 20 completed responses or successfully rendered file versions, subject to a 32 MiB aggregate HTML cap per watcher; the newest successful revision is always retained. Use the browser’s Back/Forward buttons or the Previous, Next, and Latest controls to move between them. Option/Alt+Left and Option/Alt+Right are shortcuts for Previous and Next when focus is outside an editable field. Auto-follow continues while the latest preview is open; when viewing an older one, the page stays put, shows New beside the revision count, and marks Latest (new) inside the panel as new responses or file versions arrive. Use /preview --pick --browser for assistant responses from before a response watcher started.
File watchers preserve your reading position across automatic updates, reloads, and navigation between versions of the same file. Restoration follows a nearby unchanged paragraph or heading, so edits above it do not move you elsewhere; if those anchors disappear, it falls back to relative scroll position. It accounts for delayed image/math layout, but stops adjusting as soon as you interact with the page. Positions are tab-local and isolated between watchers; if browser storage is unavailable, navigation still works without restoration. New assistant responses continue to start at the top, and browsing an older response remains undisturbed.
LLM-callable artifact export
The extension also registers a preview_export tool that pi can call directly. It renders Markdown/LaTeX content, a local file, or the latest assistant response to artifact files and returns their paths instead of requiring an interactive terminal/browser preview.
Supported formats:
pdf— writes a PDF file using the same pandoc + LaTeX path as/preview-pdfhtml— writes a standalone rendered HTML previewpng— writes one PNG per rendered preview page, appending-1-of-N,-2-of-N, etc. for multi-page output
The tool accepts optional outputPath, fontSizePx, resourcePath, and open arguments. By default it only writes files and returns paths, so another integration (for example Telegram or an upload/send-file tool) can deliver them.
Example user requests pi can satisfy with preview_export:
Make the last answer a PDF and send it to me.
Render ./report.md as HTML.
Export this markdown as PNG pages.
Programmatic helper exports
Other pi extensions can import the preview helpers directly:
import {
openPreview,
openPreviewInBrowser,
closeSharedPreviewBrowser,
} from "pi-markdown-preview";
openPreview(ctx, markdownOverride?, resourcePath?, isLatex?, fontSizePx?)opens the inline terminal preview.openPreviewInBrowser(ctx, markdownOverride?, resourcePath?, isLatex?, fontSizePx?)writes and opens the browser HTML preview.closeSharedPreviewBrowser()closes the shared headless Chromium instance used for terminal/PNG rendering. Importing extensions can call this from their ownsession_shutdownhandler; the bundled extension also calls it on pi shutdown/reload/switch.
Additional accepted argument aliases:
- Pick:
-p,pick - File:
-f - Browser target:
-b,browser,--external,external,--browser-native,native - Browser watch:
--watch,-w(assistant responses or one file),--stop - PDF target:
pdf - Terminal target:
terminal,--terminal(usually unnecessary because terminal is the default) - Font size:
--font-size <px>,--font-size=<px>,--font-size-px <px>,--fs <px>(10–24 px; terminal/browser previews; defaults: terminal 16, browser 15) - Help:
--help,-h,help - Note:
--pickand--filecannot be used together
PDF export uses Pandoc plus a LaTeX PDF engine (xelatex by default). The PDF preamble uses optional styling packages when they are available (including light code-block backgrounds via framed) and falls back to simpler output otherwise. Long-running PDF subprocesses time out after 120 seconds by default; set PI_MARKDOWN_PREVIEW_PDF_TIMEOUT_MS to adjust this.
To validate command docs against implementation:
npm run check:readme-commands
Keyboard shortcuts (terminal preview)
| Key | Action |
|---|---|
← / → |
Navigate pages |
r |
Refresh (re-render with current theme, keeping the wrapping choice) |
w |
Toggle code wrapping for this terminal preview (re-renders the images) |
o |
Open current preview in browser |
Esc |
Close preview |
Code wrapping and copying
Code blocks start unwrapped so diagrams and column-aligned output retain their layout. In a browser, long lines scroll within the code block. Terminal previews and PNG files are static images, so content beyond a block's width cannot be scrolled there.
- Terminal viewer: press
wto toggle wrapping for all code blocks. The help line showswrap code: off/on. The viewer keeps the previous images and setting if rendering fails;Escalso cancels an in-progress render. The choice lasts only until that viewer closes. This is an interactive key, not an argument:--watch/-wstill means browser watch mode. - Browser: use Wrap all code: off/on/mixed at the top, or hover over a code block to reveal Wrap / Unwrap on its upper-right border. Per-block buttons also appear when reached with
Tab, stay visible on touch devices, and do not add a row or cover the code text. The global button wraps all blocks when they are unwrapped or mixed, and unwraps all when they are wrapped; it clears individual exceptions. There is no bare-key browser shortcut. - Copy code: Copy sits beside the per-block wrapping button and copies plain code text, including indentation and logical line breaks, regardless of wrapping. It briefly shows Copied on success or Failed if clipboard access is unavailable; failures leave manual selection/copy available. Neither button labels nor visual line wrapping are included in the copied text.
- Watch mode: the global control is Wrap: off/on/mixed inside the Preview panel, rather than a separate button above the document. The global browser choice survives refreshes and revision navigation in the same tab using session storage. Per-block exceptions reset on each loaded revision. Independent watchers and fresh one-shot documents start unwrapped. If browser storage is disabled, the buttons still work, but the choice does not survive reloads.
Wrapping changes presentation, not code text or authored line breaks. Buttons stay outside the code and are omitted from terminal/PNG rendering and browser printing. Browser and terminal choices are independent; opening a separate browser preview starts unwrapped. PDF export uses the separate Pandoc/LaTeX pipeline and is unchanged. Standalone PNG exports also retain the default unwrapped layout rather than inheriting a viewer's temporary state.
Configuration
The LLM-callable preview_export tool is registered by default. To omit that tool while keeping all /preview commands available, set this before starting pi:
export PI_MARKDOWN_PREVIEW_REGISTER_EXPORT_TOOL=false
The values 0, false, no, and off disable registration (case-insensitive). Unset or any other value keeps the tool enabled.
Set PANDOC_PATH if pandoc is not on your PATH:
export PANDOC_PATH=/usr/local/bin/pandoc
Pandoc HTML conversion is bounded to 30 seconds, 50 MiB of standard output, and 5 MiB of diagnostics so a broken subprocess cannot stall or exhaust a preview session. Stopping a browser watcher cancels its active Pandoc process tree.
Set PANDOC_PDF_ENGINE to override the LaTeX engine used for PDF export (default: xelatex):
export PANDOC_PDF_ENGINE=xelatex
Set PUPPETEER_EXECUTABLE_PATH to override Chromium detection for terminal preview rendering:
export PUPPETEER_EXECUTABLE_PATH=/path/to/chromium
On Windows, standard system and per-user Chrome, Edge, Brave, and Chromium installations are detected from ProgramW6432, PROGRAMFILES, PROGRAMFILES(X86), and LOCALAPPDATA, with the common C: locations as fallbacks.
Terminal preview uses the known-good fixed screenshot path: 1200px Chromium viewport at device scale 2. Set PI_MARKDOWN_PREVIEW_DEVICE_SCALE_FACTOR only if you want to experiment with screenshot density manually (default: 2; range: 1–2.5):
export PI_MARKDOWN_PREVIEW_DEVICE_SCALE_FACTOR=2
Set MERMAID_CLI_PATH if mmdc is not on your PATH:
export MERMAID_CLI_PATH=/path/to/mmdc
Set MERMAID_PDF_THEME for PDF Mermaid rendering (default, forest, dark, neutral; default: default):
export MERMAID_PDF_THEME=default
Cache
Rendered previews are cached at ~/.pi/cache/markdown-preview/ by default.
When PI_CODING_AGENT_DIR is set, the cache is stored at $PI_CODING_AGENT_DIR/cache/markdown-preview/ instead.
Clear it with:
/preview-clear-cache
Or manually:
rm -rf "${PI_CODING_AGENT_DIR:-$HOME/.pi}/cache/markdown-preview/"
License
MIT

