Add Copy Markdown and View Raw features to documentation docs#158
Add Copy Markdown and View Raw features to documentation docs#158thibaudcolas wants to merge 1 commit intomainfrom
Conversation
This change adds a "Copy Markdown" button and a "Raw" link to the documentation footer, making it easier for users to extract content for use with LLMs. - Swizzled the EditThisPage component to add new actions. - Implemented "Copy Markdown" functionality using the GitHub raw content API. - Added a direct "Raw" link to the source file on GitHub. - Added styling to ensure visual consistency with the existing theme. - Included internationalization support for the new labels. Co-authored-by: thibaudcolas <877585+thibaudcolas@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
📝 WalkthroughWalkthroughIntroduces EditThisPageWrapper component that extends EditThisPage with copy-to-clipboard and raw link functionality. The wrapper adds a button to copy Markdown content from GitHub and a link to view the raw source, with appropriate error handling and UI state feedback. Changes
Sequence DiagramsequenceDiagram
participant User
participant Component as EditThisPageWrapper
participant GitHub as GitHub API
participant Clipboard as Clipboard API
User->>Component: Click "Copy Markdown"
activate Component
Component->>Component: Transform edit URL to raw URL
Component->>GitHub: Fetch raw Markdown content
activate GitHub
GitHub-->>Component: Return raw content
deactivate GitHub
Component->>Clipboard: Copy content to clipboard
activate Clipboard
Clipboard-->>Component: Success/Error
deactivate Clipboard
Component->>Component: Update UI state ("Copied!" or error)
Component-->>User: Show feedback (2 sec timeout)
deactivate Component
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@src/theme/EditThisPage/index.tsx`:
- Around line 63-68: The current Translate usage embeds a ternary inside its
children which prevents both variants from being extracted; update the JSX in
EditThisPage/index.tsx to render two distinct Translate components keyed to the
different states of the copied variable (e.g., use separate IDs like
"theme.common.copyMarkdown.copied" for "Copied!" and
"theme.common.copyMarkdown.copy" for "Copy Markdown") and switch between them
with the copied conditional (copied ? <Translate id="...copied">...</Translate>
: <Translate id="...copy">...</Translate>), so each string is statically
extractable for i18n.
🧹 Nitpick comments (4)
src/theme/EditThisPage/index.tsx (4)
35-36: Consider cleanup forsetTimeouton unmount.If the component unmounts before the 2-second timeout completes,
setCopied(false)will be called on an unmounted component. While React 19 handles this gracefully, it's cleaner to useuseEffectcleanup or store the timeout ID for cancellation.♻️ Suggested improvement using useRef
-import { type ReactNode, useState } from "react" +import { type ReactNode, useState, useRef, useEffect } from "react" ... export default function EditThisPageWrapper(props: Props): ReactNode { const { editUrl } = props const [copied, setCopied] = useState(false) + const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null) + + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } + } + }, []) const handleCopy = async () => { ... setCopied(true) - setTimeout(() => setCopied(false), 2000) + timeoutRef.current = setTimeout(() => setCopied(false), 2000) ... }
24-26: DRY: URL transformation logic is duplicated.The same URL transformation appears in both
handleCopy(lines 24-26) and in the render scope (lines 43-45). Consider extracting to a helper function.♻️ Suggested extraction
+const toRawGitHubUrl = (editUrl: string): string => + editUrl + .replace("github.com", "raw.githubusercontent.com") + .replace("/edit/", "/") + export default function EditThisPageWrapper(props: Props): ReactNode { const { editUrl } = props const [copied, setCopied] = useState(false) + const rawUrl = editUrl ? toRawGitHubUrl(editUrl) : undefined const handleCopy = async () => { - if (!editUrl) { + if (!rawUrl) { return } - // Convert GitHub edit URL to raw URL - // From: https://github.com/thibaudcolas/draftail.org/edit/main/docs/introduction/getting-started.md - // To: https://raw.githubusercontent.com/thibaudcolas/draftail.org/main/docs/introduction/getting-started.md - const rawUrl = editUrl - .replace("github.com", "raw.githubusercontent.com") - .replace("/edit/", "/") - try { const response = await fetch(rawUrl) ... } - const rawUrl = editUrl - ?.replace("github.com", "raw.githubusercontent.com") - .replace("/edit/", "/")Also applies to: 43-45
37-40: Consider providing user feedback on copy failure.Currently, errors are only logged to the console. Users won't know the copy failed. Consider showing a brief error state or toast notification.
💡 Example approach with error state
const [copied, setCopied] = useState(false) + const [error, setError] = useState(false) const handleCopy = async () => { ... } catch (err) { // eslint-disable-next-line no-console console.error("Failed to copy markdown", err) + setError(true) + setTimeout(() => setError(false), 2000) } }Then render an error indicator in the UI when
erroris true.
73-77: Consider addingrel="noreferrer"for security.While
noopenerprevents the new page from accessingwindow.opener, addingnoreferreralso prevents theRefererheader from being sent, which is a common best practice for external links.♻️ Suggested change
<a href={rawUrl} target="_blank" - rel="noopener noreferrer" + rel="noreferrer"Note:
noreferrerimpliesnoopenerin modern browsers, so you can simplify to justnoreferrer.
| <Translate | ||
| id="theme.common.copyMarkdown" | ||
| description="The link label to copy the Markdown source of the current page" | ||
| > | ||
| {copied ? "Copied!" : "Copy Markdown"} | ||
| </Translate> |
There was a problem hiding this comment.
i18n issue: Translate component used with dynamic content.
The Translate component is designed for static strings that get extracted at build time. Using a ternary inside the children means only one variant will be extracted, breaking translations.
Use separate Translate components for each state:
🛠️ Proposed fix
- <Translate
- id="theme.common.copyMarkdown"
- description="The link label to copy the Markdown source of the current page"
- >
- {copied ? "Copied!" : "Copy Markdown"}
- </Translate>
+ {copied ? (
+ <Translate
+ id="theme.common.copiedMarkdown"
+ description="The label shown after successfully copying the Markdown source"
+ >
+ Copied!
+ </Translate>
+ ) : (
+ <Translate
+ id="theme.common.copyMarkdown"
+ description="The link label to copy the Markdown source of the current page"
+ >
+ Copy Markdown
+ </Translate>
+ )}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <Translate | |
| id="theme.common.copyMarkdown" | |
| description="The link label to copy the Markdown source of the current page" | |
| > | |
| {copied ? "Copied!" : "Copy Markdown"} | |
| </Translate> | |
| {copied ? ( | |
| <Translate | |
| id="theme.common.copiedMarkdown" | |
| description="The label shown after successfully copying the Markdown source" | |
| > | |
| Copied! | |
| </Translate> | |
| ) : ( | |
| <Translate | |
| id="theme.common.copyMarkdown" | |
| description="The link label to copy the Markdown source of the current page" | |
| > | |
| Copy Markdown | |
| </Translate> | |
| )} |
🤖 Prompt for AI Agents
In `@src/theme/EditThisPage/index.tsx` around lines 63 - 68, The current Translate
usage embeds a ternary inside its children which prevents both variants from
being extracted; update the JSX in EditThisPage/index.tsx to render two distinct
Translate components keyed to the different states of the copied variable (e.g.,
use separate IDs like "theme.common.copyMarkdown.copied" for "Copied!" and
"theme.common.copyMarkdown.copy" for "Copy Markdown") and switch between them
with the copied conditional (copied ? <Translate id="...copied">...</Translate>
: <Translate id="...copy">...</Translate>), so each string is statically
extractable for i18n.
Added "Copy Markdown" and "View Raw" features to the documentation pages to streamline content extraction for LLM usage. The implementation includes a swizzled
EditThisPagecomponent with interactive copy functionality and direct links to raw GitHub source files.PR created automatically by Jules for task 10322830930238481409 started by @thibaudcolas
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.