diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 15b2666280..8df6f596c5 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -2,6 +2,7 @@ import { VSCodeBadge, VSCodeButton, VSCodeProgressRing } from "@vscode/webview-u import deepEqual from "fast-deep-equal" import React, { memo, useEffect, useMemo, useRef, useState } from "react" import { useSize } from "react-use" +import { useCopyToClipboard } from "../../utils/clipboard" import { ClineApiReqInfo, ClineAskUseMcpServer, @@ -985,6 +986,7 @@ export const ProgressIndicator = () => ( const Markdown = memo(({ markdown, partial }: { markdown?: string; partial?: boolean }) => { const [isHovering, setIsHovering] = useState(false) + const { copyWithFeedback } = useCopyToClipboard(200) // shorter feedback duration for copy button flash return (
{ - navigator.clipboard.writeText(markdown) - // Flash the button background briefly to indicate success - const button = document.activeElement as HTMLElement - if (button) { - button.style.background = "var(--vscode-button-background)" - setTimeout(() => { - button.style.background = "" - }, 200) + onClick={async () => { + const success = await copyWithFeedback(markdown) + if (success) { + const button = document.activeElement as HTMLElement + if (button) { + button.style.background = "var(--vscode-button-background)" + setTimeout(() => { + button.style.background = "" + }, 200) + } } }} title="Copy as markdown"> diff --git a/webview-ui/src/components/history/HistoryPreview.tsx b/webview-ui/src/components/history/HistoryPreview.tsx index 562c110097..7825844e70 100644 --- a/webview-ui/src/components/history/HistoryPreview.tsx +++ b/webview-ui/src/components/history/HistoryPreview.tsx @@ -1,8 +1,9 @@ import { VSCodeButton } from "@vscode/webview-ui-toolkit/react" import { useExtensionState } from "../../context/ExtensionStateContext" import { vscode } from "../../utils/vscode" -import { memo, useState } from "react" +import { memo } from "react" import { formatLargeNumber } from "../../utils/format" +import { useCopyToClipboard } from "../../utils/clipboard" type HistoryPreviewProps = { showHistoryView: () => void @@ -10,18 +11,7 @@ type HistoryPreviewProps = { const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => { const { taskHistory } = useExtensionState() - const [showCopyModal, setShowCopyModal] = useState(false) - - const handleCopyTask = async (e: React.MouseEvent, task: string) => { - e.stopPropagation() - try { - await navigator.clipboard.writeText(task) - setShowCopyModal(true) - setTimeout(() => setShowCopyModal(false), 2000) - } catch (error) { - console.error("Failed to copy to clipboard:", error) - } - } + const { showCopyFeedback, copyWithFeedback } = useCopyToClipboard() const handleHistorySelect = (id: string) => { vscode.postMessage({ type: "showTaskWithId", text: id }) } @@ -43,7 +33,7 @@ const HistoryPreview = ({ showHistoryView }: HistoryPreviewProps) => { return (
- {showCopyModal &&
Prompt Copied to Clipboard
} + {showCopyFeedback &&
Prompt Copied to Clipboard
} - {showCopyModal &&
Prompt Copied to Clipboard
} + {showCopyFeedback &&
Prompt Copied to Clipboard
}
{ title="Copy Prompt" className="copy-button" data-appearance="icon" - onClick={(e) => handleCopyTask(e, item.task)}> + onClick={(e) => copyWithFeedback(item.task, e)}>