A Neovim plugin that renders a live Typst preview directly into a terminal window via the Kitty graphics protocol. No browser, no external viewer, no file watcher — the preview lives inside the Neovim split that already shows the source file.
v0.1.0 — single-buffer preview works end-to-end on Ghostty 1.3+ with the
default fit-to-width mode. See docs/ARCHITECTURE.md for the system shape
and HANDOFF.md for the original design framing.
- Live preview inside Neovim, rendered by the host terminal — no extra window.
- Per-page pixmap-hash dedup: only changed pages are re-uploaded.
- Cursor → page autoscroll via
tinymist-preview'schangeCursorPosition, anchored to the source window's vertical ratio. - Mouse-click → source jump (Kitty protocol cell → document position →
winrestview). - Outline sidebar and
compileStatus→ statusline integration. - Fit modes:
width(default) andwithin; quality-multiplierdpion top. - Multi-page documents:
<leader>pn,<leader>pp,<leader>pg{N}. :checkhealth typst_term_preview.
- Neovim 0.10+.
- A terminal that implements the Kitty graphics protocol and the Unicode placeholder extension. Verified: Ghostty 1.3+. Kitty itself, WezTerm, and foot should work but are untested.
- Rust toolchain (stable) to build the
typst-term-previewserver binary. - tmux is unsupported unless built with passthrough.
{
"Y-jiji/tinymist-kitty.nvim",
ft = "typst",
build = "cargo build --release --bin typst-term-preview",
opts = {
binary = vim.fn.stdpath("data") .. "/lazy/tinymist-kitty.nvim/target/release/typst-term-preview",
auto_start = true,
},
}use {
"Y-jiji/tinymist-kitty.nvim",
ft = "typst",
run = "cargo build --release --bin typst-term-preview",
config = function()
require("typst_term_preview").setup({
binary = vim.fn.stdpath("data") .. "/site/pack/packer/start/tinymist-kitty.nvim/target/release/typst-term-preview",
auto_start = true,
})
end,
}git clone https://github.com/Y-jiji/tinymist-kitty.nvim.git \
~/.local/share/nvim/site/pack/plugins/start/tinymist-kitty.nvim
cd ~/.local/share/nvim/site/pack/plugins/start/tinymist-kitty.nvim
cargo build --release --bin typst-term-previewThen put the release binary on $PATH or point binary = "/abs/path" in
setup().
require("typst_term_preview").setup({
binary = "typst-term-preview", -- or absolute path
auto_start = true,
dpi = 96,
fit_mode = "width",
})Open a .typ file and the preview split appears. If auto_start = false,
run :TypstTermPreview to launch the session.
The server probes terminal cell size via CSI 16t at startup, but Neovim owns the TTY in raw mode and usually consumes the response first — the 9×18 fallback then kicks in and the preview looks slightly off. Measure the real value from a plain shell:
./scripts/measure-cell-px.sh…and plug it into setup:
require("typst_term_preview").setup({
cell_px = { w = 9, h = 17 }, -- whatever the script reports
})| key | default | effect |
|---|---|---|
binary |
"typst-term-preview" |
absolute path or $PATH lookup |
auto_start |
false |
spawn on FileType typst |
dpi |
96 |
quality multiplier on top of fit scale |
cell_px |
nil |
{ w, h } override of the CSI 16t result |
fit_mode |
"width" |
"width" or "within" |
scroll_anchor |
0.2 |
fallback when dynamic source-anchor is unavailable |
emit_debounce_ms |
250 |
idle window before flushing queued frame uploads |
| command | effect |
|---|---|
:TypstTermPreview |
start a preview session for the current buffer |
:TypstTermPreviewStop |
stop the session |
:TypstTermPreviewToggle |
toggle on/off |
:TypstTermPreviewUpdate |
force an immediate re-render |
:checkhealth typst_term_preview |
diagnose terminal protocol support and binary path |
Diagnostic toggles for bisecting flicker: :TypstFlickerToggleEmit,
:TypstFlickerToggleRebuild, :TypstFlickerToggleScroll,
:TypstFlickerToggleOverlay, :TypstFlickerLogToggle,
:TypstFlickerStatus.
| keys | action |
|---|---|
<leader>pn |
next page |
<leader>pp |
previous page |
<count><leader>pg |
jump to page <count> |
<leader>p+ |
zoom in |
<leader>p- |
zoom out |
<leader>p0 |
reset zoom |
Three processes: the host terminal, Neovim + the Lua plugin, and the Rust
typst-term-preview server. The plugin spawns the server and connects over
an AF_UNIX socket carrying a newline-delimited JSON control plane (mirroring
tinymist-preview's ControlPlaneMessage). The server holds a
tinymist-world snapshot, rasterizes pages with tiny-skia at a
viewport-driven scale, PNG-encodes them, and emits frame events. The plugin
unpacks each frame into a Kitty a=t upload + a=p,U=1 virtual placement
keyed by U+10EEEE placeholder cells in the [Preview] buffer — so Neovim's
own redraw cycle keeps the image stable on screen. Full details:
docs/ARCHITECTURE.md.
- Sixel or non-Kitty-protocol terminals.
- Non-typst document types.
- Remote / SSH workflows.
- Multi-document preview sessions.
- Myriad-Dreamin/tinymist — the compile/preview library this project adapts to a terminal sink.
- chomosuke/typst-preview.nvim — reference architecture for the browser path.
- Kitty graphics protocol and the Unicode placeholder extension.
See LICENSE (if present) or the source headers.