diff --git a/components/CopyButton.tsx b/components/CopyButton.tsx index 0a1d268..4a2612b 100644 --- a/components/CopyButton.tsx +++ b/components/CopyButton.tsx @@ -1,110 +1,103 @@ -import {useState, useCallback} from "react"; +import { useState, useCallback } from 'react'; +import { copyToClipboard } from '@/utils/chromeUtils'; const CopyButton = ({ - text = '要复制的文本', - buttonText = '复制文本', - className = 'action-btn', - }) => { - const [btnText, setBtnText] = useState(buttonText); - const [copyStatus, setCopyStatus] = useState(''); - - // 重置按钮状态的函数 - const resetButton = useCallback(() => { - setBtnText(buttonText); - setCopyStatus(''); - }, [buttonText]); - - // 复制成功的处理 - const handleCopySuccess = useCallback(() => { - setCopyStatus('success'); - setBtnText('复制成功!'); + text = '要复制的文本', + buttonText = '复制文本', + className = 'action-btn', + successMessage = '复制成功!', + errorMessage = '复制失败,请手动复制。', + onCopyOverride = null, +}) => { + const [status, setStatus] = useState('idle'); // 'idle' | 'copying' | 'success' | 'error' + + useEffect(() => { + let timer; + if (status === 'success' || status === 'error') { + timer = setTimeout(() => { + setStatus('idle'); + }, 2000); + } + + return () => clearTimeout(timer); + }, [status]); + + const performCopy = async () => { + if (!text) { + console.warn('没有提供要复制的文本'); + return false; + } + + const safeText = String(text); + + // 优先使用传入的复制函数 + if (onCopyOverride) { + return await onCopyOverride(safeText); + } + + const textArea = document.createElement("textarea"); + textArea.value = safeText; - // 2秒后恢复 - setTimeout(() => { - resetButton(); - }, 2000); - }, [text, resetButton]); - - // 复制失败的处理 - const handleCopyError = useCallback(() => { - setCopyStatus('error'); + // 确保元素存在但不可见,且不影响布局 + textArea.style.position = "fixed"; + textArea.style.left = "-9999px"; + textArea.style.top = "0"; + textArea.style.opacity = "0"; - // 2秒后恢复 - setTimeout(() => { - resetButton(); - }, 2000); - }, [resetButton]); - - const handleCopy = async () => { + document.body.appendChild(textArea); + textArea.focus(); + textArea.select(); + + const successful = document.execCommand('copy'); + document.body.removeChild(textArea); + + // 使用通用的复制函数 + return true; + }; + + const handleClick = useCallback(async () => { + setStatus('copying'); + try { - console.log('开始复制:', text); + const isSuccess = await performCopy(); + setStatus(isSuccess ? 'success' : 'error'); + } catch (error) { + console.error('复制时出错:', error); + setStatus('error'); + } + }, [text, onCopyOverride]); - // 首先尝试直接使用navigator.clipboard(在popup页面中可能可用) - try { - console.log('尝试直接使用navigator.clipboard复制'); - await navigator.clipboard.writeText(text.toString()); - console.log('直接复制成功'); - handleCopySuccess(); - return; - } catch (directError) { - console.log('直接复制失败,尝试使用Chrome扩展API:', directError); - } + // 根据状态计算当前显示的文本 + const currentText = status === 'success' ? successMessage + : status === 'error' ? errorMessage + : buttonText; - // 如果直接复制失败,尝试使用Chrome扩展API - if (chrome && chrome.runtime && chrome.runtime.sendMessage) { - console.log('使用Chrome扩展API复制'); - - // 使用Chrome扩展API复制 - chrome.runtime.sendMessage({ - action: 'copy', - text: text - }, (response) => { - console.log('收到background响应:', response, 'lastError:', chrome.runtime.lastError); - - // 检查是否有运行时错误 - if (chrome.runtime.lastError) { - console.error('Chrome运行时错误:', chrome.runtime.lastError.message); - handleCopyError(); - return; - } - - // 检查响应 - if (response && response.success) { - console.log('通过background复制成功'); - handleCopySuccess(); - } else { - console.error('通过background复制失败:', response?.error); - handleCopyError(); - } - }); - } else { - console.error('没有可用的复制方法'); - handleCopyError(); - } - } catch (err) { - console.error('复制过程中发生错误:', err); - handleCopyError(); + // 根据状态计算样式 (建议使用 CSS Module 或 Tailwind,这里为了演示保留内联) + const getBackgroundColor = () => { + switch (status) { + case 'success': return '#4CAF50'; + case 'error': return '#f44336'; + default: return '#4CAF50'; // 让 CSS 类控制默认颜色 } }; - + return ( ); }; -export default CopyButton; \ No newline at end of file +export default CopyButton; diff --git a/components/TimestampExecution.tsx b/components/TimestampExecution.tsx index 7018b78..36e727c 100644 --- a/components/TimestampExecution.tsx +++ b/components/TimestampExecution.tsx @@ -127,7 +127,7 @@ export function TimestampExecution() { diff --git a/entrypoints/options/index.html b/entrypoints/options/index.html new file mode 100644 index 0000000..057e5d2 --- /dev/null +++ b/entrypoints/options/index.html @@ -0,0 +1,11 @@ + + + + + + Document + + + Hello Options Page + + \ No newline at end of file diff --git a/entrypoints/popup/App.tsx b/entrypoints/popup/App.tsx index 9baf911..bf42483 100644 --- a/entrypoints/popup/App.tsx +++ b/entrypoints/popup/App.tsx @@ -43,7 +43,7 @@ function App() { const collapsedNavItems = navItems.slice(visibleItems); return ( - +