From 926971324bc987e7de4c03849885be9b32800b73 Mon Sep 17 00:00:00 2001 From: Nicolas Varrot Date: Sat, 14 Feb 2026 14:54:52 +0000 Subject: [PATCH] feat: export conversation as Markdown download --- src/components/Chat.tsx | 22 +++++++++- src/lib/exportConversation.ts | 79 +++++++++++++++++++++++++++++++++++ src/lib/i18n.ts | 2 + 3 files changed, 101 insertions(+), 2 deletions(-) create mode 100644 src/lib/exportConversation.ts diff --git a/src/components/Chat.tsx b/src/components/Chat.tsx index cba8e27..da126c7 100644 --- a/src/components/Chat.tsx +++ b/src/components/Chat.tsx @@ -3,12 +3,13 @@ import { ChatMessageComponent } from './ChatMessage'; import { ChatInput } from './ChatInput'; import { TypingIndicator } from './TypingIndicator'; import type { ChatMessage, ConnectionStatus } from '../types'; -import { Bot, ArrowDown, Loader2, ChevronsDownUp, ChevronsUpDown, Sparkles, Bookmark } from 'lucide-react'; +import { Bot, ArrowDown, Loader2, ChevronsDownUp, ChevronsUpDown, Sparkles, Bookmark, Download } from 'lucide-react'; import { MessageSearch } from './MessageSearch'; import { useT } from '../hooks/useLocale'; import { getLocale, type TranslationKey } from '../lib/i18n'; import { useToolCollapse } from '../hooks/useToolCollapse'; import { useBookmarks } from '../hooks/useBookmarks'; +import { exportAsMarkdown, downloadFile } from '../lib/exportConversation'; interface Props { messages: ChatMessage[]; @@ -208,6 +209,13 @@ export function Chat({ messages, isGenerating, isLoadingHistory, status, session const [showBookmarks, setShowBookmarks] = useState(false); const hasToolCalls = useMemo(() => messages.some(m => m.blocks.some(b => b.type === 'tool_use' || b.type === 'tool_result')), [messages]); + const handleExport = useCallback(() => { + const label = sessionKey?.replace(/^agent:[^:]+:/, '') || 'conversation'; + const md = exportAsMarkdown(messages, label); + const safeLabel = label.replace(/[^a-zA-Z0-9_-]/g, '_').slice(0, 40); + downloadFile(md, `${safeLabel}-${new Date().toISOString().slice(0, 10)}.md`); + }, [messages, sessionKey]); + // Message search const [searchOpen, setSearchOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(''); @@ -358,7 +366,7 @@ export function Chat({ messages, isGenerating, isLoadingHistory, status, session )} {/* Floating action buttons — sticky to bottom of scroll area */} - {(hasToolCalls || showScrollBtn || newMessageCount > 0) && ( + {(hasToolCalls || messages.length > 0 || showScrollBtn || newMessageCount > 0) && (
{hasToolCalls && ( @@ -382,6 +390,16 @@ export function Chat({ messages, isGenerating, isLoadingHistory, status, session {sessionBookmarks.length} )} + {messages.length > 0 && ( + + )} {(showScrollBtn || newMessageCount > 0) && (