easterbrooka/mike: txt, eml, and xlsx uploads added with server-side parsing and inline viewers

easterbrooka extended the document intake beyond pdf/docx to cover plain text, raw email, and spreadsheets - wiring each new type through the server-side parser, the LLM read path, and a new inline viewer component.

intakediscovery

The original allowlist was enforced in three places: the frontend accept= attribute, two backend routes, and the LLM's read_document dispatch. Adding new types meant touching all of them, and easterbrooka did. Parsing is server-side throughout - mailparser handles .eml, exceljs handles .xlsx, neither enters the browser bundle. The display endpoint returns pre-parsed JSON under vendor content types for eml and xlsx, and plain text for txt. Three new viewer components render each format inline. A shared upload helper centralizes the supported-types list, rejection message, and storage content type so those three values can't drift apart across the codebase.

Two explicit size caps are worth noting. Txt and eml are truncated at 200k characters; xlsx at 1000 rows by 50 columns per sheet. Truncation notices surface in both the UI and the LLM rendering. The intent is to prevent a multi-megabyte log file or an oversized spreadsheet from blowing out the context window. Legacy .xls was deliberately left out - the only npm library that reads the older binary format carries prototype-pollution CVEs, and the author's recommendation is to re-save as xlsx.

No schema changes. The documents table stored file type as a free string already, so existing rows are unaffected. No new env vars. 21 tests cover the three new extractors.

So what Worth a look if your users upload anything other than PDFs and Word docs. The size caps are sensible defaults, though you may want to adjust the xlsx limit depending on the spreadsheet complexity your workflows expect. The shared upload helper is a cleaner design than what upstream ships - worth pulling for that alone if your fork has grown its own type list.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?