View Office Documents in the Terminal¶
Open and preview Microsoft Office (.docx / .xlsx / .pptx), legacy binary (.doc / .xls / .ppt), and OpenDocument (.odt / .ods / .odp) files without leaving the terminal — no Microsoft Office, no GUI.
There is no single tool that views everything well, so this repo wires a small per-format + two-mode stack behind one dispatcher, view-office, and hooks it into the yazi file manager for inline previews.
TL;DR¶
| Format | Interactive (TUI / pager) | Quick preview (stdout) |
|---|---|---|
Word .docx |
doxx |
doxx --export ansi |
Excel .xlsx |
visidata (vd) |
markitdown \| glow |
PowerPoint .pptx |
markitdown \| glow (pager) |
markitdown \| glow; image-only decks → slide-1 thumbnail (chafa) |
Legacy .doc/.xls/.ppt, OpenDocument .odt/.ods/.odp, .rtf |
LibreOffice → OOXML twin → above | same, converted first |
$ view-office report.docx # interactive: rich TUI (doxx)
$ view-office --preview budget.xlsx # markdown/ANSI to stdout
$ view-office legacy-memo.doc # LibreOffice converts, then doxx
The view-office dispatcher¶
view-office (source: dot_dotfiles/bin/executable_view-office, deployed to ~/.dotfiles/bin/, on PATH) is a small bash script that dispatches by file extension:
view-office [--preview] [--width N] FILE
-p, --preview render to stdout (ANSI/markdown) instead of launching a TUI
-w, --width N wrap width for --preview (default: $COLUMNS or 100)
-h, --help show help
Two modes:
- Interactive (default) — launches the best full-screen viewer for the type:
doxxfor Word,visidatafor Excel, aglowpager for PowerPoint. - Preview (
--preview) — emits ANSI/markdown to stdout. This is the single source of truth that the yazi previewer (and any futuretv/fzfintegration) calls, so preview logic lives in exactly one place.
Legacy binary and OpenDocument formats are converted to their modern OOXML twin with LibreOffice headless into a temp dir, then re-dispatched to the handler above (e.g. .doc → .docx → doxx). Missing tools produce an actionable install hint and exit 2 — nothing fails silently.
Why bash, not the
uv run --scriptpattern used bypqsum/yth/mlf?view-officesits on yazi's preview hot-path (invoked on every file hover). uv/Python startup (~100–200 ms) would make previews feel laggy; bash dispatch is ~5 ms.
The tools¶
doxx — terminal-native Word viewer¶
doxx renders .docx directly in the terminal: bold/italic/headings, smart tables, LaTeX equations, and inline images (Kitty/iTerm2/Sixel). It also exports non-interactively — doxx FILE --export ansi|markdown|text|csv|json — which is what view-office --preview uses for Word.
- macOS:
brew install doxx(it's in homebrew-core — the README'sbgreenwell/tap/doxxis stale; that tap shipsxleak/lstr, not doxx). - Linux: GitHub release
.tar.xz(cargo-dist, static musl on x86_64) →~/.local/bin/doxx, installed by thedevtoolsansible role.
visidata — spreadsheet multitool¶
Already in the repo (see Data Viewers). vd file.xlsx opens a fast, vim-keyed TUI over the sheet. Used for interactive Excel; for previews, markitdown renders the sheet to a markdown table instead (visidata is TUI-only).
markitdown — Office → Markdown¶
Microsoft MarkItDown converts .docx/.xlsx/.pptx (and more) to Markdown, optimized for LLM/RAG pipelines. It is the universal preview engine for Excel and PowerPoint here, because pandoc cannot read .pptx or .xlsx (pandoc only writes pptx; it reads docx/odt/epub but not the spreadsheet/presentation formats). Installed as a uv tool (python_uv_tools).
Gotcha: the bare
markitdownpackage ships no format converters — they live in optional extras. This repo installsmarkitdown[docx,xlsx,pptx]; without the extras every Office file raisesMissingDependencyException. (Not[all]— that adds pdf/audio/youtube/azure bloat.)Image-only
.pptx: markitdown extracts text, so a deck whose slides are full-page images yields nothing butrefs. For--preview,view-office'srender_pptxdetects this by peeking at the slide XML with 7-Zip; if there's no text it extracts the first embedded slide image (ppt/media/image1.*) and renders it withchafa— ~0.5 s, versus tens of seconds for a full LibreOffice slide render on image-heavy decks. Text decks still get the markitdown path.
LibreOffice — legacy-format fallback¶
Only soffice --headless --convert-to can read the old binary formats (.doc/.xls/.ppt) and OpenDocument. Heavy (~hundreds of MB), so it's a fallback, installed by devtools (macOS cask / libreoffice-writer,calc,impress on Debian with recommends off). On macOS soffice is not on PATH; view-office resolves the .app bundle path itself. No-root Linux installs skip it and view-office prints an install hint for legacy files.
Yazi inline previews¶
Hovering an Office file in yazi renders a preview in the right pane. This is the repo's first yazi plugin integration, via the ya pkg manager and the piper.yazi plugin ("pipe any shell command as a previewer"):
# dot_config/yazi/yazi.toml
[plugin]
prepend_previewers = [
{ url = "*.docx", run = 'piper -- view-office --preview --width "$w" "$1"' },
# … .pptx .doc .xls .ppt .odt .ods .odp .rtf
]
.xlsxpreview is handled by duckdb.yazi, notview-office. Yazi routes spreadsheet hovers to the DuckDB table previewer (a nicer aligned grid). Theview-office --preview budget.xlsxCLI path (markitdown → glow) is unchanged — only Yazi's previewer wiring moved. Legacy.xls/ OpenDocument.odsstay on the view-office (LibreOffice) path here, since DuckDB can't read those.
piper.yazi passes $w (preview width) and $1 (file path); the command's stdout becomes the preview. The run string and view-office's --preview/--width interface are a contract — change one, change the other.
The ya pkg plugin mechanism¶
ya pkg is Yazi's plugin manager (ya is the CLI companion shipped with yazi). It clones a plugin, copies it into ~/.config/yazi/plugins/, and locks its version in ~/.config/yazi/package.toml.
yamust actually be installed. It ships in the same release archive as theyazibinary, but an install that copies out onlyyazileaves you with noya, no~/.config/yazi/plugins/, and therefore no Office previews at all — surfacing much later as a startup crash blaming whichever plugininit.luarequires first. Diagnose withya --version. See pitfalls/yazi-lua-runtime-failed-plugin-main-lua.md.
In this repo the lockfile is chezmoi-managed (dot_config/yazi/package.toml) and the plugins are materialized by a self-healing run-script:
| Piece | Path | Role |
|---|---|---|
| Lockfile (SSOT) | dot_config/yazi/package.toml |
pins yazi-rs/plugins:piper (rev + hash) |
| Installer | .chezmoiscripts/global/run_after_45_yazi_plugins.sh.tmpl |
runs ya pkg install when a plugin is missing or the lockfile changed |
| Previewer wiring | dot_config/yazi/yazi.toml [plugin] |
maps Office extensions → piper -- view-office … |
ya pkg install rewrites package.toml (normalizes it and drops comments), so the managed source lockfile is kept comment-free to match its canonical output — that's what avoids chezmoi drift. It is install-only by design (like the rest of the repo) — bump plugin versions with ya pkg upgrade (just upgrade-yazi-plugins), then copy the regenerated ~/.config/yazi/package.toml back into the source.
Add another yazi plugin:
ya pkg add <owner>/<repo>:<plugin> # installs + updates ~/.config/yazi/package.toml
cp ~/.config/yazi/package.toml "$(chezmoi source-path ~/.config/yazi/package.toml)"
Where things live¶
| Surface | Path |
|---|---|
| Dispatcher CLI | dot_dotfiles/bin/executable_view-office |
| Completions (zsh/bash) | dot_config/{zsh/tools,bash}/55_view_office_completion.* |
| doxx / LibreOffice install | dot_ansible/roles/devtools/tasks/main.yml |
| markitdown install | dot_ansible/roles/python_uv_tools/defaults/main.yml |
| yazi previewers | dot_config/yazi/yazi.toml |
| yazi plugin lockfile + installer | dot_config/yazi/package.toml, .chezmoiscripts/global/run_after_45_yazi_plugins.sh.tmpl |
| Upgrades | scripts/upgrade_tools.sh (just upgrade-yazi-plugins) |
Gotchas¶
markitdownneeds extras — see the box above.markitdown[docx,xlsx,pptx], not baremarkitdown.- pandoc ≠ Office reader — it cannot read
.pptx/.xlsx. Usemarkitdownfor those. (pandoc still powers the web reader for HTML.) - Uppercase extensions — the yazi
urlglobs are lowercase (*.docx); a file namedReport.DOCXwon't trigger the previewer (runningview-officedirectly still works — it lowercases internally). - LibreOffice cold start — legacy-format previews spin up a fresh isolated headless soffice profile on every hover (no caching — each invocation
mktemps its own profile), so.doc/.xls/.ppt/ODFpreviews take ~7 s each (measured; more on large files). yazi runs previewers asynchronously so the UI stays responsive, but the pane is blank until it lands. Modern OOXML formats don't touch LibreOffice and are instant. - PATH in yazi previews —
piperruns the command viash;view-officemust be on thePATHyazi inherited. Launch yazi from a configured shell so~/.dotfiles/binis present.
See also¶
- Data Viewers — VisiData wrappers.
- Web reader — read HTML pages as markdown (the pandoc/
glowsibling). - tool-managers.md — install-side index (doxx, markitdown, libreoffice,
ya pkg).