Skip to content

Fix markdown twins and "Open Markdown" button on API reference pages - #2644

Open
kaankacar wants to merge 10 commits into
mainfrom
2643-fix-api-reference-markdown
Open

Fix markdown twins and "Open Markdown" button on API reference pages#2644
kaankacar wants to merge 10 commits into
mainfrom
2643-fix-api-reference-markdown

Conversation

@kaankacar

@kaankacar kaankacar commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

Closes #2643.

Follow-up to #2289: API reference pages (generated by docusaurus-plugin-openapi-docs) were the one page type the markdown feature didn't handle — their .md twins were near-empty and the "Open Markdown" button never showed up on them. Both problems come from those pages using a different layout: the operation content (parameters, schemas, examples) is client-rendered, and there is no article .markdown header element for the plugin to inject its button into.

Part 1: complete .md twins for API operation pages

New postbuild script scripts/generate_api_markdown.mjs, chained before rewrite_md_links.mjs:

  • Every docs/**/api-reference/**/*.api.mdx source file carries the complete, dereferenced operation object in its api: frontmatter field (zlib+base64, written by the openapi plugin — the exact data the page renders client-side). The script decodes that and emits clean markdown: method + path, base URLs, auth, parameter tables, request body, response schemas as an indented tree, and JSON examples.
  • Output goes over the near-empty twin at the matching build/**/api-reference/**/<id>.md path. All 127 operation pages across the four bundled specs (Horizon, Anchor Platform platform + callbacks, SDP) are covered.
  • Same invariants as the shipped feature: output-only (writes only build/**/*.md), no HTML or repo-source changes, idempotent, scoped strictly to api-reference pages.

Example (platform/transactions/get-transaction.md): ~39 tokens before, now the full operation (~5k tokens) — still roughly a third of the rendered HTML's token count.

Part 1b: hand-written api-reference pages (<AttributeTable> and friends)

The plugin drops JSX blocks including their markdown children when it builds a twin, so the 76 hand-written api-reference pages that keep their content inside <AttributeTable>, <ExampleResponse>, <CodeExample>, or <MethodTable> lost it. Example: the Fee Stats object twin was 254 bytes, missing the entire attribute list that sits as plain markdown in the source.

A second pass in the same script regenerates those twins from the .mdx source with the component tags unwrapped and children kept:

  • Fence-aware line walking, so imports or tag-like lines inside fenced code are never touched.
  • A twin is only overwritten when it provably matches the plugin's own lossy output for that source (whitespace- and link-target-insensitive compare); anything unrecognized is left alone with a warning.
  • Link targets are masked in the comparison because rewrite_md_links.mjs runs after this script, which keeps the pass idempotent across rebuilds.

RPC method pages still leak their <RpcMethod /> tag and get no operation content from the OpenRPC spec; that stays a follow-up. Their surrounding code examples do survive now via this pass.

Part 2: "Open Markdown" button on API operation pages

New client module src/clientModules/apiReferenceMarkdownButton.js:

  • On /api-reference/ routes it injects the plugin's own MarkdownActionsDropdown component (same markup, same behavior, same .markdown-actions-container styling) right after h1.openapi__heading — the element operation pages do have.
  • Guarded against double-injection: it only acts on operation pages (identified by that heading) and only when no .markdown-actions-container exists yet, so regular pages — including prose pages that live under /api-reference/ routes, where the plugin already injects — are left alone. A MutationObserver handles late hydration and client-side navigation, mirroring the plugin's own approach.

Verification

  • Full build passes; postbuild logs both scripts.
  • Spot-checked generated markdown for Horizon, Anchor Platform, and SDP operations.
  • build/**/*.html byte-identical before/after the postbuild scripts (hash check over all HTML files).
  • Scripts idempotent: re-running writes 0 files.
  • Prose twins byte-identical to production (spot-checked against developers.stellar.org).
  • Button verified via headless Chrome against a served build: exactly one .markdown-actions-container on operation pages, linking to the page's .md; normal pages still show exactly one (the plugin's).

RPC method pages (docs/data/apis/rpc/api-reference/methods/**) are a different mechanism (OpenRPC spec rendered by the custom RpcMethod component) and are not covered here — their twins already carry the prose/SDK-guide content. Can be a follow-up if wanted.

The markdown-source plugin builds each page's .md twin from static page
content, but API reference pages render their operation content (parameters,
schemas, examples) client-side, so their twins came out as just the one-line
description (~39 tokens vs ~14k tokens of rendered HTML).

Rebuild those twins from the operation data itself: every *.api.mdx source
carries the complete dereferenced operation object in its api: frontmatter
field (zlib+base64, written by docusaurus-plugin-openapi-docs). Decode it and
emit full markdown - method + path, base URLs, auth, parameter tables,
request body, response schema trees, and JSON examples - over the near-empty
twin under build/. Covers all 127 operation pages across the four bundled
specs (Horizon, Anchor Platform platform + callbacks, SDP).

Output-only like rewrite_md_links.mjs (which now runs after it in postbuild):
writes only build/**/api-reference/**/*.md, leaves HTML and prose twins
byte-identical, and is idempotent.

Part 1 of #2643.
The markdown-source plugin injects its actions dropdown into
'article .markdown header', which regular doc pages have but OpenAPI
operation pages don't (they render h1.openapi__heading and no header),
so the button never appeared on them.

Add a small client module that injects the plugin's own dropdown component
right after that heading, on /api-reference/ routes only and only when no
.markdown-actions-container exists yet - so regular pages (including prose
pages under /api-reference/ routes, where the plugin already injects) are
untouched and nothing is ever doubled. A MutationObserver handles late
hydration and client-side navigation, mirroring the plugin's own approach.

Part 2 of #2643.
Copilot AI review requested due to automatic review settings July 20, 2026 14:10
Comment thread scripts/generate_api_markdown.mjs Fixed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds complete Markdown twins and the “Open Markdown” control for OpenAPI operation pages.

Changes:

  • Generates Markdown from embedded OpenAPI operation data.
  • Injects the Markdown dropdown on API operation pages.
  • Adds generation to the postbuild pipeline.

Recommendation: NEEDS-CHANGES — generated output omits schema details, allOf descriptions, and response headers.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 3 comments.

File Description
src/clientModules/apiReferenceMarkdownButton.js Injects the Markdown dropdown.
scripts/generate_api_markdown.mjs Generates OpenAPI Markdown twins.
package.json Adds generation to postbuild.
docusaurus.config.ts Registers the client module.

Comment thread scripts/generate_api_markdown.mjs
Comment thread scripts/generate_api_markdown.mjs Outdated
Comment thread scripts/generate_api_markdown.mjs
@stellar-jenkins-ci

Copy link
Copy Markdown

@kaankacar kaankacar self-assigned this Jul 20, 2026
kaankacar and others added 3 commits July 30, 2026 16:02
…caping or encoding'

Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Comment thread scripts/generate_api_markdown.mjs Fixed
@stellar-jenkins-ci

Copy link
Copy Markdown

2 similar comments
@stellar-jenkins-ci

Copy link
Copy Markdown

@stellar-jenkins-ci

Copy link
Copy Markdown

Same incomplete-sanitization pattern CodeQL flagged on the parameter
table (alert 21): escaping pipes without escaping backslashes first.
Addresses the remaining Copilot review comment: the schema detail
bullets now include the property's example value (inline, capped at
300 chars), its pattern regex, and minItems/maxItems constraints.
Across the 127 operation blobs that is 758 examples, 191 patterns,
and 3 array size bounds that previously did not reach the twins.
@stellar-jenkins-ci

Copy link
Copy Markdown

@kaankacar
kaankacar requested a review from Copilot July 30, 2026 13:22

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@stellar-jenkins-ci

Copy link
Copy Markdown

The markdown-source-plugin drops JSX blocks together with their markdown
children when it builds a page's twin, so the 76 hand-written
api-reference pages that keep their content inside AttributeTable,
ExampleResponse, CodeExample, or MethodTable lost it: the Fee Stats
object twin was 254 bytes with the whole attribute list missing.

A second postbuild pass regenerates those twins from the .mdx source
with the component tags unwrapped and their children kept. Fence-aware
line walking keeps imports and tag-like lines inside code blocks
untouched, and a twin is only overwritten when it provably matches the
plugin's own lossy output for that source, so anything unrecognized is
left alone. Comparison masks link targets because rewrite_md_links.mjs
runs afterwards, which keeps the pass idempotent across rebuilds.
@stellar-jenkins-ci

Copy link
Copy Markdown

@kaankacar

Copy link
Copy Markdown
Contributor Author

Update for reviewers, three things landed since the initial review:

  1. Review feedback applied. All four Copilot/CodeQL threads are resolved: backslash escaping in both table renderers (CodeQL alerts closed, check is green), allOf member descriptions surfaced (Horizon's fee_charged/max_fee), response headers rendered (SDP download instructions), and the schema detail bullets now include property-level example, pattern, and minItems/maxItems.

  2. Merged main twice, picking up the WebMCP client module and the Wrap @theme-original/Root so the markdown plugin keeps its injection #2713 Root wrapper fix, so the preview now shows the plugin's button on prose pages next to this PR's button on API pages.

  3. New: Part 1b (see updated description). While comparing twins on the preview we found a second gap: the plugin drops JSX blocks together with their markdown children, so the 76 hand-written api-reference pages that keep content inside <AttributeTable>, <ExampleResponse>, <CodeExample>, or <MethodTable> had near-empty twins too (the Fee Stats object twin was 254 bytes with the whole attribute list missing). A second pass in the same postbuild script regenerates those twins with the component tags unwrapped and children kept. It is fence-aware (imports or tag-like lines inside code blocks are never touched) and it only overwrites a twin it can prove is the plugin's own lossy output for that source, everything else is left alone with a warning. Verified on a clean build: 127 operation twins plus 76 patched component twins, idempotent on re-run, links still rewritten by the existing postbuild step afterwards.

RPC method pages remain a known gap (their content is rendered by the custom RpcMethod component from the OpenRPC spec) and stay a follow-up, though their surrounding code examples now survive via the new pass.

@kaankacar
kaankacar requested a review from ElliotFriend July 30, 2026 15:38
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.

API reference pages: empty .md twins + missing "Open Markdown" button

3 participants