diff --git a/packages/gitbook/src/components/DocumentView/CodeBlock/CopyCodeButton.tsx b/packages/gitbook/src/components/DocumentView/CodeBlock/CopyCodeButton.tsx index 1ac0f94d4..1253fb16e 100644 --- a/packages/gitbook/src/components/DocumentView/CodeBlock/CopyCodeButton.tsx +++ b/packages/gitbook/src/components/DocumentView/CodeBlock/CopyCodeButton.tsx @@ -6,7 +6,7 @@ import { Button } from '@/components/primitives'; import { t, useLanguage } from '@/intl/client'; import { type ClassValue, tcls } from '@/lib/tailwind'; -import { getCodeTextFromId } from './utils'; +import { copyToClipboard, getCodeTextFromId } from './utils'; /** * Client component to copy the code of a code block. @@ -32,15 +32,17 @@ export function CopyCodeButton(props: { codeId: string; style: ClassValue }) { }; }, [copied]); - const onClick = () => { + const onClick = async () => { const codeText = getCodeTextFromId(codeId); if (codeText === null) { return; } - navigator.clipboard.writeText(codeText); - - setCopied(true); + // Only surface the "copied" state when the write actually succeeded, so a blocked + // clipboard in the embed no longer looks like a successful copy. + if (await copyToClipboard(codeText)) { + setCopied(true); + } }; return ( diff --git a/packages/gitbook/src/components/DocumentView/CodeBlock/utils.ts b/packages/gitbook/src/components/DocumentView/CodeBlock/utils.ts index 0ce29ecc5..c20f7e37d 100644 --- a/packages/gitbook/src/components/DocumentView/CodeBlock/utils.ts +++ b/packages/gitbook/src/components/DocumentView/CodeBlock/utils.ts @@ -75,6 +75,61 @@ export function getCodeTextFromId(codeId: string): string | null { return element ? getCodeText(element) : null; } +/** + * Copy text to the clipboard in a way that survives embed contexts. + * + * The async Clipboard API (`navigator.clipboard`) is unavailable or rejects in some embed + * setups — cross-origin iframes where the `clipboard-write` permission wasn't delegated, or + * non-secure contexts — which is why copying silently failed (and logged an error) in the + * Assistant embed. We fall back to a hidden `