diff --git a/next.config.js b/next.config.js index f8ec196e..c8d7bf0e 100644 --- a/next.config.js +++ b/next.config.js @@ -22,10 +22,15 @@ const nextConfig = { async rewrites() { return { beforeFiles: [ + // Explicit .md extension also serves markdown + { + source: '/:path*.md', + destination: '/api/md/:path*', + }, // Serve markdown when Accept header prefers text/markdown // Useful for LLM agents - https://www.skeptrune.com/posts/use-the-accept-header-to-serve-markdown-instead-of-html-to-llms/ { - source: '/:path((?!llms.txt).*)', + source: '/:path((?!llms\\.txt|api/md).*)', has: [ { type: 'header', @@ -35,11 +40,6 @@ const nextConfig = { ], destination: '/api/md/:path*', }, - // Explicit .md extension also serves markdown - { - source: '/:path*.md', - destination: '/api/md/:path*', - }, ], }; }, diff --git a/src/components/PageHeading.tsx b/src/components/PageHeading.tsx index 91304cc8..a148002d 100644 --- a/src/components/PageHeading.tsx +++ b/src/components/PageHeading.tsx @@ -41,13 +41,20 @@ function CopyAsMarkdownButton() { return () => clearTimeout(timer); }, [copied]); - async function handleCopy() { + async function fetchPageBlob() { const cleanPath = asPath.split(/[?#]/)[0]; + const res = await fetch(cleanPath + '.md'); + if (!res.ok) throw new Error('Failed to fetch'); + const text = await res.text(); + return new Blob([text], {type: 'text/plain'}); + } + + async function handleCopy() { try { - const res = await fetch(cleanPath + '.md'); - if (!res.ok) return; - const text = await res.text(); - await navigator.clipboard.writeText(text); + await navigator.clipboard.write([ + // Don't wait for the blob, or Safari will refuse clipboard access + new ClipboardItem({'text/plain': fetchPageBlob()}), + ]); setCopied(true); } catch { // Silently fail