+
+ {open && (
+
+
}
+ title="Copy page"
+ description="Copy page as Markdown for LLMs"
+ onClick={onCopy}
+ />
+
}
+ title="View as Markdown"
+ description="View this page as plain text"
+ href={markdownHref}
+ onClick={() => setOpen(false)}
+ />
+
}
+ title="Open in ChatGPT"
+ description="Ask questions about this page"
+ href={chatGptHref}
+ onClick={() => setOpen(false)}
+ />
+
}
+ title="Open in Claude"
+ description="Ask questions about this page"
+ href={claudeHref}
+ onClick={() => setOpen(false)}
+ />
+
+ )}
);
}
+
+function ActionButton({
+ icon,
+ title,
+ description,
+ onClick,
+}: {
+ icon: ReactNode;
+ title: string;
+ description: string;
+ onClick: () => void;
+}) {
+ return (
+
+ );
+}
+
+function ActionLink({
+ icon,
+ title,
+ description,
+ href,
+ onClick,
+}: {
+ icon: ReactNode;
+ title: string;
+ description: string;
+ href: string;
+ onClick: () => void;
+}) {
+ return (
+
+ {icon}
+
+
+ );
+}
+
+function ActionIcon({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
+
+function ActionText({
+ title,
+ description,
+ external,
+}: {
+ title: string;
+ description: string;
+ external?: boolean;
+}) {
+ return (
+
+
+ {title}
+ {external && }
+
+
+ {description}
+
+
+ );
+}
+
+function CopyIcon() {
+ return (
+
+ );
+}
+
+function CheckIcon() {
+ return (
+
+ );
+}
+
+function ChevronIcon({ open }: { open: boolean }) {
+ return (
+
+ );
+}
+
+function MarkdownIcon() {
+ return (
+
+ );
+}
+
+function OpenAiIcon() {
+ return (
+
+ );
+}
+
+function ClaudeIcon() {
+ return (
+
+ );
+}
+
+function ExternalIcon() {
+ return (
+
+ );
+}
diff --git a/docs-site/tests/docs-search-behavior.test.mjs b/docs-site/tests/docs-search-behavior.test.mjs
index ece514776..907794c76 100644
--- a/docs-site/tests/docs-search-behavior.test.mjs
+++ b/docs-site/tests/docs-search-behavior.test.mjs
@@ -34,6 +34,28 @@ test("markdown negotiation uses the Next proxy convention", async () => {
assert.doesNotMatch(proxy, /export function middleware/);
});
+test("docs pages expose a canonical Markdown copy disclosure", async () => {
+ const page = await readDocsFile("app/docs/[[...slug]]/page.tsx");
+ const action = await readDocsFile("components/docs-page-actions.tsx");
+
+ assert.match(page, /
{
const css = await readDocsFile("app/global.css");