Isolated export module: clipboard, Markdown file, and Word doc from any chat response
`b4345deb` adds `frontend/src/lib/exportDraft.ts` - 677 lines of pure export helpers covering clipboard writes, filename sanitization, Markdown wrapping, and Markdown-to-docx conversion. A 317-line test suite ships alongside it. The module is shared by both chat views with no side effects on the chat state.
The three export paths work differently at the implementation level. Copy Draft calls navigator.clipboard.write with two clipboard items simultaneously: a text/html payload (converted from Markdown so Word receives structured content) and a text/plain Markdown payload as fallback. Download Markdown generates a safe slugified filename from the task title and current date, prepends a metadata header with matter name, task, date, and the attorney-review reminder, and triggers a blob download. Download Word Draft runs the Markdown through a converter that uses the docx package already in the project - headings map to HeadingLevel constants, bullets become numbered or bullet lists depending on GFM syntax, and simple tables are preserved.
The test suite in exportDraft.test.ts covers filename sanitization (including Unicode and special characters), header content, the no-scrub invariant, and credential-leak avoidance. The credential tests check the wrapper's additions only - the wrapper preserves AI content verbatim and a separate test confirms that legal prose mentioning "API key" or "Bearer token" survives unchanged.
347a4240 added a 200-character gating threshold. The export row renders only when the stripped export content is at least 200 characters long. The comment in the code is explicit: the threshold is conservative - it would rather show buttons on a borderline-short answer than hide them on a real memo. Short conversational replies ("Done.", "I need more information.") don't get the export row.
The per-matter chat threads task title lookup through a backward scan of the message array to find the most recent prior user message that carried a workflow title. That context is passed as taskTitle to the export functions so filenames and headers name the task correctly.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?