|
1 | | -import { useMemo } from "react"; |
| 1 | +import { useMemo, useState } from "react"; |
2 | 2 | import { Stack, IconButton } from "@fluentui/react"; |
3 | 3 | import { useTranslation } from "react-i18next"; |
4 | 4 | import DOMPurify from "dompurify"; |
@@ -46,13 +46,34 @@ export const Answer = ({ |
46 | 46 | const parsedAnswer = useMemo(() => parseAnswerToHtml(answer, isStreaming, onCitationClicked), [answer]); |
47 | 47 | const { t } = useTranslation(); |
48 | 48 | const sanitizedAnswerHtml = DOMPurify.sanitize(parsedAnswer.answerHtml); |
| 49 | + const [copied, setCopied] = useState(false); |
| 50 | + |
| 51 | + const handleCopy = () => { |
| 52 | + // Single replace to remove all HTML tags to remove the citations |
| 53 | + const textToCopy = sanitizedAnswerHtml.replace(/<a [^>]*><sup>\d+<\/sup><\/a>|<[^>]+>/g, ""); |
| 54 | + |
| 55 | + navigator.clipboard |
| 56 | + .writeText(textToCopy) |
| 57 | + .then(() => { |
| 58 | + setCopied(true); |
| 59 | + setTimeout(() => setCopied(false), 2000); |
| 60 | + }) |
| 61 | + .catch(err => console.error("Failed to copy text: ", err)); |
| 62 | + }; |
49 | 63 |
|
50 | 64 | return ( |
51 | 65 | <Stack className={`${styles.answerContainer} ${isSelected && styles.selected}`} verticalAlign="space-between"> |
52 | 66 | <Stack.Item> |
53 | 67 | <Stack horizontal horizontalAlign="space-between"> |
54 | 68 | <AnswerIcon /> |
55 | 69 | <div> |
| 70 | + <IconButton |
| 71 | + style={{ color: "black" }} |
| 72 | + iconProps={{ iconName: copied ? "CheckMark" : "Copy" }} |
| 73 | + title={copied ? t("tooltips.copied") : t("tooltips.copy")} |
| 74 | + ariaLabel={copied ? t("tooltips.copied") : t("tooltips.copy")} |
| 75 | + onClick={handleCopy} |
| 76 | + /> |
56 | 77 | <IconButton |
57 | 78 | style={{ color: "black" }} |
58 | 79 | iconProps={{ iconName: "Lightbulb" }} |
|
0 commit comments