Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
c89afc2
docs(plans): conversation export design
PAMulligan Sep 19, 2026
43de4ec
docs(plans): conversation export implementation plan
PAMulligan Sep 19, 2026
12d8bc0
docs(plans): correct three defects found in the pre-flight scan
PAMulligan Sep 19, 2026
c6049d2
feat(widget): record createdAt on chat messages
PAMulligan Sep 19, 2026
e5cc07f
feat(widget): message text protections for Markdown export
PAMulligan Sep 19, 2026
f012dc4
feat(widget): Markdown and JSON conversation serializers
PAMulligan Sep 19, 2026
e001999
fix(widget): keep code intact when a message ends inside an open fence
PAMulligan Sep 20, 2026
88cd147
docs(plans): trailing-blank-line trimming applies only outside code
PAMulligan Sep 20, 2026
c64b453
feat(widget): clipboard and download helpers
PAMulligan Sep 20, 2026
95bb6c6
feat(widget): conversation export strings in en, es, fr, de
PAMulligan Sep 20, 2026
ae759d0
feat(widget): accessible header menu
PAMulligan Sep 20, 2026
62ca757
feat(widget): useConversationExport hook
PAMulligan Sep 20, 2026
368319b
feat(widget): conversationExport option and header menu wiring
PAMulligan Sep 20, 2026
e91b492
feat(widget): conversationExport in ClaudiusConfig and <claudius-chat>
PAMulligan Sep 20, 2026
b6e4347
feat(scripts): conversationExport in client config schema, validation…
PAMulligan Sep 20, 2026
a3e5d03
test(widget): e2e for conversation export
PAMulligan Sep 20, 2026
6e3c207
test(widget): wait for the export menu items before asserting on them
PAMulligan Sep 20, 2026
f1ac295
docs(plans): the export e2e spec waits for its menu items
PAMulligan Sep 20, 2026
69a2931
docs: conversation export guide
PAMulligan Sep 20, 2026
99c3e2f
chore(widget): raise bundle budgets for conversation export
PAMulligan Sep 20, 2026
75f02da
refactor(widget): harden the transcript serializer
PAMulligan Sep 20, 2026
c1fbf00
refactor(widget): keep a blur with no relatedTarget from closing the …
PAMulligan Sep 20, 2026
a6bb805
refactor(widget): let the clipboard fallback fail without throwing
PAMulligan Sep 20, 2026
c776849
refactor(widget): prove what the mounted guard actually prevents
PAMulligan Sep 20, 2026
59b6e85
refactor(widget): accept only true for the conversation-export attribute
PAMulligan Sep 20, 2026
9301008
refactor(widget): shorten the German copy-failure message
PAMulligan Sep 20, 2026
552fe99
docs: correct the fence and attribute rules the code now follows
PAMulligan Sep 20, 2026
40308b4
docs(plans): the plan quotes the German copy-failure string as shipped
PAMulligan Sep 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,12 @@ pnpm test # Run tests
| `VoiceLevel` | Voice-activity indicator shown inside the input while listening |
| `MessageSpeechControls` | Read aloud / pause / resume / stop buttons under assistant replies |
| `AttachmentPreview` | Image thumbnail / file chip for pending and sent attachments |
| `HeaderMenu` | Generic accessible overflow menu in the chat header; hosts the export actions |

### useChat Hook

Manages chat state:
- `messages` - Array of chat messages
- `messages` - Array of chat messages, each stamped with an ISO `createdAt` when it enters the conversation
- `isLoading` - Loading state during API calls
- `isStreaming` - True while an assistant reply is streaming in
- `streamingMessageId` - Id of the message currently receiving tokens
Expand Down Expand Up @@ -195,6 +196,28 @@ the worker, but browsers recognize speech on their vendor's servers by
default. Do not describe it as on-device. Docs: configuration/voice.md;
design: docs/plans/2026-09-18-voice-input-tts-design.md.

### Conversation Export

Widget-only and opt-in (`conversationExport` prop / `ClaudiusConfig` key /
`<claudius-chat conversation-export>` / `widget.conversationExport` in client
configs). It fails closed: only the literal `true` enables it, and nothing
renders when it is off. A `HeaderMenu` in the chat header offers Copy as
Markdown, Download as Markdown, and Download as JSON, all in the browser with
no request to the worker.

Pure serializers live in `widget/src/utils/exportConversation.ts`
(`protectMessageText`, `conversationToMarkdown`, `conversationToJson`,
`exportFilename`); clipboard and download side effects in
`widget/src/utils/saveText.ts`; `useConversationExport` (owned by `ChatWindow`)
turns messages into menu items plus a transient status. Markdown keeps message
text as written but closes unclosed code fences, turns single newlines into
hard breaks outside code, and escapes a line-leading `<`. JSON is the
persisted `ChatMessage[]`, never with inline attachment bytes.

Never assert on raw `Intl` output in tests: CI's Node 20 emits U+202F before
"PM" and local Node 24 does not. Docs: configuration/conversation-export.md;
design: docs/plans/2026-09-19-conversation-export-design.md.

### Chat Request/Response

```typescript
Expand Down
4 changes: 4 additions & 0 deletions clients/_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,10 @@
}
}
]
},
"conversationExport": {
"type": "boolean",
"description": "Let visitors copy the conversation as Markdown, or download it as Markdown or JSON, from a menu in the chat header. Off by default. Nothing is sent to the worker."
}
}
},
Expand Down
465 changes: 465 additions & 0 deletions docs/plans/2026-09-19-conversation-export-design.md

Large diffs are not rendered by default.

Loading
Loading