diff --git a/frontend/src/components/workspace/messages/markdown-content.tsx b/frontend/src/components/workspace/messages/markdown-content.tsx index 2c7026b0..e2dab4fb 100644 --- a/frontend/src/components/workspace/messages/markdown-content.tsx +++ b/frontend/src/components/workspace/messages/markdown-content.tsx @@ -12,6 +12,12 @@ import { MessageResponse, type MessageResponseProps, } from "@/components/ai-elements/message"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { useI18n } from "@/core/i18n/hooks"; import { streamdownPlugins } from "@/core/streamdown"; import { CopyButton } from "@/components/workspace/copy-button"; @@ -58,9 +64,22 @@ function toMarkdownTable(data: TableData): string { return [headerLine, dividerLine, ...rowLines].join("\n"); } -function downloadMarkdownFile(content: string, filename: string) { +function escapeCsvCell(cell: string): string { + const normalized = cell.replace(/\r\n/g, "\n"); + if (!/["\n,]/.test(normalized)) return normalized; + return `"${normalized.replace(/"/g, '""')}"`; +} + +function toCsvTable(data: TableData): string { + if (data.headers.length === 0) return ""; + const headerLine = data.headers.map(escapeCsvCell).join(","); + const rowLines = data.rows.map((row) => row.map(escapeCsvCell).join(",")); + return [headerLine, ...rowLines].join("\r\n"); +} + +function downloadTextFile(content: string, filename: string, mimeType: string) { const blob = new Blob(["\uFEFF", content], { - type: "text/markdown;charset=utf-8", + type: mimeType, }); const url = URL.createObjectURL(blob); const anchor = document.createElement("a"); @@ -99,13 +118,23 @@ export function MarkdownTable({ })(); const handleDownload = useCallback(() => { + const table = tableRef.current; + if (!table) return; + const data = parseTableData(table); + if (!data) return; + const csv = toCsvTable(data); + if (!csv) return; + downloadTextFile(csv, "table.csv", "text/csv;charset=utf-8"); + }, []); + + const handleDownloadMarkdown = useCallback(() => { const table = tableRef.current; if (!table) return; const data = parseTableData(table); if (!data) return; const markdown = toMarkdownTable(data); if (!markdown) return; - downloadMarkdownFile(markdown, "table.md"); + downloadTextFile(markdown, "table.md", "text/markdown;charset=utf-8"); }, []); return ( @@ -115,15 +144,24 @@ export function MarkdownTable({ >
- - - + + + + + + + + CSV + + Markdown + + +