Skip to content

feat(docs): add per-page .md endpoint and Copy MD button#379

Open
ParwarYasinQadr wants to merge 2 commits into
get-convex:mainfrom
ParwarYasinQadr:feat/markdown-endpoint
Open

feat(docs): add per-page .md endpoint and Copy MD button#379
ParwarYasinQadr wants to merge 2 commits into
get-convex:mainfrom
ParwarYasinQadr:feat/markdown-endpoint

Conversation

@ParwarYasinQadr

Copy link
Copy Markdown

Closes #378

Appending .md to any docs URL returns the page as markdown. Useful for piping docs into Claude/ChatGPT/Cursor without scraping HTML.

/better-auth/basic-usage/authorization → renders normally
/better-auth/basic-usage/authorization.md → raw markdown

Also added a Copy MD button + Open in ▾ dropdown (Copy markdown link / Open in new tab) in the top-right of every docs page, styled with
Fumadocs theme tokens.

Changes:

  • source.config.ts: enabled includeProcessedMarkdown with a custom stringify so the output is actual markdown. Strips inline SVGs,
    unwraps <Cards> / <Callout> / <CodeBlockTabs> / fd-step divs, renders Callouts as blockquotes, keeps tab labels.
  • lib/get-llm-text.ts: formats # Title (/url) + description + body
  • app/llms.mdx/[[...slug]]/route.ts: route handler. Statically prerendered for every page, with Access-Control-Allow-Origin: * and a
    1h browser / 24h CDN cache.
  • next.config.mjs: rewrite /:path*.md/llms.mdx/:path*
  • components/copy-markdown.tsx: Copy button + Radix popover dropdown. Uses Fumadocs UI's transitive deps so no new packages.

Tested locally across all 25 docs pages, regular site still renders, unknown paths 404, build clean.

Skipped llms.txt / llms-full.txt to keep the PR focused — happy to follow up in a separate one if useful.

Appending .md to any docs URL returns the page as clean markdown,
making it easy for users and AI tools to feed docs into LLMs.

- Adds /llms.mdx/[[...slug]]/route.ts handler with CORS + cache headers
- Adds /:path*.md → /llms.mdx/:path* rewrite in next.config.mjs
- Configures includeProcessedMarkdown with a custom stringify that
  drops inline SVGs, unwraps JSX wrappers (Cards, fd-step divs,
  CodeBlockTabs), renders Callouts as blockquotes, and preserves
  tab labels so output is real markdown rather than raw MDX
- Adds Copy MD button + Open-in dropdown on every docs page,
  styled with Fumadocs theme tokens
@vercel

vercel Bot commented May 19, 2026

Copy link
Copy Markdown

@ParwarYasinQadr is attempting to deploy a commit to the Convex Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai

coderabbitai Bot commented May 19, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: get-convex/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b28567c7-8912-43e8-853d-89ea3b327c58

📥 Commits

Reviewing files that changed from the base of the PR and between 85abef1 and e917c88.

📒 Files selected for processing (2)
  • docs/components/copy-markdown.tsx
  • docs/lib/get-llm-text.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/components/copy-markdown.tsx

📝 Walkthrough

Walkthrough

This PR adds markdown export functionality to the documentation site. Users can now append .md to any docs URL to fetch clean, LLM-ready markdown. The implementation involves custom MDX postprocessing rules that transform interactive elements (cards, callouts, tabs) into markdown equivalents, text normalization to clean whitespace and strip internal markers, a new route handler exposing markdown via /*.md URLs with appropriate headers, a client-side copy-to-clipboard component with feedback UI, and integration into the docs page layout with URL computation helpers.

Sequence Diagram(s)

sequenceDiagram
  participant Browser
  participant CopyButton as CopyMarkdownButton
  participant Fetch as BrowserFetch
  participant NextRewrite as Next.js Rewrite
  participant RouteHandler as /llms.mdx/[[...slug]] Route
  participant Source as source.getPage
  participant TextNorm as getLLMText
  Browser->>CopyButton: user clicks "Copy MD"
  CopyButton->>Fetch: fetch(/path/to/page.md)
  Fetch->>NextRewrite: GET /path/to/page.md
  NextRewrite->>RouteHandler: rewrite → GET /llms.mdx/path/to/page
  RouteHandler->>Source: source.getPage(slug)
  Source-->>RouteHandler: page
  RouteHandler->>TextNorm: getLLMText(page)
  TextNorm-->>RouteHandler: formatted markdown
  RouteHandler-->>Fetch: 200 text/markdown
  Fetch-->>CopyButton: response text
  CopyButton->>Browser: writeText(markdown) → clipboard
  CopyButton-->>Browser: show "Copied" UI
Loading
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main changes: adding a per-page .md endpoint and a Copy MD button component.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, explaining the feature, implementation details, testing, and technical approach.
Linked Issues check ✅ Passed All code changes align with issue #378: adds .md endpoint via route handler and rewrite [app/llms.mdx/[[...slug]]/route.ts, next.config.mjs], enables markdown processing [source.config.ts], includes formatting logic [lib/get-llm-text.ts], and adds UI button [components/copy-markdown.tsx].
Out of Scope Changes check ✅ Passed All changes are within scope of issue #378: the new files and config edits directly implement the .md endpoint feature without introducing unrelated functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/components/copy-markdown.tsx`:
- Around line 54-57: The handleCopyLink function should mirror
handleCopyMarkdown's error handling: wrap the
navigator.clipboard.writeText(fullUrl()) call in a try/catch, await it inside
try, call flash(setLinkCopied, () => setOpen(false)) on success, and in catch
log or otherwise handle the error (e.g., console.error or use existing logger)
to avoid unhandled promise rejections; update the handleCopyLink function
(references: handleCopyLink, fullUrl, navigator.clipboard.writeText, flash,
setLinkCopied, setOpen) accordingly.

In `@docs/lib/get-llm-text.ts`:
- Around line 14-18: stripDropMarkers currently only filters out lines that
consist solely of the drop marker, leaving marker characters embedded in
non-empty lines; update the stripDropMarkers function to remove the marker
character globally instead: within stripDropMarkers, replace all occurrences of
the marker character (the visible character in the regex /^\s*​+\s*$/ used
previously) across the entire text (e.g., using a global replace) and then trim
or collapse any resulting empty lines if needed so no invisible marker artifacts
remain in output markdown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: get-convex/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 386950ff-a523-4b5b-b61b-d3b36a0b2539

📥 Commits

Reviewing files that changed from the base of the PR and between be73010 and 85abef1.

📒 Files selected for processing (6)
  • docs/app/(home)/[[...slug]]/page.tsx
  • docs/app/llms.mdx/[[...slug]]/route.ts
  • docs/components/copy-markdown.tsx
  • docs/lib/get-llm-text.ts
  • docs/next.config.mjs
  • docs/source.config.ts

Comment thread docs/components/copy-markdown.tsx
Comment thread docs/lib/get-llm-text.ts Outdated
- Wrap handleCopyLink in try/catch to match handleCopyMarkdown's
  error handling and avoid unhandled promise rejections on clipboard
  permission denial
- Strip zero-width-space drop markers globally instead of only on
  whole-marker lines, so embedded markers can't survive in output
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add *.md endpoint per docs page

1 participant