fix(docs): hide copy menu on non-markdown pages

This commit is contained in:
unclecode
2025-10-03 20:09:48 +08:00
parent 80aa6c11d9
commit 5145d42df7

View File

@@ -7,9 +7,12 @@ document.addEventListener('DOMContentLoaded', () => {
githubRepo: 'unclecode/crawl4ai', githubRepo: 'unclecode/crawl4ai',
githubBranch: 'main', githubBranch: 'main',
docsPath: 'docs/md_v2', 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 // Check if we should show the button on this page
function shouldShowButton() { function shouldShowButton() {
const currentPath = window.location.pathname; const currentPath = window.location.pathname;
@@ -19,6 +22,17 @@ document.addEventListener('DOMContentLoaded', () => {
return false; 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) // Don't show on excluded paths (apps)
for (const excludePath of config.excludePaths) { for (const excludePath of config.excludePaths) {
if (currentPath.includes(excludePath)) { if (currentPath.includes(excludePath)) {
@@ -53,6 +67,56 @@ document.addEventListener('DOMContentLoaded', () => {
return `${path}.md`; 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 // Get GitHub raw URL for current page
function getGithubRawUrl() { function getGithubRawUrl() {
const mdPath = getCurrentMarkdownPath(); const mdPath = getCurrentMarkdownPath();
@@ -180,19 +244,11 @@ document.addEventListener('DOMContentLoaded', () => {
// Copy markdown to clipboard // Copy markdown to clipboard
async function copyMarkdownToClipboard(link) { async function copyMarkdownToClipboard(link) {
const rawUrl = getGithubRawUrl();
// Add loading state // Add loading state
link.classList.add('loading'); link.classList.add('loading');
try { try {
const response = await fetch(rawUrl); const markdown = await getMarkdownContent();
if (!response.ok) {
throw new Error(`Failed to fetch markdown: ${response.status}`);
}
const markdown = await response.text();
// Copy to clipboard // Copy to clipboard
await navigator.clipboard.writeText(markdown); await navigator.clipboard.writeText(markdown);
@@ -221,126 +277,141 @@ document.addEventListener('DOMContentLoaded', () => {
window.open(githubUrl, '_blank', 'noopener,noreferrer'); window.open(githubUrl, '_blank', 'noopener,noreferrer');
} }
// Initialize (async () => {
const { button, dropdown, overlay } = createPageActionsUI(); if (!shouldShowButton()) {
return;
// 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);
} }
});
// Close when clicking outside const markdownAvailable = await ensureMarkdownCached();
document.addEventListener('click', (e) => { if (!markdownAvailable) {
if (!dropdown.contains(e.target) && !button.contains(e.target)) { return;
closeDropdown(button, dropdown, overlay);
} }
});
// Prevent dropdown from closing when clicking inside const ui = createPageActionsUI();
dropdown.addEventListener('click', (e) => { if (!ui) {
// Only stop propagation if not clicking on a link return;
if (!e.target.closest('.page-action-link')) { }
const { button, dropdown, overlay } = ui;
// Event listeners
button.addEventListener('click', (e) => {
e.stopPropagation(); e.stopPropagation();
} toggleDropdown(button, dropdown, overlay);
});
// 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 overlay.addEventListener('click', () => {
let resizeTimer; closeDropdown(button, dropdown, overlay);
window.addEventListener('resize', () => { });
clearTimeout(resizeTimer);
resizeTimer = setTimeout(() => { // Copy markdown action
// Close dropdown on resize to prevent positioning issues document.getElementById('action-copy-markdown').addEventListener('click', async (e) => {
if (dropdown.classList.contains('active')) { 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); closeDropdown(button, dropdown, overlay);
} }
}, 250); });
});
// Accessibility: Focus management // Close when clicking outside
button.addEventListener('keydown', (e) => { document.addEventListener('click', (e) => {
if (e.key === 'Enter' || e.key === ' ') { if (!dropdown.contains(e.target) && !button.contains(e.target)) {
e.preventDefault(); closeDropdown(button, dropdown, overlay);
toggleDropdown(button, dropdown, overlay); }
});
// Focus first menu item when opening // Prevent dropdown from closing when clicking inside
if (dropdown.classList.contains('active')) { dropdown.addEventListener('click', (e) => {
const firstLink = dropdown.querySelector('.page-action-link:not(.disabled)'); // Only stop propagation if not clicking on a link
if (firstLink) { if (!e.target.closest('.page-action-link')) {
setTimeout(() => firstLink.focus(), 100); 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 // Arrow key navigation within menu
dropdown.addEventListener('keydown', (e) => { dropdown.addEventListener('keydown', (e) => {
if (!dropdown.classList.contains('active')) return; if (!dropdown.classList.contains('active')) return;
const links = Array.from(dropdown.querySelectorAll('.page-action-link:not(.disabled)')); const links = Array.from(dropdown.querySelectorAll('.page-action-link:not(.disabled)'));
const currentIndex = links.indexOf(document.activeElement); const currentIndex = links.indexOf(document.activeElement);
if (e.key === 'ArrowDown') { if (e.key === 'ArrowDown') {
e.preventDefault(); e.preventDefault();
const nextIndex = (currentIndex + 1) % links.length; const nextIndex = (currentIndex + 1) % links.length;
links[nextIndex].focus(); links[nextIndex].focus();
} else if (e.key === 'ArrowUp') { } else if (e.key === 'ArrowUp') {
e.preventDefault(); e.preventDefault();
const prevIndex = (currentIndex - 1 + links.length) % links.length; const prevIndex = (currentIndex - 1 + links.length) % links.length;
links[prevIndex].focus(); links[prevIndex].focus();
} else if (e.key === 'Home') { } else if (e.key === 'Home') {
e.preventDefault(); e.preventDefault();
links[0].focus(); links[0].focus();
} else if (e.key === 'End') { } else if (e.key === 'End') {
e.preventDefault(); e.preventDefault();
links[links.length - 1].focus(); links[links.length - 1].focus();
} }
}); });
console.log('Page Actions initialized for:', getCurrentMarkdownPath()); console.log('Page Actions initialized for:', getCurrentMarkdownPath());
})();
}); });