From d2bb82c79ec4d8202ea60571db03a599134fd25a Mon Sep 17 00:00:00 2001 From: Rae Sharp Date: Wed, 4 Feb 2026 17:27:46 -0500 Subject: [PATCH 1/4] Removes script and swizzles copy markdown button --- docusaurus.config.js | 1 - scripts/copymarkdown.js | 14 --------- src/components/CopyMarkdownButton.js | 42 -------------------------- src/theme/DocItem/Content/index.js | 45 ++++++++++++++++++++++++++++ 4 files changed, 45 insertions(+), 57 deletions(-) delete mode 100644 scripts/copymarkdown.js create mode 100644 src/theme/DocItem/Content/index.js diff --git a/docusaurus.config.js b/docusaurus.config.js index 6c0779f43..bf3f1cf46 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -12,7 +12,6 @@ const config = { organizationName: "upbound", projectName: "docs", onBrokenLinks: "warn", - clientModules: [require.resolve("./scripts/copymarkdown.js")], scripts: [ { src: "https://cdn-cookieyes.com/client_data/401fea7900d8d7b84b9e7b40/script.js", diff --git a/scripts/copymarkdown.js b/scripts/copymarkdown.js deleted file mode 100644 index 59200f459..000000000 --- a/scripts/copymarkdown.js +++ /dev/null @@ -1,14 +0,0 @@ -// scripts/copymarkdown.js -import { initCopyButton } from '../src/components/CopyMarkdownButton'; - -initCopyButton(); - -if (typeof window !== 'undefined') { - window.addEventListener('popstate', initCopyButton); - - const originalPushState = history.pushState; - history.pushState = function() { - originalPushState.apply(history, arguments); - setTimeout(initCopyButton, 100); - }; -} diff --git a/src/components/CopyMarkdownButton.js b/src/components/CopyMarkdownButton.js index 1a9eeb0dd..307ce47aa 100644 --- a/src/components/CopyMarkdownButton.js +++ b/src/components/CopyMarkdownButton.js @@ -574,46 +574,4 @@ Generated: ${new Date().toISOString()} ); }; -// Auto-initialize function that can be called from client module -export const initCopyButton = () => { - if (typeof window === "undefined") return; - - // Wait for page to be fully loaded - if (document.readyState === "loading") { - document.addEventListener("DOMContentLoaded", initCopyButton); - return; - } - // Only add on doc pages, not homepage or overview pages - if ( - window.location.pathname !== "/" && - window.location.pathname !== "/search" && - window.location.pathname !== "/guides/" && - window.location.pathname !== "/manuals/" && - window.location.pathname !== "/reference/" - ) { - // Remove any existing button first - const existing = document.querySelector( - ".copy-markdown-button-container" - ); - if (existing) existing.remove(); - - // Find the main title (h1) to insert button after it - const mainTitle = document.querySelector( - "main h1, article h1, .markdown h1" - ); - - if (mainTitle) { - // Create container and insert after title - const container = document.createElement("div"); - mainTitle.parentNode.insertBefore(container, mainTitle.nextSibling); - - // Use React 18 createRoot - import("react-dom/client").then(({ createRoot }) => { - const root = createRoot(container); - root.render(React.createElement(CopyMarkdownButton)); - }); - } - } -}; - export default CopyMarkdownButton; diff --git a/src/theme/DocItem/Content/index.js b/src/theme/DocItem/Content/index.js new file mode 100644 index 000000000..8e2c1bca1 --- /dev/null +++ b/src/theme/DocItem/Content/index.js @@ -0,0 +1,45 @@ +import React, { useEffect, useRef, useState } from 'react'; +import Content from '@theme-original/DocItem/Content'; +import CopyMarkdownButton from '@site/src/components/CopyMarkdownButton'; +import { useLocation } from '@docusaurus/router'; +import { createRoot } from 'react-dom/client'; + +export default function ContentWrapper(props) { + const location = useLocation(); + const contentRef = useRef(null); + const [buttonRendered, setButtonRendered] = useState(false); + + const shouldShowButton = location.pathname !== "/" && + location.pathname !== "/search" && + location.pathname !== "/guides/" && + location.pathname !== "/manuals/" && + location.pathname !== "/reference/"; + + useEffect(() => { + if (!shouldShowButton || buttonRendered) return; + + // Find the h1 element within the content + const h1Element = contentRef.current?.querySelector('h1'); + + if (h1Element) { + // Create a container for the button + const buttonContainer = document.createElement('div'); + buttonContainer.className = 'copy-markdown-button-wrapper'; + + // Insert the container right after the h1 + h1Element.parentNode.insertBefore(buttonContainer, h1Element.nextSibling); + + // Render the button into the container + const root = createRoot(buttonContainer); + root.render(); + + setButtonRendered(true); + } + }, [shouldShowButton, buttonRendered, location.pathname]); + + return ( +
+ +
+ ); +} From d944000555f5402f625de06cb6922022e82207e2 Mon Sep 17 00:00:00 2001 From: Rae Sharp Date: Wed, 4 Feb 2026 18:57:43 -0500 Subject: [PATCH 2/4] update the operator character in the code block in dark mode, add markdown list rendering --- src/css/custom.css | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/src/css/custom.css b/src/css/custom.css index 1ab54db9a..556e86c81 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -247,27 +247,37 @@ html[data-theme="dark"] [data-radix-popper-content-wrapper] [role="dialog"] a:ho text-decoration: none !important; } -article - a:not(.card):not(.menu__link):not(.navbar__item):not( - .table-of-contents__link - ):not(.cta-button) { +body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-size: var(--ifm-font-size-base); + line-height: var(--ifm-line-height-base); + font-weight: var(--ifm-font-weight-base); + min-width: 320px; + scroll-behavior: smooth; +} + +/* Scope list styles to article content only */ +article ul, +article ol { + list-style-position: inside !important; + display: block !important; + list-style-type: disc !important; +} +article a:not(.card):not(.menu__link):not(.navbar__item):not(.table-of-contents__link):not(.cta-button) { color: var(--grape); text-decoration: none; border-bottom: 1px solid var(--grape); transition: all 0.2s ease; } -article - a:not(.card):not(.menu__link):not(.navbar__item):not( - .table-of-contents__link - ):not(.cta-button):hover { +article a:not(.card):not(.menu__link):not(.navbar__item):not(.table-of-contents__link):not(.cta-button):hover { color: var(--link-hover-color); border-bottom-color: var(--link-hover-color); border-bottom-width: 2px; text-decoration: none; } - /* ============================================================ */ /* NAVBAR COMPONENT */ /* ============================================================ */ @@ -773,6 +783,12 @@ html[data-theme="dark"] .theme-admonition-danger .theme-admonition-heading h5 { color: rgba(238, 86, 165, 0.95); } +html[data-theme='dark'] .token.operator { + + color: inherit !important; + background: transparent !important; +} + /* ============================================================ */ /* FOOTER COMPONENT */ /* ============================================================ */ From b011eaaa9a429e5e7e390dd8c5e7bc04b2ae7cc3 Mon Sep 17 00:00:00 2001 From: Rae Sharp Date: Thu, 5 Feb 2026 03:09:45 -0500 Subject: [PATCH 3/4] Updating the copy markdown component --- docs/guides/index.md | 3 - docs/manuals/cli/overview.md | 4 - docs/manuals/index.md | 2 - docs/manuals/packages/overview.md | 3 - docs/manuals/platform/overview.md | 5 +- docs/manuals/spaces/overview.md | 4 +- docs/manuals/uxp/overview.md | 5 +- docs/reference/index.md | 3 - src/components/CopyMarkdownButton.js | 577 --------------------------- src/theme/DocItem/Content/index.js | 45 --- src/theme/MDXContent/index.js | 34 ++ 11 files changed, 37 insertions(+), 648 deletions(-) delete mode 100644 src/components/CopyMarkdownButton.js delete mode 100644 src/theme/DocItem/Content/index.js create mode 100644 src/theme/MDXContent/index.js diff --git a/docs/guides/index.md b/docs/guides/index.md index 349fadb82..9cde8b115 100644 --- a/docs/guides/index.md +++ b/docs/guides/index.md @@ -3,11 +3,8 @@ title: Guides Overview sidebar_position: 1 sidebar_label: Overview description: Learn what Upbound Crossplane is and how it works -hide_title: true --- -# Overview - import GuidesCards from '@site/src/components/GuidesCards'; This section contains end-to-end deployment guides, solution architectures, and diff --git a/docs/manuals/cli/overview.md b/docs/manuals/cli/overview.md index b59dca9f6..ea70e8886 100644 --- a/docs/manuals/cli/overview.md +++ b/docs/manuals/cli/overview.md @@ -4,11 +4,7 @@ sidebar_label: Overview sidebar_position: 1 description: Install Crossplane, interact with the Upbound Marketplace and Managed Control Planes with the Upbound Up CLI. -hide_title: true --- - -# Overview - The Upbound `up` command-line enables interaction with Upbound control planes. It also simplifies common workflows with Upbound Crossplane (UXP) and building Crossplane packages for the Upbound Marketplace or any OCI-compliant registry. diff --git a/docs/manuals/index.md b/docs/manuals/index.md index c90a72bec..d42263087 100644 --- a/docs/manuals/index.md +++ b/docs/manuals/index.md @@ -1,11 +1,9 @@ --- title: Manuals Overview sidebar_label: Overview -hide_title: true sidebar_position: 1 description: Learn about the deployment options for Upbound --- -# Overview import ManualsCards from '@site/src/components/ManualsCards'; This section contains detailed guides, configuration examples, and diff --git a/docs/manuals/packages/overview.md b/docs/manuals/packages/overview.md index 4c9e74adc..f455e96ac 100644 --- a/docs/manuals/packages/overview.md +++ b/docs/manuals/packages/overview.md @@ -3,11 +3,8 @@ title: Packages Overview sidebar_position: 1 description: Product documentation for the Official Packages offered by Upbound. sidebar_label: Overview -hide_title: true --- -# Overview - Crossplane supports these package types: `Configurations`, `Functions` and `Providers`. Upbound Crossplane (UXP) extends this with support for `Add-Ons`. * **`Add-On`** packages are Upbound Crossplane-only packages that extend your control plane capabilities. diff --git a/docs/manuals/platform/overview.md b/docs/manuals/platform/overview.md index ea5138b84..9c15da203 100644 --- a/docs/manuals/platform/overview.md +++ b/docs/manuals/platform/overview.md @@ -1,13 +1,10 @@ --- -title: Platform Overview +title: Platform Overview sidebar_label: Overview -hide_title: true sidebar_position: 1 description: Overview of platform --- -# Overview - This section covers identity management, access control, and other administrative features that allow you to configure team and organizational collaboration. diff --git a/docs/manuals/spaces/overview.md b/docs/manuals/spaces/overview.md index 741031d0e..a3ed343f7 100644 --- a/docs/manuals/spaces/overview.md +++ b/docs/manuals/spaces/overview.md @@ -1,11 +1,9 @@ --- -title: Spaces Overview +title: Spaces Overview sidebar_label: Overview -hide_title: true sidebar_position: 1 description: Spaces are a host environment in Upbound for control planes-as-a-service --- -# Overview diff --git a/docs/manuals/uxp/overview.md b/docs/manuals/uxp/overview.md index b97277f07..414c3361b 100644 --- a/docs/manuals/uxp/overview.md +++ b/docs/manuals/uxp/overview.md @@ -1,13 +1,10 @@ --- -title: UXP Overview +title: UXP Overview sidebar_label: Overview -hide_title: true sidebar_position: 1 description: Upbound Crossplane is the AI-native distribution of Crossplane by Upbound --- -# Overview - Upbound Crossplane (UXP) is the AI-native distribution of [Crossplane][crossplane] by Upbound. Crossplane is a framework for building your own [control plane][control-plane]. :::tip diff --git a/docs/reference/index.md b/docs/reference/index.md index 7fb3069e9..34e32672c 100644 --- a/docs/reference/index.md +++ b/docs/reference/index.md @@ -1,14 +1,11 @@ --- title: Reference Overview sidebar_label: Overview -hide_title: true sidebar_position: 1 description: Reference material for Upbound --- import ReferenceCards from '@site/src/components/ReferenceCards'; -# Overview - This section contains technical specifications, API documentation and reference materials for Upbound. diff --git a/src/components/CopyMarkdownButton.js b/src/components/CopyMarkdownButton.js deleted file mode 100644 index 307ce47aa..000000000 --- a/src/components/CopyMarkdownButton.js +++ /dev/null @@ -1,577 +0,0 @@ -// src/components/CopyMarkdownButton.js -import React, { useState, useEffect } from "react"; - -const styles = ` -.copy-markdown-button-container { - margin: 1rem 0; - display: flex; - justify-content: flex-start; -} - -@media (max-width: 768px) { - .copy-markdown-button-container { - margin: 0.75rem 0; - } -} - -.copy-page-btn { - position: relative; - display: inline-flex; - align-items: center; - gap: 0.375rem; - padding: 0.5rem 0.75rem; - background-color: var(--ifm-background-color); - border: 1px solid var(--upbound-border-color); - border-radius: 0.375rem; - color: var(--ifm-font-color-base); - font-family: var(--ifm-font-family-base); - font-size: 0.8125rem; - font-weight: 500; - cursor: pointer; - transition: all 0.15s ease; - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); - white-space: nowrap; - min-width: auto; -} - -.copy-page-btn:hover:not(:disabled) { - background-color: var(--upbound-light-bg); - border-color: var(--grape); - color: var(--grape); - transform: translateY(-1px); - box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); -} - -.copy-page-btn:active:not(:disabled) { - transform: translateY(0); -} - -.copy-page-btn:disabled { - opacity: 0.6; - cursor: not-allowed; -} - -.copy-page-btn svg { - width: 14px; - height: 14px; - flex-shrink: 0; - transition: transform 0.15s ease; -} - -.copy-page-btn:hover:not(:disabled) svg { - transform: scale(1.05); -} - -.copy-btn-text { - transition: all 0.15s ease; -} - -.copy-page-btn.success svg { - transform: scale(1.1); -} - -/* Loading animation */ -@keyframes spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } -} - -.copy-page-btn.loading svg { - animation: spin 1s linear infinite; -} - -/* Tooltip */ -.copy-tooltip { - position: absolute; - bottom: calc(100% + 8px); - left: 50%; - transform: translateX(-50%); - padding: 0.375rem 0.625rem; - background-color: var(--up-grey-800); - color: white; - border-radius: 0.25rem; - font-size: 0.6875rem; - font-weight: 500; - white-space: nowrap; - opacity: 0; - visibility: hidden; - transition: all 0.15s ease; - pointer-events: none; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); - z-index: 1000; -} - -.copy-tooltip::after { - content: ''; - position: absolute; - top: 100%; - left: 50%; - transform: translateX(-50%); - border: 4px solid transparent; - border-top-color: var(--up-grey-800); -} - -.copy-tooltip.show { - opacity: 1; - visibility: visible; -} - -/* Dark theme */ -html[data-theme='dark'] .copy-page-btn { - background-color: var(--up-grey-800); - border-color: var(--up-grey-700); - color: var(--up-grey-200); -} - -html[data-theme='dark'] .copy-page-btn:hover:not(:disabled) { - background-color: var(--up-grey-700); - border-color: var(--up-purple-300); - color: var(--up-purple-200); -} - -html[data-theme='dark'] .copy-tooltip { - background-color: var(--up-grey-700); - color: var(--up-grey-100); -} - -html[data-theme='dark'] .copy-tooltip::after { - border-top-color: var(--up-grey-700); -} - -/* Mobile adjustments */ -@media (max-width: 768px) { - .copy-page-btn { - padding: 0.4375rem 0.625rem; - font-size: 0.75rem; - gap: 0.3125rem; - } - - .copy-page-btn svg { - width: 13px; - height: 13px; - } - - .copy-tooltip { - font-size: 0.625rem; - padding: 0.3125rem 0.5rem; - } -} - -@media print { - .copy-markdown-button-container { - display: none; - } -} -`; - -const CopyMarkdownButton = () => { - const [state, setState] = useState("idle"); // 'idle', 'loading', 'success', 'error' - const [showTooltip, setShowTooltip] = useState(false); - const [stylesInjected, setStylesInjected] = useState(false); - - // Inject styles on component mount - useEffect(() => { - if (!stylesInjected) { - const styleElement = document.createElement("style"); - styleElement.textContent = styles; - document.head.appendChild(styleElement); - setStylesInjected(true); - } - }, [stylesInjected]); - - // Function to extract markdown content from the current page - const extractPageContent = () => { - const mainContent = - document.querySelector('main[role="main"]') || - document.querySelector(".main-wrapper") || - document.querySelector("article") || - document.querySelector(".markdown"); - - if (!mainContent) { - return "Unable to extract page content."; - } - - let markdown = ""; - - // Get page title - const title = - document.querySelector("h1")?.textContent || - document.querySelector("title")?.textContent || - "Untitled Page"; - - markdown += `# ${title}\n\n`; - - // Improved content extraction - const processElement = (element, level = 0) => { - let content = ""; - - // Skip unwanted elements - if ( - element.closest && - (element.closest(".navbar") || - element.closest(".menu") || - element.closest(".table-of-contents") || - element.closest(".pagination-nav") || - element.closest(".breadcrumbs") || - element.closest(".copy-markdown-button-container") || - element.closest('[class*="sidebar"]') || - element.closest("nav") || - (element.matches && - element.matches("script, style, noscript"))) - ) { - return ""; - } - - const tagName = element.tagName?.toLowerCase(); - - switch (tagName) { - case "h1": - // Skip main title as we already added it - if (element === document.querySelector("h1")) return ""; - content += `\n# ${element.textContent.trim()}\n\n`; - break; - case "h2": - content += `\n## ${element.textContent.trim()}\n\n`; - break; - case "h3": - content += `\n### ${element.textContent.trim()}\n\n`; - break; - case "h4": - content += `\n#### ${element.textContent.trim()}\n\n`; - break; - case "h5": - content += `\n##### ${element.textContent.trim()}\n\n`; - break; - case "h6": - content += `\n###### ${element.textContent.trim()}\n\n`; - break; - case "p": - const text = element.textContent?.trim(); - if (text && !element.closest("pre, code")) { - content += `${text}\n\n`; - } - break; - case "pre": - const codeElement = element.querySelector("code"); - const codeText = codeElement - ? codeElement.textContent - : element.textContent; - const language = - codeElement?.className?.match(/language-(\w+)/)?.[1] || - ""; - content += `\`\`\`${language}\n${codeText.trim()}\n\`\`\`\n\n`; - break; - case "blockquote": - const quoteLines = - element.textContent?.trim().split("\n") || []; - const quotedText = quoteLines - .map((line) => `> ${line.trim()}`) - .join("\n"); - content += `${quotedText}\n\n`; - break; - case "ul": - const ulItems = Array.from(element.children).filter( - (child) => child.tagName === "LI" - ); - ulItems.forEach((li) => { - content += `- ${li.textContent?.trim()}\n`; - }); - content += "\n"; - break; - case "ol": - const olItems = Array.from(element.children).filter( - (child) => child.tagName === "LI" - ); - olItems.forEach((li, index) => { - content += `${index + 1}. ${li.textContent?.trim()}\n`; - }); - content += "\n"; - break; - case "table": - const rows = Array.from(element.querySelectorAll("tr")); - if (rows.length > 0) { - rows.forEach((row, rowIndex) => { - const cells = Array.from( - row.querySelectorAll("td, th") - ); - const rowText = cells - .map((cell) => cell.textContent?.trim() || "") - .join(" | "); - content += `| ${rowText} |\n`; - - // Add separator after header row - if (rowIndex === 0) { - const separator = cells - .map(() => "---") - .join(" | "); - content += `| ${separator} |\n`; - } - }); - content += "\n"; - } - break; - case "div": - case "section": - case "article": - // For container elements, process children - Array.from(element.children).forEach((child) => { - content += processElement(child, level + 1); - }); - break; - default: - // For other elements, check if they have direct text content - if (element.children.length === 0) { - const text = element.textContent?.trim(); - if (text && text.length > 0) { - // Handle inline code - if (tagName === "code") { - content += `\`${text}\``; - } else if ( - tagName === "strong" || - tagName === "b" - ) { - content += `**${text}**`; - } else if (tagName === "em" || tagName === "i") { - content += `*${text}*`; - } else if (tagName === "a") { - const href = element.href; - if (href) { - content += `[${text}](${href})`; - } else { - content += text; - } - } else { - content += text; - } - } - } else { - // Has children, process them - Array.from(element.children).forEach((child) => { - content += processElement(child, level + 1); - }); - } - break; - } - - return content; - }; - - // Process all direct children of main content - Array.from(mainContent.children).forEach((child) => { - markdown += processElement(child); - }); - - // Clean up extra whitespace - markdown = markdown.replace(/\n{3,}/g, "\n\n").trim(); - - // Add metadata - const metadata = `--- -URL: ${window.location.href} -Title: ${title} -Generated: ${new Date().toISOString()} ---- - -`; - - return metadata + markdown; - }; - - const handleCopyClick = async () => { - setState("loading"); - - try { - const markdown = extractPageContent(); - await navigator.clipboard.writeText(markdown); - - setState("success"); - setShowTooltip(true); - - // Reset to idle after showing success - setTimeout(() => { - setState("idle"); - setShowTooltip(false); - }, 2000); - } catch (error) { - console.error("Failed to copy page content:", error); - - // Fallback for browsers that don't support clipboard API - try { - const textArea = document.createElement("textarea"); - textArea.value = extractPageContent(); - document.body.appendChild(textArea); - textArea.select(); - document.execCommand("copy"); - document.body.removeChild(textArea); - - setState("success"); - setShowTooltip(true); - - setTimeout(() => { - setState("idle"); - setShowTooltip(false); - }, 2000); - } catch (fallbackError) { - setState("error"); - setShowTooltip(true); - - setTimeout(() => { - setState("idle"); - setShowTooltip(false); - }, 3000); - } - } - }; - - const getButtonContent = () => { - switch (state) { - case "loading": - return { - icon: ( - - - - - ), - text: "Copying...", - }; - case "success": - return { - icon: ( - - - - ), - text: "Copied!", - }; - case "error": - return { - icon: ( - - - - - - ), - text: "Error", - }; - default: - return { - icon: ( - - - - - ), - text: "Copy as Markdown", - }; - } - }; - - const { icon, text } = getButtonContent(); - const tooltipText = - state === "success" - ? "Page copied to clipboard!" - : state === "error" - ? "Failed to copy" - : "Copy this page as Markdown for use with LLMs"; - - return ( -
- - -
- {tooltipText} -
-
- ); -}; - -export default CopyMarkdownButton; diff --git a/src/theme/DocItem/Content/index.js b/src/theme/DocItem/Content/index.js deleted file mode 100644 index 8e2c1bca1..000000000 --- a/src/theme/DocItem/Content/index.js +++ /dev/null @@ -1,45 +0,0 @@ -import React, { useEffect, useRef, useState } from 'react'; -import Content from '@theme-original/DocItem/Content'; -import CopyMarkdownButton from '@site/src/components/CopyMarkdownButton'; -import { useLocation } from '@docusaurus/router'; -import { createRoot } from 'react-dom/client'; - -export default function ContentWrapper(props) { - const location = useLocation(); - const contentRef = useRef(null); - const [buttonRendered, setButtonRendered] = useState(false); - - const shouldShowButton = location.pathname !== "/" && - location.pathname !== "/search" && - location.pathname !== "/guides/" && - location.pathname !== "/manuals/" && - location.pathname !== "/reference/"; - - useEffect(() => { - if (!shouldShowButton || buttonRendered) return; - - // Find the h1 element within the content - const h1Element = contentRef.current?.querySelector('h1'); - - if (h1Element) { - // Create a container for the button - const buttonContainer = document.createElement('div'); - buttonContainer.className = 'copy-markdown-button-wrapper'; - - // Insert the container right after the h1 - h1Element.parentNode.insertBefore(buttonContainer, h1Element.nextSibling); - - // Render the button into the container - const root = createRoot(buttonContainer); - root.render(); - - setButtonRendered(true); - } - }, [shouldShowButton, buttonRendered, location.pathname]); - - return ( -
- -
- ); -} diff --git a/src/theme/MDXContent/index.js b/src/theme/MDXContent/index.js new file mode 100644 index 000000000..edd2654a8 --- /dev/null +++ b/src/theme/MDXContent/index.js @@ -0,0 +1,34 @@ +import React, { useState, useRef } from "react"; +import { useLocation } from "@docusaurus/router"; +import OriginalMDXContent from "@theme-original/MDXContent"; + +export default function MDXContentWrapper({ children, ...props }) { + const [copied, setCopied] = useState(false); + const contentRef = useRef(); + const location = useLocation(); + + const excludedPaths = ["/guides/", "/manuals/", "/reference/"]; + const isHidden = excludedPaths.includes(location.pathname); + + const handleCopy = () => { + if (contentRef.current) { + navigator.clipboard + .writeText(contentRef.current.innerText) + .then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }); + } + }; + + return ( + <> + +
+ {children} +
+ + ); +} From e041ca2c493c67d1b6810f7c17bc7ff45471e87b Mon Sep 17 00:00:00 2001 From: Rae Sharp Date: Fri, 6 Feb 2026 13:29:47 -0500 Subject: [PATCH 4/4] removing custom code block component (copy line/no copy), removing hidden style on copy markdown --- src/theme/CodeBlock/index.js | 150 ---------------------------------- src/theme/MDXContent/index.js | 2 +- 2 files changed, 1 insertion(+), 151 deletions(-) delete mode 100644 src/theme/CodeBlock/index.js diff --git a/src/theme/CodeBlock/index.js b/src/theme/CodeBlock/index.js deleted file mode 100644 index 51a188df8..000000000 --- a/src/theme/CodeBlock/index.js +++ /dev/null @@ -1,150 +0,0 @@ -// src/theme/CodeBlock/index.js -import React, { useEffect, useRef } from 'react'; -import CodeBlock from '@theme-original/CodeBlock'; -import { useLocation } from '@docusaurus/router'; - -function CodeBlockWithCopyLines(props) { - const codeBlockRef = useRef(null); - const location = useLocation(); - - const metastring = props.metastring || ''; - - const disableCopyMatch = metastring.match(/copy-lines=["|']?none["|']?/i); - const disableAllCopy = disableCopyMatch !== null; - - let copyLinesMatch = null; - let copyLinesStr = ''; - try { - copyLinesMatch = metastring.match(/copy-lines=["|']?([\d,-]+)["|']?/i); - copyLinesStr = copyLinesMatch ? copyLinesMatch[1] : ''; - } catch (e) { - console.error('Error parsing copy-lines:', e); - } - - const hasCopyLines = copyLinesStr !== ''; - - function parseLineRanges(rangeStr) { - if (!rangeStr) return []; - - try { - const lineNumbers = []; - rangeStr.split(',').forEach(part => { - part = part.trim(); - if (!part) return; - - if (part.includes('-')) { - const [startStr, endStr] = part.split('-'); - const start = parseInt(startStr, 10); - const end = parseInt(endStr, 10); - - if (!isNaN(start) && !isNaN(end)) { - for (let i = start; i <= end; i++) { - lineNumbers.push(i); - } - } - } else { - const num = parseInt(part, 10); - if (!isNaN(num)) { - lineNumbers.push(num); - } - } - }); - return lineNumbers; - } catch (e) { - console.error('Error parsing line ranges:', e); - return []; - } - } - - useEffect(() => { - if (!hasCopyLines && !disableAllCopy) return; - - const patchCopyButton = () => { - setTimeout(() => { - const wrapper = codeBlockRef.current; - if (!wrapper) return; - - const copyButton = wrapper.querySelector('button[aria-label="Copy code to clipboard"]'); - if (!copyButton) return; - - if (disableAllCopy) { - copyButton.style.display = 'none'; - return; - } - - if (hasCopyLines) { - const linesToCopy = parseLineRanges(copyLinesStr); - if (linesToCopy.length === 0) return; - - copyButton.setAttribute('aria-label', `Copy lines ${copyLinesStr}`); - copyButton.setAttribute('title', `Copy lines ${copyLinesStr}`); - - const originalClick = copyButton.onclick; - - copyButton.onclick = (e) => { - e.preventDefault(); - e.stopPropagation(); - - try { - const lineElements = wrapper.querySelectorAll('.token-line'); - - if (!lineElements || lineElements.length === 0) { - console.error('Could not find line elements'); - return; - } - - const linesToExtract = linesToCopy - .filter(lineNum => lineNum > 0 && lineNum <= lineElements.length); - - const contentLines = linesToExtract.map(lineNum => { - const lineElement = lineElements[lineNum - 1]; - return lineElement ? lineElement.textContent : ''; - }); - - const content = contentLines.join('\n'); - console.log('Copying content:', content); - - navigator.clipboard.writeText(content) - .then(() => { - copyButton.classList.add('copied'); - - const svgElements = copyButton.querySelectorAll('svg'); - if (svgElements.length >= 2) { - svgElements[0].style.display = 'none'; - svgElements[1].style.display = 'block'; - } - - setTimeout(() => { - copyButton.classList.remove('copied'); - if (svgElements.length >= 2) { - svgElements[0].style.display = 'block'; - svgElements[1].style.display = 'none'; - } - }, 2000); - }) - .catch(error => { - console.error('Failed to copy: ', error); - }); - } catch (error) { - console.error('Error in custom copy handler:', error); - if (originalClick) originalClick(e); - } - }; - - } - }, 100); - }; - - patchCopyButton(); - - return () => {}; - }, [disableAllCopy, hasCopyLines, copyLinesStr, location.pathname]); - - return ( -
- -
- ); -} - -export default CodeBlockWithCopyLines; diff --git a/src/theme/MDXContent/index.js b/src/theme/MDXContent/index.js index edd2654a8..d74dd5afd 100644 --- a/src/theme/MDXContent/index.js +++ b/src/theme/MDXContent/index.js @@ -23,7 +23,7 @@ export default function MDXContentWrapper({ children, ...props }) { return ( <> -