From 20cab5b104b5f68e80ce0f1fda03b63147565eeb Mon Sep 17 00:00:00 2001 From: nick-delirium Date: Tue, 6 May 2025 15:22:23 +0200 Subject: [PATCH] ui: export as pdf, copy text contents of a message --- .../app/components/Kai/components/ChatMsg.tsx | 53 ++++++++++++++++--- .../components/ui/CopyButton/CopyButton.js | 18 ++++++- frontend/app/styles/main.css | 13 +++++ 3 files changed, 75 insertions(+), 9 deletions(-) diff --git a/frontend/app/components/Kai/components/ChatMsg.tsx b/frontend/app/components/Kai/components/ChatMsg.tsx index 1921a0b80..72a6e664e 100644 --- a/frontend/app/components/Kai/components/ChatMsg.tsx +++ b/frontend/app/components/Kai/components/ChatMsg.tsx @@ -1,10 +1,12 @@ import React from 'react'; -import { Icon } from 'UI'; +import { Icon, CopyButton } from 'UI'; import cn from 'classnames'; import Markdown from 'react-markdown'; import remarkGfm from 'remark-gfm' -import { Loader, ThumbsUp, ThumbsDown, ListRestart } from 'lucide-react'; +import { Loader, ThumbsUp, ThumbsDown, ListRestart, FileDown } from 'lucide-react'; +import { Button, Tooltip } from 'antd'; import { kaiStore } from '../KaiStore'; +import { toast } from 'react-toastify'; export function ChatMsg({ text, @@ -19,12 +21,39 @@ export function ChatMsg({ userName?: string; isLast?: boolean; }) { + const [isProcessing, setIsProcessing] = React.useState(false); + const bodyRef = React.useRef(null); const onRetry = () => { kaiStore.editMessage(text) } const onFeedback = (feedback: 'like' | 'dislike', messageId: string) => { kaiStore.sendMsgFeedback(feedback, messageId); }; + + const onExport = () => { + setIsProcessing(true); + import('jsPDF').then(({ jsPDF }) => { + const doc = new jsPDF(); + + doc.html(bodyRef.current, { + callback: function(doc) { + doc.save('document.pdf'); + }, + margin: [10, 10, 10, 10], + x: 0, + y: 0, + width: 190, // Target width + windowWidth: 675 // Window width for rendering + }); + }) + .catch(e => { + console.error('Error exporting message:', e); + toast.error('Failed to export message'); + }) + .finally(() => { + setIsProcessing(false); + }); + } return (
)}
-
+
{text}
{isUser ? ( @@ -67,12 +96,16 @@ export function ChatMsg({ ) : null ) : (
- onFeedback('like', messageId)}> + onFeedback('like', messageId)}> - onFeedback('dislike', messageId)}> + onFeedback('dislike', messageId)}> + + + +
)}
@@ -83,14 +116,18 @@ export function ChatMsg({ function IconButton({ children, onClick, + tooltip, + processing, }: { children: React.ReactNode; onClick?: () => void; + tooltip?: string; + processing?: boolean; }) { return ( -
- {children} -
+ +