diff --git a/packages/docusaurus-theme-classic/package.json b/packages/docusaurus-theme-classic/package.json index 63fcff4e0a3d..09fa053d21a4 100644 --- a/packages/docusaurus-theme-classic/package.json +++ b/packages/docusaurus-theme-classic/package.json @@ -35,6 +35,7 @@ "@docusaurus/utils-validation": "3.9.2", "@mdx-js/react": "^3.0.0", "clsx": "^2.0.0", + "copy-text-to-clipboard": "^3.2.0", "infima": "0.2.0-alpha.45", "lodash": "^4.17.21", "nprogress": "^0.2.0", diff --git a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Buttons/CopyButton/index.tsx b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Buttons/CopyButton/index.tsx index a09400029890..849b9fa32017 100644 --- a/packages/docusaurus-theme-classic/src/theme/CodeBlock/Buttons/CopyButton/index.tsx +++ b/packages/docusaurus-theme-classic/src/theme/CodeBlock/Buttons/CopyButton/index.tsx @@ -44,6 +44,19 @@ function ariaLabel(isCopied: boolean) { }); } +async function copyToClipboard(text: string) { + // The clipboard API is only defined in secure contexts (HTTPS / localhost). + // See https://developer.mozilla.org/en-US/docs/Web/API/Clipboard + if (navigator.clipboard) { + return navigator.clipboard.writeText(text); + } + // Fall back to copy-text-to-clipboard for non-secure contexts (e.g. HTTP + // on a local network). The fallback is lazily loaded to avoid bundle + // overhead for the common HTTPS case. + const {default: copy} = await import('copy-text-to-clipboard'); + return copy(text); +} + function useCopyButton() { const { metadata: {code}, @@ -52,12 +65,14 @@ function useCopyButton() { const copyTimeout = useRef(undefined); const copyCode = useCallback(() => { - navigator.clipboard.writeText(code).then(() => { + copyToClipboard(code).then(() => { setIsCopied(true); copyTimeout.current = window.setTimeout(() => { setIsCopied(false); }, 1000); }); + // Errors are intentionally not caught so they remain unhandled and can + // be captured by observability tools (e.g. Sentry, PostHog). }, [code]); useEffect(() => () => window.clearTimeout(copyTimeout.current), []); diff --git a/yarn.lock b/yarn.lock index 7f9b9a8df2e8..07c274a231b6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6907,6 +6907,11 @@ cookie@~0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +copy-text-to-clipboard@^3.2.0: + version "3.2.2" + resolved "https://registry.yarnpkg.com/copy-text-to-clipboard/-/copy-text-to-clipboard-3.2.2.tgz#99bc79db3f2d355ec33a08d573aff6804491ddb9" + integrity sha512-T6SqyLd1iLuqPA90J5N4cTalrtovCySh58iiZDGJ6FGznbclKh4UI+FGacQSgFzwKG77W7XT5gwbVEbd9cIH1A== + copy-webpack-plugin@^11.0.0: version "11.0.0" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz#96d4dbdb5f73d02dd72d0528d1958721ab72e04a"