diff --git a/apps/web/src/__tests__/components/__snapshots__/snapshot.test.tsx.snap b/apps/web/src/__tests__/components/__snapshots__/snapshot.test.tsx.snap index a21eb9a..198b065 100644 --- a/apps/web/src/__tests__/components/__snapshots__/snapshot.test.tsx.snap +++ b/apps/web/src/__tests__/components/__snapshots__/snapshot.test.tsx.snap @@ -3,6 +3,7 @@ exports[`Copy components snapshots > CopyButton renders correctly 1`] = ` diff --git a/apps/web/src/components/copy-button.tsx b/apps/web/src/components/copy-button.tsx index ff4af53..52c5657 100644 --- a/apps/web/src/components/copy-button.tsx +++ b/apps/web/src/components/copy-button.tsx @@ -8,18 +8,38 @@ interface CopyButtonProps { label?: string className?: string iconSize?: number + onCopied?: () => void } -export function CopyButton({ value, label, className = '', iconSize = 14 }: CopyButtonProps) { +/** Write text to clipboard with a mobile-safe fallback via a temporary textarea. */ +async function writeToClipboard(text: string): Promise { + if (navigator.clipboard?.writeText) { + await navigator.clipboard.writeText(text) + return + } + // Fallback for mobile browsers that don't support navigator.clipboard + const el = document.createElement('textarea') + el.value = text + el.style.cssText = 'position:fixed;top:0;left:0;opacity:0' + document.body.appendChild(el) + el.focus() + el.select() + const ok = document.execCommand('copy') + document.body.removeChild(el) + if (!ok) throw new Error('copy failed') +} + +export function CopyButton({ value, label, className = '', iconSize = 14, onCopied }: CopyButtonProps) { const [copied, setCopied] = useState(false) async function handleCopy() { try { - await navigator.clipboard.writeText(value) + await writeToClipboard(value) setCopied(true) + onCopied?.() setTimeout(() => setCopied(false), 2000) - } catch (err) { - console.error('Failed to copy:', err) + } catch { + // Surface nothing to the user visually — caller can supply onCopied for toast } } @@ -27,6 +47,7 @@ export function CopyButton({ value, label, className = '', iconSize = 14 }: Copy