From 13af7f14b3834122cd8ee05fce5be4914824cdac Mon Sep 17 00:00:00 2001 From: Darius Cepulis Date: Thu, 2 Apr 2026 09:20:51 -0500 Subject: [PATCH] feat(site): add llms.txt footer link to generated markdown pages (#1201) Co-authored-by: Claude Opus 4.6 (1M context) --- site/integrations/llms-markdown.ts | 63 +++++++++++++++++------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/site/integrations/llms-markdown.ts b/site/integrations/llms-markdown.ts index 25a4fc1f..5aff9bf5 100644 --- a/site/integrations/llms-markdown.ts +++ b/site/integrations/llms-markdown.ts @@ -100,8 +100,9 @@ export default function llmsMarkdown(): AstroIntegration { // Write markdown file as sibling to the directory // docs/framework/html/how-to/slug -> docs/framework/html/how-to/slug.md const mdPath = join(siteDir, `${pathname}.md`); + const footer = generatePageFooter(pathname, framework, siteUrl); await mkdir(dirname(mdPath), { recursive: true }); - await writeFile(mdPath, markdown, 'utf-8'); + await writeFile(mdPath, markdown + footer, 'utf-8'); // Track for llms.txt index (with leading slash for URLs) if (pathname.startsWith('docs/')) { @@ -158,14 +159,34 @@ export default function llmsMarkdown(): AstroIntegration { }; } +function capitalize(str: string): string { + return str.charAt(0).toUpperCase() + str.slice(1); +} + +/** Breadcrumb footer linking a per-page .md back to its parent index and root llms.txt. */ +function generatePageFooter(pathname: string, framework: string | undefined, siteUrl: string): string { + const lines = ['\n\n---\n']; + if (pathname.startsWith('docs/') && framework) { + lines.push(`${capitalize(framework)} documentation: ${siteUrl}/docs/framework/${framework}/llms.txt`); + } else if (pathname.startsWith('blog/')) { + lines.push(`All blog posts: ${siteUrl}/blog/llms.txt`); + } + lines.push(`All documentation: ${siteUrl}/llms.txt`); + return lines.join('\n'); +} + +/** Breadcrumb footer linking a sub-index back to root llms.txt. */ +function generateIndexFooter(siteUrl: string): string { + return `\n---\n\nAll documentation: ${siteUrl}/llms.txt\n`; +} + function generateRootIndex(frameworks: string[], hasBlog: boolean, otherPages: PageEntry[], siteUrl: string): string { let content = `# Video.js v10\n\n`; content += `> Modern video player framework with multi-platform support\n\n`; content += `## Documentation\n\n`; for (const fw of [...frameworks].sort()) { - const label = fw.charAt(0).toUpperCase() + fw.slice(1); - content += `- [${label} Docs](${siteUrl}/docs/framework/${fw}/llms.txt)\n`; + content += `- [${capitalize(fw)} Docs](${siteUrl}/docs/framework/${fw}/llms.txt)\n`; } content += `\n`; @@ -178,11 +199,9 @@ function generateRootIndex(frameworks: string[], hasBlog: boolean, otherPages: P content += `## Other\n\n`; const sorted = [...otherPages].sort((a, b) => a.pathname.localeCompare(b.pathname)); for (const page of sorted) { - if (page.description) { - content += `- [${page.title}](${siteUrl}${page.pathname}.md): ${page.description}\n`; - } else { - content += `- [${page.title}](${siteUrl}${page.pathname}.md)\n`; - } + content += page.description + ? `- [${page.title}](${siteUrl}${page.pathname}.md): ${page.description}\n` + : `- [${page.title}](${siteUrl}${page.pathname}.md)\n`; } content += `\n`; } @@ -191,25 +210,19 @@ function generateRootIndex(frameworks: string[], hasBlog: boolean, otherPages: P } function generateDocsIndex(framework: string, pages: PageEntry[], siteUrl: string): string { - const label = framework.charAt(0).toUpperCase() + framework.slice(1); - let content = `# Video.js v10 — ${label} Documentation\n\n`; - + let content = `# Video.js v10 — ${capitalize(framework)} Documentation\n\n`; const sorted = [...pages].sort((a, b) => a.pathname.localeCompare(b.pathname)); for (const page of sorted) { - if (page.description) { - content += `- [${page.title}](${siteUrl}${page.pathname}.md): ${page.description}\n`; - } else { - content += `- [${page.title}](${siteUrl}${page.pathname}.md)\n`; - } + content += page.description + ? `- [${page.title}](${siteUrl}${page.pathname}.md): ${page.description}\n` + : `- [${page.title}](${siteUrl}${page.pathname}.md)\n`; } - content += `\n`; - + content += generateIndexFooter(siteUrl); return content; } function generateBlogIndex(pages: PageEntry[], siteUrl: string): string { let content = `# Video.js v10 — Blog\n\n`; - // Newest first const sorted = [...pages].sort((a, b) => { if (a.sort && b.sort) { @@ -217,15 +230,11 @@ function generateBlogIndex(pages: PageEntry[], siteUrl: string): string { } return b.pathname.localeCompare(a.pathname); }); - for (const post of sorted) { - if (post.description) { - content += `- [${post.title}](${siteUrl}${post.pathname}.md): ${post.description}\n`; - } else { - content += `- [${post.title}](${siteUrl}${post.pathname}.md)\n`; - } + content += post.description + ? `- [${post.title}](${siteUrl}${post.pathname}.md): ${post.description}\n` + : `- [${post.title}](${siteUrl}${post.pathname}.md)\n`; } - content += `\n`; - + content += generateIndexFooter(siteUrl); return content; }