Skip to content

Conversation

@nearestnabors
Copy link
Contributor

@nearestnabors nearestnabors commented Jan 17, 2026

Summary

  • Add <link rel="alternate" type="text/markdown"> to all page headers, pointing to the .md version of each page
  • Improve the MDX-to-markdown compilation so .md URLs return clean, readable markdown instead of raw MDX
  • Add fallback content for component-only pages (like the landing page) that shows the title, description, and link to the full interactive page

This enables LLM crawlers and training pipelines to discover and consume the markdown versions of our documentation, similar to what Vercel does with their docs.

Test plan

  • Visit any page and check the HTML source for <link rel="alternate" type="text/markdown" href="...">
  • Visit https://docs.arcade.dev/en/get-started/quickstarts/call-tool-agent.md - should return clean markdown with code blocks preserved
  • Visit https://docs.arcade.dev/en/home.md - should return fallback content with title/description and link to full page

🤖 Generated with Claude Code


Note

Enables discovery and consumption of Markdown-rendered docs alongside MDX pages.

  • New app/api/markdown/[[...slug]]/route.ts compiles MDX to clean Markdown: preserves frontmatter, strips imports/exports and JSX, converts Image to ![alt](url), maps internal links to .md, normalizes indentation, and provides fallback title/description for component-only pages
  • GET handler now reads page.mdx, compiles via compileMdxToMarkdown, and serves text/markdown
  • app/layout.tsx: injects <link rel="alternate" type="text/markdown" href="https://docs.arcade.dev{pathname}.md"> on all non-root pages

Written by Cursor Bugbot for commit c43e842. This will update automatically on new commits. Configure here.

nearestnabors and others added 3 commits January 17, 2026 01:04
- Add <link rel="alternate" type="text/markdown"> to page headers pointing to .md version
- Improve MDX-to-markdown compilation to produce clean markdown output
- Preserve code blocks and frontmatter while stripping JSX components

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Pages that only contain React components (like the landing page) now
return a helpful markdown response with the title, description, and
a link to the full interactive page.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@vercel
Copy link

vercel bot commented Jan 17, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
docs Ready Ready Preview, Comment Jan 23, 2026 9:50pm

Request Review

@evantahler
Copy link
Contributor

Screenshot 2026-01-16 at 5 18 00 PM On https://docs-git-feature-add-md-alternate-links-arcade-ai.vercel.app/en/get-started/quickstarts/call-tool-agent.md, there's something weird with the insertion of new codeblocks. The "```" I think needs to be at the start of the line

cursor[bot]

This comment was marked as outdated.

- Add dedent function to normalize indentation when extracting content from JSX components
- Add normalizeIndentation function to clean up stray whitespace while preserving meaningful markdown indentation (nested lists, blockquotes)
- Move list detection regex patterns to module top level for performance
- Ensures code block markers (```) start at column 0

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

The previous regex patterns `["']?([^"'\n]+)["']?` would truncate text
at the first apostrophe (e.g., "Arcade's" became "Arcade").

This fix:
- Uses separate patterns for double-quoted, single-quoted, and unquoted values
- Requires closing quotes to be at end of line to prevent apostrophes from
  being misinterpreted as closing delimiters
- Adds stripSurroundingQuotes helper for fallback cases

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cursor[bot]

This comment was marked as outdated.

When x-pathname header is not set, pathname defaults to "/" which would
produce an invalid alternate link "https://docs.arcade.dev/.md".
Only render the alternate link when we have a real page path.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@nearestnabors
Copy link
Contributor Author

@evantahler Got a minute to have a second look?

@evantahler
Copy link
Contributor

I like the <link rel="alternate" type="text/markdown"> idea a lot that this PR introducdes, but I'm curious as to why the MDX needed to 'rendered' to MD? Were the react components harming LLM legibility? There are still HTML elements in the pages (e.g. https://docs-git-feature-add-md-alternate-links-arcade-ai.vercel.app/en/get-started/setup/api-keys.md). Are HTML fragments OK?

If the goal is to keep html fragments, buy 0-out react, I'd suggest an alternative approach:

  • make a new MDX renderer in app/api/markdown/[[...slug]]/route.ts
  • pass a wildcard react component that returns "</>"
  • rather than using regular expressions, render the mdx to md "for real", preserving anything that wasn't react.

@nearestnabors
Copy link
Contributor Author

@evantahler Dammit!

So when agents parse markdown, HTML and MDX getting mixed in there make it hard on them. IIIRC from the friend who did this, the entire thing needs rendering down to markdown.

What approach do you recommend in light of this?

@nearestnabors
Copy link
Contributor Author

Actually, let me just try parsing the HTML back into Markdown. We have some complex MDX.

nearestnabors and others added 2 commits January 20, 2026 17:51
- Add scripts/generate-markdown.ts to pre-render MDX to markdown
- Update proxy.ts to serve static .md files from public/
- Delete API route in favor of static file serving
- Add link rewriting to add /en/ prefix and .md extension
- Add markdown-friendly component implementations
- Fix localhost URL in gmail integration page

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cursor[bot]

This comment was marked as outdated.

cursor[bot]

This comment was marked as outdated.

@teallarson
Copy link
Contributor

@nearestnabors I looked at the deploy preview and am not seeing the <link rel="alternate" type="text/markdown"> (here's the page I was on: https://docs-j0lo97vmm-arcade-ai.vercel.app/en/get-started/quickstarts/call-tool-client). I should see that, right?

I visited the .md version of the page, and I see components and imports, but they are rendering as text not as whatever the .mdx would spit out, so maybe that's the intention here.

cursor[bot]

This comment was marked as outdated.

Replace simple regex pattern that failed on nested braces with
existing BRACE_PATTERN that properly handles up to 3 levels of
nesting. This fixes issues with expressions like {obj || {default: true}}
and {items.map(x => ({a: x}))} where stray closing braces would remain
in the output.
@cursor
Copy link

cursor bot commented Jan 23, 2026

Bugbot Autofix resolved the bug found in the latest run.

  • ✅ Fixed: JSX expression regex fails with nested braces
    • Replaced simple regex with existing BRACE_PATTERN that properly handles up to 3 levels of nested braces, fixing issues with expressions like {obj || {default: true}} and {items.map(x => ({a: x}))}.

- Convert GuideOverview.Outcomes/Prerequisites/YouWillLearn to ## headers
- Convert <Image> components to ![alt](src) markdown syntax
- Add .md extension to internal links for LLM crawlers

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix IMAGE_COMPONENT_REGEX to use JSX_ATTRS_PATTERN for handling > in attributes
- Fix INTERNAL_LINK_REGEX to correctly place .md before URL fragments
- Fix IMAGE_ALT_REGEX and IMAGE_SRC_REGEX to handle apostrophes in quoted strings
@cursor
Copy link

cursor bot commented Jan 23, 2026

Bugbot Autofix resolved all 3 bugs found in the markdown API route regex patterns.

  • ✅ Fixed: IMAGE_COMPONENT_REGEX fails on > in attributes
    • Updated IMAGE_COMPONENT_REGEX to use JSX_ATTRS_PATTERN which properly handles > characters inside quoted attributes.
  • ✅ Fixed: Internal links with fragments get .md placed incorrectly
    • Modified INTERNAL_LINK_REGEX to capture path and fragment separately, ensuring .md is inserted before the fragment.
  • ✅ Fixed: Image alt/src with apostrophes get truncated
    • Updated IMAGE_ALT_REGEX and IMAGE_SRC_REGEX to match quote types separately, preventing apostrophes from truncating values.

Track the indentation level of opening code fences and only treat  appears inside code blocks (e.g., in Python strings containing markdown examples).
@cursor
Copy link

cursor bot commented Jan 23, 2026

Bugbot Autofix resolved the bug found in the latest run.

  • ✅ Fixed: Code block state tracking fails on embedded fences
    • Modified normalizeIndentation to track opening fence indentation and only treat ``` lines as closing fences when not indented more than the opening fence, preventing false toggles for embedded fence markers.

Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is ON. A Cloud Agent has been kicked off to fix the reported issue.

The bug was caused by calling trim() before dedent(), which removed
leading whitespace from the first line and caused dedent to calculate
minIndent as 0. This left subsequent lines with incorrect indentation.

Fixed by swapping the order to dedent(content).trim() at all four
occurrences (lines 256, 260, 264, and 300).
@cursor
Copy link

cursor bot commented Jan 23, 2026

Bugbot Autofix resolved the bug found in the latest run.

  • ✅ Fixed: Wrong operation order causes incorrect indentation in extracted content
    • Swapped operation order from dedent(content.trim()) to dedent(content).trim() at all four occurrences (lines 256, 260, 264, and 300) to ensure dedent calculates minimum indentation correctly before trimming.

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.

5 participants