DOCX → editable document (feasibility & plan)
Goal. Upload a .docx and turn it into an in-app editable document — the same
shape as the existing "Write document" flow: DOCX → new text/html document → edit in
the rich-text editor → Finalize to PDF makes it a real record.
Verdict: very feasible, ~a day of work. Every hard part already exists in the
stack; the only missing piece is one DOCX→HTML conversion endpoint and a "convert"
action wiring it to the editor.
Why it slots in cleanly
The Write-document flow already does everything downstream of the conversion:
| Piece | Status |
|---|---|
| Rich-text editor | Live — TipTap/ProseMirror (TextEditorView.tsx) with StarterKit + tables, and @tiptap/extension-image is already a dependency |
| Editable storage format | Live — documents whose current version is text/html, saved through the same AddVersion as uploads |
| Editable → record | Live — Finalize renders the HTML to PDF via gotenberg (letterhead supported) and publishes as a version/new doc |
| DOCX parsing in the stack | Partially — the extract sidecar already uses python-docx for text extraction (formatting lost) |
Conversion options considered
mammoth(Python) in the extract sidecar — RECOMMENDED. Purpose-built
DOCX→semantic HTML: headings, lists, tables, bold/italic, links, images
(data URIs). Its output vocabulary maps almost 1:1 onto what TipTap accepts, so the
round-trip is stable. One pip dependency (mammoth==1.12.0), no system packages,
and the sidecar is already our document-parsing home.- LibreOffice
--convert-to html. Gotenberg's API only outputs PDF, so this needs a
new LO container or shell-outs; the HTML it emits is presentational soup (inline
styles, spans) that TipTap would mangle on first save. Rejected. mammoth.jsclient-side in the browser. Zero backend work, but conversion then
exists only in the web app (mobile/API miss out) and skips server-side
sanitization. Rejected as primary; fine as a fallback idea.
Proposed pipeline
- Sidecar:
POST /docx/html(multipart file →{html}) using mammoth.
~30 lines next to the existing extractors. ⚠️ Remember: the extract sidecar must be
rebuilt separately fromupdate.sh. - Backend:
POST /documents/{docID}/make-editable(ReadWrite access, PDF-style
gate on the mime being docx): reads the version bytes → sidecar → sanitize the
HTML server-side (allowlist; TipTap re-parsing is a de-facto filter but must not
be the only one sincetext/htmlversions are also rendered by gotenberg for
previews) → create a new document titled "(editable)" with a
text/htmlfirst version, same folder/classification — exactly what "Write
document" produces. (New document, not a new version, so the original DOCX record
stays untouched — mirrors the Finalize "new document" option.) - FE: a "Convert to editable document" action on the detail view for docx
versions (and later an upload-modal checkbox); on success navigate straight into
/documents/d/{id}/edit.
Fidelity expectations (to set with users)
mammoth deliberately keeps meaning and drops layout: fonts, colors, columns,
headers/footers, text boxes and tracked changes do not survive; footnotes partially.
Images arrive as base64 data URIs (watch document size; we may strip or cap them in
v1). This is excellent for letter/memo/report-grade documents — which is what the
Write flow targets — and NOT a pixel-faithful contract editor. The banner on the
converted doc should say so.
Open decisions for v1
- Images: keep (data URIs), cap size, or strip with a notice. Suggest cap ~2 MB total.
- Where the action lives first: detail-view action (suggested) vs upload-time option.
- Whether
.doc/.odtjoin later via LibreOffice→docx pre-step (defer).