From 5145d42df7539c2c0743494f50cd0c11b8b1cef7 Mon Sep 17 00:00:00 2001 From: unclecode Date: Fri, 3 Oct 2025 20:09:48 +0800 Subject: [PATCH] fix(docs): hide copy menu on non-markdown pages --- docs/md_v2/assets/page_actions.js | 301 ++++++++++++++++++------------ 1 file changed, 186 insertions(+), 115 deletions(-) diff --git a/docs/md_v2/assets/page_actions.js b/docs/md_v2/assets/page_actions.js index 92893730..984a2c09 100644 --- a/docs/md_v2/assets/page_actions.js +++ b/docs/md_v2/assets/page_actions.js @@ -7,9 +7,12 @@ document.addEventListener('DOMContentLoaded', () => { githubRepo: 'unclecode/crawl4ai', githubBranch: 'main', docsPath: 'docs/md_v2', - excludePaths: ['/apps/c4a-script/', '/apps/llmtxt/', '/apps/crawl4ai-assistant/'], // Don't show on app pages + excludePaths: ['/apps/c4a-script/', '/apps/llmtxt/', '/apps/crawl4ai-assistant/', '/core/ask-ai/'], // Don't show on app pages }; + let cachedMarkdown = null; + let cachedMarkdownPath = null; + // Check if we should show the button on this page function shouldShowButton() { const currentPath = window.location.pathname; @@ -19,6 +22,17 @@ document.addEventListener('DOMContentLoaded', () => { return false; } + // Don't show on 404 pages + if (document.title && document.title.toLowerCase().includes('404')) { + return false; + } + + // Require mkdocs main content container + const mainContent = document.getElementById('terminal-mkdocs-main-content'); + if (!mainContent) { + return false; + } + // Don't show on excluded paths (apps) for (const excludePath of config.excludePaths) { if (currentPath.includes(excludePath)) { @@ -53,6 +67,56 @@ document.addEventListener('DOMContentLoaded', () => { return `${path}.md`; } + async function loadMarkdownContent() { + const mdPath = getCurrentMarkdownPath(); + + if (!mdPath) { + throw new Error('Invalid markdown path'); + } + + const rawUrl = getGithubRawUrl(); + const response = await fetch(rawUrl); + + if (!response.ok) { + throw new Error(`Failed to fetch markdown: ${response.status}`); + } + + const markdown = await response.text(); + cachedMarkdown = markdown; + cachedMarkdownPath = mdPath; + return markdown; + } + + async function ensureMarkdownCached() { + const mdPath = getCurrentMarkdownPath(); + + if (!mdPath) { + return false; + } + + if (cachedMarkdown && cachedMarkdownPath === mdPath) { + return true; + } + + try { + await loadMarkdownContent(); + return true; + } catch (error) { + console.warn('Page Actions: Markdown not available for this page.', error); + cachedMarkdown = null; + cachedMarkdownPath = null; + return false; + } + } + + async function getMarkdownContent() { + const available = await ensureMarkdownCached(); + if (!available) { + throw new Error('Markdown not available for this page.'); + } + return cachedMarkdown; + } + // Get GitHub raw URL for current page function getGithubRawUrl() { const mdPath = getCurrentMarkdownPath(); @@ -180,19 +244,11 @@ document.addEventListener('DOMContentLoaded', () => { // Copy markdown to clipboard async function copyMarkdownToClipboard(link) { - const rawUrl = getGithubRawUrl(); - // Add loading state link.classList.add('loading'); try { - const response = await fetch(rawUrl); - - if (!response.ok) { - throw new Error(`Failed to fetch markdown: ${response.status}`); - } - - const markdown = await response.text(); + const markdown = await getMarkdownContent(); // Copy to clipboard await navigator.clipboard.writeText(markdown); @@ -221,126 +277,141 @@ document.addEventListener('DOMContentLoaded', () => { window.open(githubUrl, '_blank', 'noopener,noreferrer'); } - // Initialize - const { button, dropdown, overlay } = createPageActionsUI(); - - // Event listeners - button.addEventListener('click', (e) => { - e.stopPropagation(); - toggleDropdown(button, dropdown, overlay); - }); - - overlay.addEventListener('click', () => { - closeDropdown(button, dropdown, overlay); - }); - - // Copy markdown action - document.getElementById('action-copy-markdown').addEventListener('click', async (e) => { - e.preventDefault(); - e.stopPropagation(); - await copyMarkdownToClipboard(e.currentTarget); - }); - - // View markdown action - document.getElementById('action-view-markdown').addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - viewMarkdown(); - closeDropdown(button, dropdown, overlay); - }); - - // Ask AI action (disabled for now) - document.getElementById('action-ask-ai').addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - // Future: Integrate with Ask AI feature - // For now, do nothing (disabled state) - }); - - // Close on ESC key - document.addEventListener('keydown', (e) => { - if (e.key === 'Escape' && dropdown.classList.contains('active')) { - closeDropdown(button, dropdown, overlay); + (async () => { + if (!shouldShowButton()) { + return; } - }); - // Close when clicking outside - document.addEventListener('click', (e) => { - if (!dropdown.contains(e.target) && !button.contains(e.target)) { - closeDropdown(button, dropdown, overlay); + const markdownAvailable = await ensureMarkdownCached(); + if (!markdownAvailable) { + return; } - }); - // Prevent dropdown from closing when clicking inside - dropdown.addEventListener('click', (e) => { - // Only stop propagation if not clicking on a link - if (!e.target.closest('.page-action-link')) { + const ui = createPageActionsUI(); + if (!ui) { + return; + } + + const { button, dropdown, overlay } = ui; + + // Event listeners + button.addEventListener('click', (e) => { e.stopPropagation(); - } - }); - - // Close dropdown on link click (except for copy which handles itself) - dropdown.querySelectorAll('.page-action-link:not(#action-copy-markdown)').forEach(link => { - link.addEventListener('click', () => { - if (!link.classList.contains('disabled')) { - setTimeout(() => { - closeDropdown(button, dropdown, overlay); - }, 100); - } + toggleDropdown(button, dropdown, overlay); }); - }); - // Handle window resize - let resizeTimer; - window.addEventListener('resize', () => { - clearTimeout(resizeTimer); - resizeTimer = setTimeout(() => { - // Close dropdown on resize to prevent positioning issues - if (dropdown.classList.contains('active')) { + overlay.addEventListener('click', () => { + closeDropdown(button, dropdown, overlay); + }); + + // Copy markdown action + document.getElementById('action-copy-markdown').addEventListener('click', async (e) => { + e.preventDefault(); + e.stopPropagation(); + await copyMarkdownToClipboard(e.currentTarget); + }); + + // View markdown action + document.getElementById('action-view-markdown').addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + viewMarkdown(); + closeDropdown(button, dropdown, overlay); + }); + + // Ask AI action (disabled for now) + document.getElementById('action-ask-ai').addEventListener('click', (e) => { + e.preventDefault(); + e.stopPropagation(); + // Future: Integrate with Ask AI feature + // For now, do nothing (disabled state) + }); + + // Close on ESC key + document.addEventListener('keydown', (e) => { + if (e.key === 'Escape' && dropdown.classList.contains('active')) { closeDropdown(button, dropdown, overlay); } - }, 250); - }); + }); - // Accessibility: Focus management - button.addEventListener('keydown', (e) => { - if (e.key === 'Enter' || e.key === ' ') { - e.preventDefault(); - toggleDropdown(button, dropdown, overlay); + // Close when clicking outside + document.addEventListener('click', (e) => { + if (!dropdown.contains(e.target) && !button.contains(e.target)) { + closeDropdown(button, dropdown, overlay); + } + }); - // Focus first menu item when opening - if (dropdown.classList.contains('active')) { - const firstLink = dropdown.querySelector('.page-action-link:not(.disabled)'); - if (firstLink) { - setTimeout(() => firstLink.focus(), 100); + // Prevent dropdown from closing when clicking inside + dropdown.addEventListener('click', (e) => { + // Only stop propagation if not clicking on a link + if (!e.target.closest('.page-action-link')) { + e.stopPropagation(); + } + }); + + // Close dropdown on link click (except for copy which handles itself) + dropdown.querySelectorAll('.page-action-link:not(#action-copy-markdown)').forEach(link => { + link.addEventListener('click', () => { + if (!link.classList.contains('disabled')) { + setTimeout(() => { + closeDropdown(button, dropdown, overlay); + }, 100); + } + }); + }); + + // Handle window resize + let resizeTimer; + window.addEventListener('resize', () => { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(() => { + // Close dropdown on resize to prevent positioning issues + if (dropdown.classList.contains('active')) { + closeDropdown(button, dropdown, overlay); + } + }, 250); + }); + + // Accessibility: Focus management + button.addEventListener('keydown', (e) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + toggleDropdown(button, dropdown, overlay); + + // Focus first menu item when opening + if (dropdown.classList.contains('active')) { + const firstLink = dropdown.querySelector('.page-action-link:not(.disabled)'); + if (firstLink) { + setTimeout(() => firstLink.focus(), 100); + } } } - } - }); + }); - // Arrow key navigation within menu - dropdown.addEventListener('keydown', (e) => { - if (!dropdown.classList.contains('active')) return; + // Arrow key navigation within menu + dropdown.addEventListener('keydown', (e) => { + if (!dropdown.classList.contains('active')) return; - const links = Array.from(dropdown.querySelectorAll('.page-action-link:not(.disabled)')); - const currentIndex = links.indexOf(document.activeElement); + const links = Array.from(dropdown.querySelectorAll('.page-action-link:not(.disabled)')); + const currentIndex = links.indexOf(document.activeElement); - if (e.key === 'ArrowDown') { - e.preventDefault(); - const nextIndex = (currentIndex + 1) % links.length; - links[nextIndex].focus(); - } else if (e.key === 'ArrowUp') { - e.preventDefault(); - const prevIndex = (currentIndex - 1 + links.length) % links.length; - links[prevIndex].focus(); - } else if (e.key === 'Home') { - e.preventDefault(); - links[0].focus(); - } else if (e.key === 'End') { - e.preventDefault(); - links[links.length - 1].focus(); - } - }); + if (e.key === 'ArrowDown') { + e.preventDefault(); + const nextIndex = (currentIndex + 1) % links.length; + links[nextIndex].focus(); + } else if (e.key === 'ArrowUp') { + e.preventDefault(); + const prevIndex = (currentIndex - 1 + links.length) % links.length; + links[prevIndex].focus(); + } else if (e.key === 'Home') { + e.preventDefault(); + links[0].focus(); + } else if (e.key === 'End') { + e.preventDefault(); + links[links.length - 1].focus(); + } + }); - console.log('Page Actions initialized for:', getCurrentMarkdownPath()); + console.log('Page Actions initialized for:', getCurrentMarkdownPath()); + })(); }); \ No newline at end of file