From c89afc2b3820dad7a8b566868454d9c9033898a9 Mon Sep 17 00:00:00 2001 From: PMullz Date: Sat, 19 Sep 2026 11:42:31 -0400 Subject: [PATCH 01/28] docs(plans): conversation export design Design for #55: an opt-in header menu that copies the conversation as Markdown or downloads it as Markdown or JSON, entirely in the browser. Records the decisions that depart from or go beyond the issue text: opt-in rather than on by default (embeds load the floating @1 CDN tag), a new optional ChatMessage.createdAt so timestamps exist to export, and the protections that stop one message damaging the rest of a rendered transcript (unclosed code fences, single-newline line breaks, HTML block openers). Co-Authored-By: Claude Fable 5.1 --- .../2026-09-19-conversation-export-design.md | 433 ++++++++++++++++++ 1 file changed, 433 insertions(+) create mode 100644 docs/plans/2026-09-19-conversation-export-design.md diff --git a/docs/plans/2026-09-19-conversation-export-design.md b/docs/plans/2026-09-19-conversation-export-design.md new file mode 100644 index 0000000..9f73cd2 --- /dev/null +++ b/docs/plans/2026-09-19-conversation-export-design.md @@ -0,0 +1,433 @@ +# Conversation export: design + +Issue: [#55](https://github.com/PMDevSolutions/Claudius/issues/55) +Date: 2026-09-19 +Status: approved, implemented in the same PR as this document + +## Summary + +Add an opt-in `conversationExport` option to the widget. It puts an overflow +menu in the chat header with three actions: copy the conversation to the +clipboard as Markdown, download it as a Markdown file, and download it as a +JSON file. Everything happens in the browser. Nothing is sent to the worker. + +Exporting timestamps requires recording them, so `ChatMessage` gains an +optional `createdAt`. That is the only change outside the new feature. There +is no worker, API, or wire-format change. + +## Facts that constrain the design + +These were checked against the code, MDN, and a local Node 24 (ICU 78.3) on +2026-09-19. Several shaped the design more than the issue text did. + +1. **Messages carry no timestamp.** `ChatMessage` has `id`, `role`, `content`, + `sources`, `attachments`, and `toolUses`. The issue requires timestamps in + the Markdown export, so they have to be recorded first. +2. **The worker ignores unknown message fields, and always has.** Today + `validateMessages` returns `{ role, content, attachments? }`. The very + first version of `worker/src/chat.ts` (b53d64a) already mapped each message + to `{ role, content }`. The widget has always sent `id`, and later + `sources` and `toolUses`, which every deployed worker discards. A new + optional field is therefore safe even for a worker that is never + redeployed. +3. **Embeds load a floating tag.** The quick start documents + `cdn.jsdelivr.net/gh/PMDevSolutions/Claudius@1/cdn/claudius.iife.js`. A + minor release reaches every live site with no action by its owner, so a + feature that is on by default appears everywhere at once. +4. **The widget renders very little Markdown.** `ChatMessage` handles bold, + italic, and URLs, and turns every newline into a `
`. Everything else, + code fences included, is shown as literal text. So "preserves code blocks" + cannot mean converting anything: it means not damaging text that is + already Markdown. It also means a single newline is a visible line break + on screen, while CommonMark treats it as a space. The default system + prompt tells the bot to "ALWAYS use line breaks between sentences", so + this is the normal case, not an edge case. +5. **Partial replies are kept.** When a visitor stops a stream, or a stream + breaks after text has rendered, `useChat` keeps the partial reply as a + settled message. A reply cut off inside a code block leaves an unclosed + fence in the history. +6. **Escape closes the chat from a document-level listener** + (`ChatWindow`), and the focus trap treats every enabled `button` as + focusable, whatever its `tabindex`. +7. **Inline attachment bytes exist only in memory.** A `ChatAttachment` holds + base64 `data` until the worker stores the file. `stripAttachmentData` + removes it before anything is persisted. +8. **Clipboard writes are restricted.** MDN: writing "can only be done in a + secure context"; in Firefox and Safari "writing requires transient + activation"; and `clipboard-write` "must be allowed for `