Skip to content

Expose per-page Markdown actions on the docs site#715

Open
dahlia wants to merge 8 commits intofedify-dev:mainfrom
dahlia:docs/markdown-button
Open

Expose per-page Markdown actions on the docs site#715
dahlia wants to merge 8 commits intofedify-dev:mainfrom
dahlia:docs/markdown-button

Conversation

@dahlia
Copy link
Copy Markdown
Member

@dahlia dahlia commented Apr 23, 2026

A documentation page with a small Markdown dropdown button aligned to the right of the page title A documentation page with the Markdown dropdown expanded, showing actions to view the page as Markdown or copy its Markdown content

Preview it at https://pr-715.fedify.pages.dev/intro.

Closes #706.

Summary

  • Add a compact per-page Markdown action to the docs so readers can open or copy the generated Markdown for the current page without guessing the output path.
  • Keep llms.txt and llms-full.txt focused by excluding high-noise pages such as the changelog, contribution guide, README, and sponsors page, while still exposing per-page Markdown actions on those pages.
  • Align the control with each page title so it stays out of the way on ordinary pages and does not overlap long multi-line headings.

Background

The docs build was already generating per-page Markdown through vitepress-plugin-llms, but there was no obvious way to reach it from a documentation page itself. In practice, that meant readers had to know about llms.txt ahead of time or guess the generated *.md URL by hand.

This change adds a visible page-level entry point for that Markdown representation while preserving the existing docs layout and keeping the global LLM-oriented indexes selective.

Implementation notes

  • Add a page-level Markdown dropdown to the docs theme.
  • Place the control next to the document title instead of leaving it in the content flow.
  • Adjust the title and action layout so long wrapped h1 headings do not collide with the control.
  • Keep llms.txt and llms-full.txt curated with per-output exclusions instead of hiding the affected pages from per-page Markdown entirely.
  • In the dev server, keep the menu visible but show an alert instead of triggering the generated Markdown route directly.

Testing

  • Verified the control on the running docs dev server with Playwright.
  • Checked desktop and mobile placement.
  • Checked long multi-line headings to confirm that the action does not overlap the title.

Add a compact per-page Markdown action to the docs so readers can
open or copy the generated Markdown for the current page without
guessing the output path.

Adjust the docs site integration so the action sits alongside page
titles, behaves safely in the dev server, and keeps llms.txt and
llms-full.txt curated by excluding high-noise pages.

Closes fedify-dev#706

Assisted-by: Codex:gpt-5.4
@dahlia dahlia added this to the Fedify 2.2 milestone Apr 23, 2026
@dahlia dahlia self-assigned this Apr 23, 2026
@dahlia dahlia added the type/documentation Improvements or additions to documentation label Apr 23, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a per-page Markdown UI to the docs site, refactors the vitepress-plugin-llms config to per-output ignore lists, registers a new Vue component that injects "View/Copy Markdown" actions into the page title area, and adds responsive CSS for the title/actions layout.

Changes

Cohort / File(s) Summary
Changelog
CHANGES.md
Adds a docs changelog entry announcing the per-page Markdown UI and defines issue/PR reference links ([#706], [#715]).
Docs config
docs/.vitepress/config.mts
Refactors plugin config from global ignoreFiles to ignoreFilesPerOutput; per-output ignores set to changelog.md, contribute.md, README.md, sponsors.md (so security.md and tutorial.md are no longer ignored).
Vue component
docs/.vitepress/theme/components/PageMarkdownActions.vue
New component that locates/injects a page-title actions target, computes the page .md URL from route.path (normalizes root/trailing slashes), provides a "Markdown" dropdown with "View as Markdown" and "Copy Markdown", handles dev vs. prod behavior, fetch/clipboard interactions, UI state timeouts, and cleanup on unmount.
Theme integration
docs/.vitepress/theme/index.ts
Registers PageMarkdownActions globally and injects it into the theme via an overridden Layout() that renders into the doc-before slot using h(...).
Styling
docs/.vitepress/theme/style.css
Adds responsive styles for the page title row: desktop (>=960px) flex layout with overflow handling; mobile (<960px) column layout with right-aligned actions.

Sequence Diagram(s)

sequenceDiagram
  participant Reader as Reader (Browser)
  participant Component as PageMarkdownActions
  participant DocsServer as VitePress Server
  participant Clipboard as Clipboard API

  Reader->>Component: Click "View as Markdown"
  Component->>Component: compute markdown URL from route.path
  Component->>DocsServer: open markdown URL (new tab)
  DocsServer-->>Reader: serve generated .md file

  Reader->>Component: Click "Copy Markdown"
  Component->>Component: compute markdown URL
  Component->>DocsServer: fetch markdown text
  DocsServer-->>Component: return markdown text (or dev notice)
  alt Dev server / unavailable
    Component-->>Reader: show alert "Unavailable in dev server"
  else Available
    Component->>Clipboard: navigator.clipboard.writeText(markdown)
    Clipboard-->>Component: success / failure
    Component-->>Reader: show "Copied" or "Copy failed" feedback
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: exposing a per-page Markdown action dropdown on the docs site for readers to view or copy page Markdown.
Description check ✅ Passed The description clearly explains the feature, implementation approach, and testing performed, directly relating to the changes in the changeset.
Linked Issues check ✅ Passed All coding requirements from #706 are met: per-page Markdown action added to theme, control placed next to title, per-output exclusions configured, and desktop/mobile layouts verified.
Out of Scope Changes check ✅ Passed All changes directly support the objective of exposing per-page Markdown actions; no extraneous modifications detected outside the linked issue scope.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ 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.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a per-page Markdown action to the documentation site, allowing users to view or copy the LLM-friendly Markdown for any page. Key changes include the addition of the PageMarkdownActions component, theme integration, and updated llmstxt plugin configuration. Review feedback identifies critical issues in the new component, such as a potential SSR crash caused by DOM access during the build process, a reactivity bug where the Markdown path does not update during navigation, and incorrect path resolution for index pages. A style guide violation regarding file name formatting in the changelog was also noted.

Comment thread docs/.vitepress/theme/components/PageMarkdownActions.vue
Comment thread CHANGES.md Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/components/PageMarkdownActions.vue`:
- Around line 69-87: The copyMarkdown function can leave copied.value true when
a later failure occurs (so the UI still shows "Copied"); in the catch block
reset copied.value to false and clear or cancel any pending success timer so
copyFailed takes effect; locate copyMarkdown and update the catch handling to
set copied.value = false (and clear the timeout used when setting copied.value
true) before setting copyFailed.value = true and starting its timeout.
- Around line 15-56: The code sets path only once in onMounted and registers the
watch outside of onMounted which causes stale markdown path and DOM access
before mount; change path to be a computed based on route.path (use computed(()
=> /* derive .md from route.path like current path logic */)) and replace usages
of path with this computed (markdownPath), and move the watch(...) call that
calls ensureTarget() into onMounted so ensureTarget() runs only after mount;
keep the ensureTarget() function unchanged but reference it from the
onMounted-initialized watcher and update any existing markdownPath variable
usage to the new computed one.
🪄 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 UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0ad09e6e-4d74-479b-9a65-fbfc9012680b

📥 Commits

Reviewing files that changed from the base of the PR and between cf6bb04 and c922f39.

📒 Files selected for processing (5)
  • CHANGES.md
  • docs/.vitepress/config.mts
  • docs/.vitepress/theme/components/PageMarkdownActions.vue
  • docs/.vitepress/theme/index.ts
  • docs/.vitepress/theme/style.css

Comment thread docs/.vitepress/theme/components/PageMarkdownActions.vue Outdated
Comment thread docs/.vitepress/theme/components/PageMarkdownActions.vue Outdated
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

dahlia added 2 commits April 23, 2026 14:34
Make the docs Markdown action follow VitePress route changes,
avoid DOM work before mount, and handle index-style page paths
correctly. This keeps the action pointed at the current page and
prevents stale UI state after copy failures.

fedify-dev#715 (comment)
fedify-dev#715 (comment)
fedify-dev#715 (comment)

Assisted-by: Codex:gpt-5.4
Format *README.md* in the changelog entry so the docs section
follows the repository's Markdown file path style.

fedify-dev#715 (comment)

Assisted-by: Codex:gpt-5.4
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/components/PageMarkdownActions.vue`:
- Line 115: The v-if on the Teleport uses a dead null check against the computed
markdownPath which always returns a string; remove the redundant "markdownPath
!= null &&" and leave only "targetReady" in the Teleport v-if (locate the
Teleport element in PageMarkdownActions.vue and the computed named markdownPath
to confirm behavior) so the conditional reflects only the meaningful guard.
- Around line 107-111: The viewMarkdown handler currently returns early in
production so the details dropdown stays open; change viewMarkdown(event) to
handle both dev and prod: if isDev keep the preventDefault() +
alert(devMessage), otherwise allow the link action and call closeMenu(event) so
the dropdown closes after navigation (or call closeMenu(event) after
window.open/navigation if you open a new tab manually). Also update copyMarkdown
to call closeMenu(event) at the end of its successful branch so the menu closes
after a successful copy; refer to the viewMarkdown, copyMarkdown and closeMenu
function names to locate the changes.
- Around line 22-55: ensureTarget() currently mutates the VitePress-rendered
<h1> and can pick up a stale or missing element after SPA navigation; modify
updateTarget/ensureTarget to wait reliably for the new .vp-doc h1 by adding a
retry strategy (e.g., loop with requestAnimationFrame or attach a scoped
MutationObserver to .vp-doc that resolves when an h1 appears) instead of relying
solely on nextTick(), and ensure you clean up any created wrapper/target on
unmount by moving wrapper/target creation into a stable Vue-managed element or
removing the created .page-title-row and .page-title-actions-target in an
onBeforeUnmount handler; reference ensureTarget(), updateTarget(), onMounted()
and targetReady when making these changes.
🪄 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 UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 189311f0-1074-469c-8b9a-2dc4f61915e8

📥 Commits

Reviewing files that changed from the base of the PR and between c922f39 and da76ca9.

📒 Files selected for processing (2)
  • CHANGES.md
  • docs/.vitepress/theme/components/PageMarkdownActions.vue

Comment thread docs/.vitepress/theme/components/PageMarkdownActions.vue Outdated
Comment thread docs/.vitepress/theme/components/PageMarkdownActions.vue
Comment thread docs/.vitepress/theme/components/PageMarkdownActions.vue Outdated
@issues-auto-labeler issues-auto-labeler Bot added component/build Build system and packaging component/cli CLI tools related labels Apr 23, 2026
Make the docs Markdown dropdown wait for the current page heading,
clean up its injected title wrapper on unmount, and close the menu
after actions so repeated navigation leaves no stale open state.

fedify-dev#715 (comment)
fedify-dev#715 (comment)
fedify-dev#715 (comment)

Assisted-by: Codex:gpt-5.4
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/components/PageMarkdownActions.vue`:
- Line 16: The title DOM is not restored before each route retarget which lets a
VitePress-rendered <h1> be trapped in a non-Vue wrapper and allows stale async
updateTarget completions to reattach old nodes; modify the component logic so
that ensureTarget() first calls cleanupTarget() (or an explicit unwrapTitle()
helper) to restore the current heading DOM before reparenting, and add a
cancellation/version token (e.g., incrementing requestId or using an AbortToken
checked in updateTarget's async path) so older async completions ignore and do
not retarget the DOM after a newer route change; update references in
PageMarkdownActions.vue to use the same token in ensureTarget(), updateTarget(),
and cleanupTarget() to locate and fix the behavior.
🪄 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 UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: e3085149-92cc-4bd8-8bd3-559e25ad5b43

📥 Commits

Reviewing files that changed from the base of the PR and between da76ca9 and 9c30731.

📒 Files selected for processing (1)
  • docs/.vitepress/theme/components/PageMarkdownActions.vue

Comment thread docs/.vitepress/theme/components/PageMarkdownActions.vue
Prevent stale async title retargeting from older route updates,
restore the wrapped heading before each new retarget, and
invalidate pending work during unmount. This same pass also makes
copy failures distinguish between Markdown load problems and
clipboard write failures.

fedify-dev#715 (comment)

Assisted-by: Codex:gpt-5.4
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/components/PageMarkdownActions.vue`:
- Around line 91-99: The SPA navigation can let a pending copy promise from the
previous page set stale feedback; update updateTarget() (which increments
targetUpdateVersion and calls ensureTarget) to also reset copied and copyFailed
refs (set copied.value = false and copyFailed.value = false) when retargeting,
and in the copy handler capture the current targetUpdateVersion at start and
before setting copied.value or copyFailed.value verify the captured version
matches targetUpdateVersion so stale async completions are ignored; reference
updateTarget(), targetUpdateVersion, copied, copyFailed and the copy handler to
locate and apply the changes (also apply the same resets/checks in the other
retargeting region around lines 143–179).
🪄 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 UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: aa291ef4-2cd5-4caf-afd1-1bd434572e69

📥 Commits

Reviewing files that changed from the base of the PR and between 9c30731 and 06979f5.

📒 Files selected for processing (1)
  • docs/.vitepress/theme/components/PageMarkdownActions.vue

Comment thread docs/.vitepress/theme/components/PageMarkdownActions.vue
Reset copy feedback when the docs page retargets and ignore clipboard
results that complete after a newer route update. This keeps copy
status messages from leaking across VitePress SPA navigation.

fedify-dev#715 (comment)

Assisted-by: Codex:gpt-5.4
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/.vitepress/theme/components/PageMarkdownActions.vue`:
- Around line 145-175: The post-await call to closeMenu in copyMarkdown is a
no-op because event.currentTarget is cleared after awaiting; fix by capturing
the actionable element (e.g., const trigger = (event.currentTarget as
HTMLElement | null) or the details element) before any await and pass that
captured element into closeMenu after the awaits, or alternatively change
closeMenu to accept an HTMLElement/details element and update viewMarkdown to
pass event.currentTarget the same way so both handlers consistently call
closeMenu with the element instead of relying on event after async awaits;
update references in copyMarkdown, viewMarkdown, and closeMenu accordingly (also
note getMarkdown and navigator.clipboard.writeText remain awaiting operations).
🪄 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 UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3387ae55-4d55-479c-b177-bbf11a4ca1bf

📥 Commits

Reviewing files that changed from the base of the PR and between 06979f5 and d52f2c5.

📒 Files selected for processing (1)
  • docs/.vitepress/theme/components/PageMarkdownActions.vue

Comment thread docs/.vitepress/theme/components/PageMarkdownActions.vue Outdated
Capture the dropdown trigger before awaiting Markdown fetch and
clipboard writes so the menu still closes after a successful copy.
This avoids relying on event.currentTarget after async suspension.

fedify-dev#715 (comment)

Assisted-by: Codex:gpt-5.4
@dahlia dahlia requested review from 2chanhaeng, malkoG and sij411 April 23, 2026 06:57
@github-actions
Copy link
Copy Markdown
Contributor

Pre-release has been published for this pull request:

Documentation

The docs for this pull request have been published:

https://fb23abf4.fedify.pages.dev

Leave the docs Markdown dropdown open after a successful copy so
readers can actually see the copied state feedback. Viewing the page
as Markdown still closes the menu, but copy now favors immediate UI
confirmation over collapsing the dropdown.

Assisted-by: Codex:gpt-5.4
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

component/build Build system and packaging component/cli CLI tools related type/documentation Improvements or additions to documentation

Development

Successfully merging this pull request may close these issues.

Expose per-page Markdown on the docs site

1 participant