feat(viewer): preview word documents (.docx/.doc/.odt/.rtf) as PDF
Nightly Build / build (push) Canceled after 10m54s

The file viewer converts word-processor documents to PDF server-side via
LibreOffice (skald_core::docx::DocxConverter), mirroring the LaTeX pipeline
but content-hash cached: the format is self-contained, so there is no
dependency graph and the file watcher needs no expansion. Container-only
documents are shuttled out and converted on the host. With no LibreOffice
installed the viewer says so and falls back to download-only. Downloads
still save the original document, not the preview PDF.
This commit is contained in:
Daniele
2026-09-08 16:30:13 +01:00
parent 4ea932ef54
commit 027d815b66
17 changed files with 657 additions and 17 deletions
+5 -4
View File
@@ -14,13 +14,14 @@ The header shows the file's path exactly as your tools spell it (`shared/recipes
| SVG | rendered in an isolated frame — scripts inside it never run |
| PDF | drawn by the app itself, so it looks and scrolls the same in every browser and on the phone |
| LaTeX (`.tex`) | **compiled to PDF on the server** and shown as the document |
| Word documents (`.docx`, `.doc`, `.odt`, `.rtf`) | **converted to PDF on the server** (needs LibreOffice installed there) and shown as the document |
| HTML | rendered live in an isolated frame; the toggle in the header switches to the source |
| Anything else | not displayed — the file can still be downloaded |
Two consequences worth knowing:
- **Always give `show_file_to_user` the `.tex`, never a `.pdf` you built from it.** The `.tex` is recompiled and the view follows its dependencies — `\input` fragments, styles, images — so it stays current. A raw `.pdf` is served as bytes: it is never recompiled and the user ends up looking at a stale render.
- **A compile that fails is not a dead end.** The viewer shows the source instead, with the actual error block foldable at the top. That error is worth reading if they ask why "the document is not showing" — it usually names a line.
- **Always give `show_file_to_user` the `.tex`, never a `.pdf` you built from it.** The `.tex` is recompiled and the view follows its dependencies — `\input` fragments, styles, images — so it stays current. A raw `.pdf` is served as bytes: it is never recompiled and the user ends up looking at a stale render. The same applies to word documents: give the `.docx`, not a PDF exported from it — the viewer converts it, and re-converts it when the file changes.
- **A compile that fails is not a dead end.** The viewer shows the source instead, with the actual error block foldable at the top. That error is worth reading if they ask why "the document is not showing" — it usually names a line. (For a word document there is no readable source to show, so a failed conversion explains itself in the same foldable block over the download state.)
## It is live
@@ -52,7 +53,7 @@ Files inside a folder that is under version control — in practice, project fol
## Download
The download button saves the file with its real name. For a `.tex` it downloads the **compiled PDF**, not the source — that is usually what someone asking to "send me the document" wants; if they want the source itself, they want the `.tex`, and it is worth checking which.
The download button saves the file with its real name. For a `.tex` it downloads the **compiled PDF**, not the source — that is usually what someone asking to "send me the document" wants; if they want the source itself, they want the `.tex`, and it is worth checking which. A word document instead downloads as **the original file** (the `.docx`, `.odt`…): unlike a `.tex` source it is the editable document itself, and the PDF on screen is only the preview.
## What the viewer tells you
@@ -66,7 +67,7 @@ If the eye is off, none of that arrives, and you genuinely do not know what they
- *"It says the file changed while I was editing."* — something else wrote to it. Three buttons on the banner; copy-then-reload loses nothing.
- *"The PDF is wrong / old."* — if there is a `.tex` beside it, they are looking at a stale build. Open the `.tex` instead: it recompiles.
- *"Where is the old version?"* — the clock, if the file is in a project folder. Otherwise there is no history to show, and the honest answer is that this file is not versioned.
- *"It won't show the file."* — a kind the viewer cannot render (an archive, an office document, an unknown binary) shows the download instead. That is the whole story; there is no plugin to install.
- *"It won't show the file."* — a kind the viewer cannot render (an archive, an unknown binary) shows the download instead, and that is the whole story. An office document showing only the download means the server has **no LibreOffice installed**: the admin installing it turns the preview on — nothing to change in the app, and the foldable block on the page says exactly this.
- *"Show me that file."* — `show_file_to_user`, one file per call, on any path in their own workspace including a memory note. It must already exist.
## Related