The React UI read every non-media attachment with file.text(). For a PDF
that decodes the binary bytes as UTF-8, so the model received raw
"%PDF ... stream ... endobj" noise instead of the document. The legacy
Alpine UI ran pdf.js; that step was not ported when the React UI
replaced it, but both file pickers still advertise .pdf.
Add a shared readAttachmentText helper that routes PDFs through
pdfjs-dist and reads other files as before. pdf.js and its worker load
on first use, so the main bundle does not grow. A PDF that cannot be
parsed or has no text layer (scanned, encrypted, damaged) is rejected
with a toast instead of being attached as an empty or garbage file.
Cover the chat and home paths with Playwright specs that build a real
PDF in the test.
Assisted-by: Claude Code:claude-sonnet-5-5 [playwright] [eslint]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
Co-authored-by: Ettore Di Giacinto <mudler@localai.io>