diff --git a/src/components/LLMActions/LLMActions.tsx b/src/components/LLMActions/LLMActions.tsx index bbc03ca956..025a1cf11a 100644 --- a/src/components/LLMActions/LLMActions.tsx +++ b/src/components/LLMActions/LLMActions.tsx @@ -4,6 +4,7 @@ import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; import { FaRegCopy, FaCheck, FaMarkdown, FaExternalLinkAlt, FaChevronDown, FaChevronUp } from 'react-icons/fa'; import { SiOpenai, SiClaude } from 'react-icons/si'; import styles from './LLMActions.module.css'; +import { getMarkdownPath } from './markdownPath'; export default function LLMActions() { const [copied, setCopied] = useState(false); @@ -22,7 +23,7 @@ export default function LLMActions() { // that build output rather than the raw MDX source. // NOTE: the .md files only exist after `yarn build`; under `yarn start` (dev // server) these requests will 404. Verify locally with `yarn build && yarn serve`. - const mdPath = `${permalink.replace(/\/$/, '')}.md`; + const mdPath = getMarkdownPath(permalink); const mdUrl = `${siteConfig.url}${mdPath}`; const prompt = `Read ${mdUrl} and answer questions about the content.`; diff --git a/src/components/LLMActions/MarkdownAlternateLink.tsx b/src/components/LLMActions/MarkdownAlternateLink.tsx new file mode 100644 index 0000000000..9b0d060d8d --- /dev/null +++ b/src/components/LLMActions/MarkdownAlternateLink.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import Head from '@docusaurus/Head'; +import { useDoc } from '@docusaurus/plugin-content-docs/client'; +import { getMarkdownPath } from './markdownPath'; + +/** + * Emits a per-page into the page + * , pointing at the generated clean Markdown (.md). This lets + * crawlers and LLMs discover the Markdown rendition without constructing the URL. + * + * Rendered server-side into the static HTML by the swizzled DocItem/Content. + * Skipped on pages with no Markdown counterpart (llm_exclude), matching the + * markdown-pages plugin and LLMActions. See MARKDOWN_PIPELINE.md. + */ +export default function MarkdownAlternateLink(): JSX.Element | null { + const { metadata, frontMatter } = useDoc(); + + if (!metadata.permalink || frontMatter.llm_exclude) { + return null; + } + + const href = getMarkdownPath(metadata.permalink); + + return ( + + + + ); +} diff --git a/src/components/LLMActions/markdownPath.ts b/src/components/LLMActions/markdownPath.ts new file mode 100644 index 0000000000..ddb4c8fdd3 --- /dev/null +++ b/src/components/LLMActions/markdownPath.ts @@ -0,0 +1,11 @@ +/** + * Site-relative path to the generated clean Markdown for a doc permalink. + * + * The markdown-pages plugin writes each page to `.md` at build time + * (see MARKDOWN_PIPELINE.md). Shared by LLMActions (Copy / View as Markdown) and + * MarkdownAlternateLink (the head tag) so the convention + * lives in one place. + */ +export function getMarkdownPath(permalink: string): string { + return `${permalink.replace(/\/$/, '')}.md`; +} diff --git a/src/theme/DocItem/Content/index.tsx b/src/theme/DocItem/Content/index.tsx index dff2655e4e..fde3a603e8 100644 --- a/src/theme/DocItem/Content/index.tsx +++ b/src/theme/DocItem/Content/index.tsx @@ -7,6 +7,7 @@ import MDXContent from '@theme/MDXContent'; import type { Props } from '@theme/DocItem/Content'; import LLMActions from '@site/src/components/LLMActions/LLMActions'; +import MarkdownAlternateLink from '@site/src/components/LLMActions/MarkdownAlternateLink'; import styles from './styles.module.css'; /** @@ -28,6 +29,7 @@ export default function DocItemContent({ children }: Props): JSX.Element { return (
+ {syntheticTitle && (
{syntheticTitle}