From 6a036c309b4cce2a176f0983c4ad4ba044f79295 Mon Sep 17 00:00:00 2001 From: Kyle Mathews Date: Thu, 9 Oct 2025 17:04:38 -0600 Subject: [PATCH 1/2] Serve markdown to LLMs via Accept header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add content negotiation to doc routes to serve markdown when Accept: text/markdown or Accept: text/plain is requested. This allows AI coding agents and LLMs to fetch cleaner markdown content instead of HTML, reducing token usage by ~10x. - Add prefersMarkdown() helper to check Accept header - Add ServerRoute handler to both doc routes - Include Vary: Accept header for proper caching 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/routes/$libraryId/$version.docs.$.tsx | 55 +++++++++++++++++ .../$version.docs.framework.$framework.$.tsx | 61 +++++++++++++++++++ 2 files changed, 116 insertions(+) diff --git a/src/routes/$libraryId/$version.docs.$.tsx b/src/routes/$libraryId/$version.docs.$.tsx index c980bc5a..38372734 100644 --- a/src/routes/$libraryId/$version.docs.$.tsx +++ b/src/routes/$libraryId/$version.docs.$.tsx @@ -5,6 +5,60 @@ import { findLibrary, getBranch, getLibrary } from '~/libraries' import { DocContainer } from '~/components/DocContainer' import { notFound } from '@tanstack/react-router' +// Helper function to check if the Accept header prefers markdown +function prefersMarkdown(acceptHeader: string | null): boolean { + if (!acceptHeader) return false + + const accepts = acceptHeader.split(',').map(type => { + const [mediaType, ...params] = type.trim().split(';') + const quality = params.find(p => p.trim().startsWith('q=')) + const q = quality ? parseFloat(quality.split('=')[1]) : 1.0 + return { mediaType: mediaType.toLowerCase(), q } + }) + + const markdownQ = accepts.find(a => + a.mediaType === 'text/markdown' || a.mediaType === 'text/plain' + )?.q || 0 + + const htmlQ = accepts.find(a => + a.mediaType === 'text/html' || a.mediaType === '*/*' + )?.q || 0 + + return markdownQ > 0 && markdownQ > htmlQ +} + +export const ServerRoute = createServerFileRoute().methods({ + GET: async ({ request, params }) => { + const acceptHeader = request.headers.get('Accept') + + if (prefersMarkdown(acceptHeader)) { + const url = new URL(request.url) + const { libraryId, version, _splat: docsPath } = params + const library = getLibrary(libraryId) + const root = library.docsRoot || 'docs' + + const doc = await loadDocs({ + repo: library.repo, + branch: getBranch(library, version), + docsPath: `${root}/${docsPath}`, + currentPath: url.pathname, + redirectPath: `/${library.id}/${version}/docs/overview`, + }) + + const markdownContent = `# ${doc.title}\n${doc.content}` + + return new Response(markdownContent, { + headers: { + 'Content-Type': 'text/markdown; charset=utf-8', + 'Cache-Control': 'public, max-age=0, must-revalidate', + 'Cdn-Cache-Control': 'max-age=300, stale-while-revalidate=300, durable', + 'Vary': 'Accept', + }, + }) + } + }, +}) + export const Route = createFileRoute({ staleTime: 1000 * 60 * 5, loader: (ctx) => { @@ -43,6 +97,7 @@ export const Route = createFileRoute({ return { 'cache-control': 'public, max-age=0, must-revalidate', 'cdn-cache-control': 'max-age=300, stale-while-revalidate=300, durable', + 'vary': 'Accept', } }, }) diff --git a/src/routes/$libraryId/$version.docs.framework.$framework.$.tsx b/src/routes/$libraryId/$version.docs.framework.$framework.$.tsx index f246e3cd..479d1eb4 100644 --- a/src/routes/$libraryId/$version.docs.framework.$framework.$.tsx +++ b/src/routes/$libraryId/$version.docs.framework.$framework.$.tsx @@ -5,6 +5,60 @@ import { getBranch, getLibrary } from '~/libraries' import { capitalize } from '~/utils/utils' import { DocContainer } from '~/components/DocContainer' +// Helper function to check if the Accept header prefers markdown +function prefersMarkdown(acceptHeader: string | null): boolean { + if (!acceptHeader) return false + + const accepts = acceptHeader.split(',').map(type => { + const [mediaType, ...params] = type.trim().split(';') + const quality = params.find(p => p.trim().startsWith('q=')) + const q = quality ? parseFloat(quality.split('=')[1]) : 1.0 + return { mediaType: mediaType.toLowerCase(), q } + }) + + const markdownQ = accepts.find(a => + a.mediaType === 'text/markdown' || a.mediaType === 'text/plain' + )?.q || 0 + + const htmlQ = accepts.find(a => + a.mediaType === 'text/html' || a.mediaType === '*/*' + )?.q || 0 + + return markdownQ > 0 && markdownQ > htmlQ +} + +export const ServerRoute = createServerFileRoute().methods({ + GET: async ({ request, params }) => { + const acceptHeader = request.headers.get('Accept') + + if (prefersMarkdown(acceptHeader)) { + const url = new URL(request.url) + const { libraryId, version, framework, _splat: docsPath } = params + const library = getLibrary(libraryId) + const root = library.docsRoot || 'docs' + + const doc = await loadDocs({ + repo: library.repo, + branch: getBranch(library, version), + docsPath: `${root}/framework/${framework}/${docsPath}`, + currentPath: url.pathname, + redirectPath: `/${library.id}/${version}/docs/overview`, + }) + + const markdownContent = `# ${doc.title}\n${doc.content}` + + return new Response(markdownContent, { + headers: { + 'Content-Type': 'text/markdown; charset=utf-8', + 'Cache-Control': 'public, max-age=0, must-revalidate', + 'Cdn-Cache-Control': 'max-age=300, stale-while-revalidate=300, durable', + 'Vary': 'Accept', + }, + }) + } + }, +}) + export const Route = createFileRoute({ staleTime: 1000 * 60 * 5, loader: (ctx) => { @@ -36,6 +90,13 @@ export const Route = createFileRoute({ }), } }, + headers: (ctx) => { + return { + 'cache-control': 'public, max-age=0, must-revalidate', + 'cdn-cache-control': 'max-age=300, stale-while-revalidate=300, durable', + 'vary': 'Accept', + } + }, }) function Docs() { From 67530f09d94914b198c84621c2d7778c3c863f9a Mon Sep 17 00:00:00 2001 From: Kyle Mathews Date: Thu, 9 Oct 2025 17:12:49 -0600 Subject: [PATCH 2/2] DRY up markdown serving logic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract shared utilities to src/utils/docs.ts: - prefersMarkdown() - Parse Accept header with quality values - createMarkdownResponse() - Create standardized markdown response Both doc routes now use these shared functions, eliminating duplicate code and making maintenance easier. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- src/routes/$libraryId/$version.docs.$.tsx | 39 +++---------------- .../$version.docs.framework.$framework.$.tsx | 39 +++---------------- src/utils/docs.ts | 39 +++++++++++++++++++ 3 files changed, 51 insertions(+), 66 deletions(-) diff --git a/src/routes/$libraryId/$version.docs.$.tsx b/src/routes/$libraryId/$version.docs.$.tsx index 38372734..ea0ec063 100644 --- a/src/routes/$libraryId/$version.docs.$.tsx +++ b/src/routes/$libraryId/$version.docs.$.tsx @@ -1,32 +1,14 @@ import { seo } from '~/utils/seo' import { Doc } from '~/components/Doc' -import { loadDocs } from '~/utils/docs' +import { + loadDocs, + prefersMarkdown, + createMarkdownResponse, +} from '~/utils/docs' import { findLibrary, getBranch, getLibrary } from '~/libraries' import { DocContainer } from '~/components/DocContainer' import { notFound } from '@tanstack/react-router' -// Helper function to check if the Accept header prefers markdown -function prefersMarkdown(acceptHeader: string | null): boolean { - if (!acceptHeader) return false - - const accepts = acceptHeader.split(',').map(type => { - const [mediaType, ...params] = type.trim().split(';') - const quality = params.find(p => p.trim().startsWith('q=')) - const q = quality ? parseFloat(quality.split('=')[1]) : 1.0 - return { mediaType: mediaType.toLowerCase(), q } - }) - - const markdownQ = accepts.find(a => - a.mediaType === 'text/markdown' || a.mediaType === 'text/plain' - )?.q || 0 - - const htmlQ = accepts.find(a => - a.mediaType === 'text/html' || a.mediaType === '*/*' - )?.q || 0 - - return markdownQ > 0 && markdownQ > htmlQ -} - export const ServerRoute = createServerFileRoute().methods({ GET: async ({ request, params }) => { const acceptHeader = request.headers.get('Accept') @@ -45,16 +27,7 @@ export const ServerRoute = createServerFileRoute().methods({ redirectPath: `/${library.id}/${version}/docs/overview`, }) - const markdownContent = `# ${doc.title}\n${doc.content}` - - return new Response(markdownContent, { - headers: { - 'Content-Type': 'text/markdown; charset=utf-8', - 'Cache-Control': 'public, max-age=0, must-revalidate', - 'Cdn-Cache-Control': 'max-age=300, stale-while-revalidate=300, durable', - 'Vary': 'Accept', - }, - }) + return createMarkdownResponse(doc.title, doc.content) } }, }) diff --git a/src/routes/$libraryId/$version.docs.framework.$framework.$.tsx b/src/routes/$libraryId/$version.docs.framework.$framework.$.tsx index 479d1eb4..7c54b9b8 100644 --- a/src/routes/$libraryId/$version.docs.framework.$framework.$.tsx +++ b/src/routes/$libraryId/$version.docs.framework.$framework.$.tsx @@ -1,32 +1,14 @@ import { seo } from '~/utils/seo' import { Doc } from '~/components/Doc' -import { loadDocs } from '~/utils/docs' +import { + loadDocs, + prefersMarkdown, + createMarkdownResponse, +} from '~/utils/docs' import { getBranch, getLibrary } from '~/libraries' import { capitalize } from '~/utils/utils' import { DocContainer } from '~/components/DocContainer' -// Helper function to check if the Accept header prefers markdown -function prefersMarkdown(acceptHeader: string | null): boolean { - if (!acceptHeader) return false - - const accepts = acceptHeader.split(',').map(type => { - const [mediaType, ...params] = type.trim().split(';') - const quality = params.find(p => p.trim().startsWith('q=')) - const q = quality ? parseFloat(quality.split('=')[1]) : 1.0 - return { mediaType: mediaType.toLowerCase(), q } - }) - - const markdownQ = accepts.find(a => - a.mediaType === 'text/markdown' || a.mediaType === 'text/plain' - )?.q || 0 - - const htmlQ = accepts.find(a => - a.mediaType === 'text/html' || a.mediaType === '*/*' - )?.q || 0 - - return markdownQ > 0 && markdownQ > htmlQ -} - export const ServerRoute = createServerFileRoute().methods({ GET: async ({ request, params }) => { const acceptHeader = request.headers.get('Accept') @@ -45,16 +27,7 @@ export const ServerRoute = createServerFileRoute().methods({ redirectPath: `/${library.id}/${version}/docs/overview`, }) - const markdownContent = `# ${doc.title}\n${doc.content}` - - return new Response(markdownContent, { - headers: { - 'Content-Type': 'text/markdown; charset=utf-8', - 'Cache-Control': 'public, max-age=0, must-revalidate', - 'Cdn-Cache-Control': 'max-age=300, stale-while-revalidate=300, durable', - 'Vary': 'Accept', - }, - }) + return createMarkdownResponse(doc.title, doc.content) } }, }) diff --git a/src/utils/docs.ts b/src/utils/docs.ts index 4af832d8..d3ea262f 100644 --- a/src/utils/docs.ts +++ b/src/utils/docs.ts @@ -216,3 +216,42 @@ export const fetchAllMaintainerStats = createServerFn({ } }) */ + +// Helper function to check if the Accept header prefers markdown +export function prefersMarkdown(acceptHeader: string | null): boolean { + if (!acceptHeader) return false + + const accepts = acceptHeader.split(',').map(type => { + const [mediaType, ...params] = type.trim().split(';') + const quality = params.find(p => p.trim().startsWith('q=')) + const q = quality ? parseFloat(quality.split('=')[1]) : 1.0 + return { mediaType: mediaType.toLowerCase(), q } + }) + + const markdownQ = accepts.find(a => + a.mediaType === 'text/markdown' || a.mediaType === 'text/plain' + )?.q || 0 + + const htmlQ = accepts.find(a => + a.mediaType === 'text/html' || a.mediaType === '*/*' + )?.q || 0 + + return markdownQ > 0 && markdownQ > htmlQ +} + +// Helper function to create markdown response +export function createMarkdownResponse( + title: string, + content: string +): Response { + const markdownContent = `# ${title}\n${content}` + + return new Response(markdownContent, { + headers: { + 'Content-Type': 'text/markdown; charset=utf-8', + 'Cache-Control': 'public, max-age=0, must-revalidate', + 'Cdn-Cache-Control': 'max-age=300, stale-while-revalidate=300, durable', + 'Vary': 'Accept', + }, + }) +}