Skip to content

Commit 5145d42

Browse files
committed
fix(docs): hide copy menu on non-markdown pages
1 parent 80aa6c1 commit 5145d42

File tree

1 file changed

+186
-115
lines changed

1 file changed

+186
-115
lines changed

docs/md_v2/assets/page_actions.js

Lines changed: 186 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ document.addEventListener('DOMContentLoaded', () => {
77
githubRepo: 'unclecode/crawl4ai',
88
githubBranch: 'main',
99
docsPath: 'docs/md_v2',
10-
excludePaths: ['/apps/c4a-script/', '/apps/llmtxt/', '/apps/crawl4ai-assistant/'], // Don't show on app pages
10+
excludePaths: ['/apps/c4a-script/', '/apps/llmtxt/', '/apps/crawl4ai-assistant/', '/core/ask-ai/'], // Don't show on app pages
1111
};
1212

13+
let cachedMarkdown = null;
14+
let cachedMarkdownPath = null;
15+
1316
// Check if we should show the button on this page
1417
function shouldShowButton() {
1518
const currentPath = window.location.pathname;
@@ -19,6 +22,17 @@ document.addEventListener('DOMContentLoaded', () => {
1922
return false;
2023
}
2124

25+
// Don't show on 404 pages
26+
if (document.title && document.title.toLowerCase().includes('404')) {
27+
return false;
28+
}
29+
30+
// Require mkdocs main content container
31+
const mainContent = document.getElementById('terminal-mkdocs-main-content');
32+
if (!mainContent) {
33+
return false;
34+
}
35+
2236
// Don't show on excluded paths (apps)
2337
for (const excludePath of config.excludePaths) {
2438
if (currentPath.includes(excludePath)) {
@@ -53,6 +67,56 @@ document.addEventListener('DOMContentLoaded', () => {
5367
return `${path}.md`;
5468
}
5569

70+
async function loadMarkdownContent() {
71+
const mdPath = getCurrentMarkdownPath();
72+
73+
if (!mdPath) {
74+
throw new Error('Invalid markdown path');
75+
}
76+
77+
const rawUrl = getGithubRawUrl();
78+
const response = await fetch(rawUrl);
79+
80+
if (!response.ok) {
81+
throw new Error(`Failed to fetch markdown: ${response.status}`);
82+
}
83+
84+
const markdown = await response.text();
85+
cachedMarkdown = markdown;
86+
cachedMarkdownPath = mdPath;
87+
return markdown;
88+
}
89+
90+
async function ensureMarkdownCached() {
91+
const mdPath = getCurrentMarkdownPath();
92+
93+
if (!mdPath) {
94+
return false;
95+
}
96+
97+
if (cachedMarkdown && cachedMarkdownPath === mdPath) {
98+
return true;
99+
}
100+
101+
try {
102+
await loadMarkdownContent();
103+
return true;
104+
} catch (error) {
105+
console.warn('Page Actions: Markdown not available for this page.', error);
106+
cachedMarkdown = null;
107+
cachedMarkdownPath = null;
108+
return false;
109+
}
110+
}
111+
112+
async function getMarkdownContent() {
113+
const available = await ensureMarkdownCached();
114+
if (!available) {
115+
throw new Error('Markdown not available for this page.');
116+
}
117+
return cachedMarkdown;
118+
}
119+
56120
// Get GitHub raw URL for current page
57121
function getGithubRawUrl() {
58122
const mdPath = getCurrentMarkdownPath();
@@ -180,19 +244,11 @@ document.addEventListener('DOMContentLoaded', () => {
180244

181245
// Copy markdown to clipboard
182246
async function copyMarkdownToClipboard(link) {
183-
const rawUrl = getGithubRawUrl();
184-
185247
// Add loading state
186248
link.classList.add('loading');
187249

188250
try {
189-
const response = await fetch(rawUrl);
190-
191-
if (!response.ok) {
192-
throw new Error(`Failed to fetch markdown: ${response.status}`);
193-
}
194-
195-
const markdown = await response.text();
251+
const markdown = await getMarkdownContent();
196252

197253
// Copy to clipboard
198254
await navigator.clipboard.writeText(markdown);
@@ -221,126 +277,141 @@ document.addEventListener('DOMContentLoaded', () => {
221277
window.open(githubUrl, '_blank', 'noopener,noreferrer');
222278
}
223279

224-
// Initialize
225-
const { button, dropdown, overlay } = createPageActionsUI();
226-
227-
// Event listeners
228-
button.addEventListener('click', (e) => {
229-
e.stopPropagation();
230-
toggleDropdown(button, dropdown, overlay);
231-
});
232-
233-
overlay.addEventListener('click', () => {
234-
closeDropdown(button, dropdown, overlay);
235-
});
236-
237-
// Copy markdown action
238-
document.getElementById('action-copy-markdown').addEventListener('click', async (e) => {
239-
e.preventDefault();
240-
e.stopPropagation();
241-
await copyMarkdownToClipboard(e.currentTarget);
242-
});
243-
244-
// View markdown action
245-
document.getElementById('action-view-markdown').addEventListener('click', (e) => {
246-
e.preventDefault();
247-
e.stopPropagation();
248-
viewMarkdown();
249-
closeDropdown(button, dropdown, overlay);
250-
});
251-
252-
// Ask AI action (disabled for now)
253-
document.getElementById('action-ask-ai').addEventListener('click', (e) => {
254-
e.preventDefault();
255-
e.stopPropagation();
256-
// Future: Integrate with Ask AI feature
257-
// For now, do nothing (disabled state)
258-
});
259-
260-
// Close on ESC key
261-
document.addEventListener('keydown', (e) => {
262-
if (e.key === 'Escape' && dropdown.classList.contains('active')) {
263-
closeDropdown(button, dropdown, overlay);
280+
(async () => {
281+
if (!shouldShowButton()) {
282+
return;
264283
}
265-
});
266284

267-
// Close when clicking outside
268-
document.addEventListener('click', (e) => {
269-
if (!dropdown.contains(e.target) && !button.contains(e.target)) {
270-
closeDropdown(button, dropdown, overlay);
285+
const markdownAvailable = await ensureMarkdownCached();
286+
if (!markdownAvailable) {
287+
return;
271288
}
272-
});
273289

274-
// Prevent dropdown from closing when clicking inside
275-
dropdown.addEventListener('click', (e) => {
276-
// Only stop propagation if not clicking on a link
277-
if (!e.target.closest('.page-action-link')) {
278-
e.stopPropagation();
290+
const ui = createPageActionsUI();
291+
if (!ui) {
292+
return;
279293
}
280-
});
281294

282-
// Close dropdown on link click (except for copy which handles itself)
283-
dropdown.querySelectorAll('.page-action-link:not(#action-copy-markdown)').forEach(link => {
284-
link.addEventListener('click', () => {
285-
if (!link.classList.contains('disabled')) {
286-
setTimeout(() => {
287-
closeDropdown(button, dropdown, overlay);
288-
}, 100);
295+
const { button, dropdown, overlay } = ui;
296+
297+
// Event listeners
298+
button.addEventListener('click', (e) => {
299+
e.stopPropagation();
300+
toggleDropdown(button, dropdown, overlay);
301+
});
302+
303+
overlay.addEventListener('click', () => {
304+
closeDropdown(button, dropdown, overlay);
305+
});
306+
307+
// Copy markdown action
308+
document.getElementById('action-copy-markdown').addEventListener('click', async (e) => {
309+
e.preventDefault();
310+
e.stopPropagation();
311+
await copyMarkdownToClipboard(e.currentTarget);
312+
});
313+
314+
// View markdown action
315+
document.getElementById('action-view-markdown').addEventListener('click', (e) => {
316+
e.preventDefault();
317+
e.stopPropagation();
318+
viewMarkdown();
319+
closeDropdown(button, dropdown, overlay);
320+
});
321+
322+
// Ask AI action (disabled for now)
323+
document.getElementById('action-ask-ai').addEventListener('click', (e) => {
324+
e.preventDefault();
325+
e.stopPropagation();
326+
// Future: Integrate with Ask AI feature
327+
// For now, do nothing (disabled state)
328+
});
329+
330+
// Close on ESC key
331+
document.addEventListener('keydown', (e) => {
332+
if (e.key === 'Escape' && dropdown.classList.contains('active')) {
333+
closeDropdown(button, dropdown, overlay);
289334
}
290335
});
291-
});
292-
293-
// Handle window resize
294-
let resizeTimer;
295-
window.addEventListener('resize', () => {
296-
clearTimeout(resizeTimer);
297-
resizeTimer = setTimeout(() => {
298-
// Close dropdown on resize to prevent positioning issues
299-
if (dropdown.classList.contains('active')) {
336+
337+
// Close when clicking outside
338+
document.addEventListener('click', (e) => {
339+
if (!dropdown.contains(e.target) && !button.contains(e.target)) {
300340
closeDropdown(button, dropdown, overlay);
301341
}
302-
}, 250);
303-
});
342+
});
304343

305-
// Accessibility: Focus management
306-
button.addEventListener('keydown', (e) => {
307-
if (e.key === 'Enter' || e.key === ' ') {
308-
e.preventDefault();
309-
toggleDropdown(button, dropdown, overlay);
344+
// Prevent dropdown from closing when clicking inside
345+
dropdown.addEventListener('click', (e) => {
346+
// Only stop propagation if not clicking on a link
347+
if (!e.target.closest('.page-action-link')) {
348+
e.stopPropagation();
349+
}
350+
});
310351

311-
// Focus first menu item when opening
312-
if (dropdown.classList.contains('active')) {
313-
const firstLink = dropdown.querySelector('.page-action-link:not(.disabled)');
314-
if (firstLink) {
315-
setTimeout(() => firstLink.focus(), 100);
352+
// Close dropdown on link click (except for copy which handles itself)
353+
dropdown.querySelectorAll('.page-action-link:not(#action-copy-markdown)').forEach(link => {
354+
link.addEventListener('click', () => {
355+
if (!link.classList.contains('disabled')) {
356+
setTimeout(() => {
357+
closeDropdown(button, dropdown, overlay);
358+
}, 100);
316359
}
317-
}
318-
}
319-
});
360+
});
361+
});
320362

321-
// Arrow key navigation within menu
322-
dropdown.addEventListener('keydown', (e) => {
323-
if (!dropdown.classList.contains('active')) return;
363+
// Handle window resize
364+
let resizeTimer;
365+
window.addEventListener('resize', () => {
366+
clearTimeout(resizeTimer);
367+
resizeTimer = setTimeout(() => {
368+
// Close dropdown on resize to prevent positioning issues
369+
if (dropdown.classList.contains('active')) {
370+
closeDropdown(button, dropdown, overlay);
371+
}
372+
}, 250);
373+
});
324374

325-
const links = Array.from(dropdown.querySelectorAll('.page-action-link:not(.disabled)'));
326-
const currentIndex = links.indexOf(document.activeElement);
375+
// Accessibility: Focus management
376+
button.addEventListener('keydown', (e) => {
377+
if (e.key === 'Enter' || e.key === ' ') {
378+
e.preventDefault();
379+
toggleDropdown(button, dropdown, overlay);
380+
381+
// Focus first menu item when opening
382+
if (dropdown.classList.contains('active')) {
383+
const firstLink = dropdown.querySelector('.page-action-link:not(.disabled)');
384+
if (firstLink) {
385+
setTimeout(() => firstLink.focus(), 100);
386+
}
387+
}
388+
}
389+
});
327390

328-
if (e.key === 'ArrowDown') {
329-
e.preventDefault();
330-
const nextIndex = (currentIndex + 1) % links.length;
331-
links[nextIndex].focus();
332-
} else if (e.key === 'ArrowUp') {
333-
e.preventDefault();
334-
const prevIndex = (currentIndex - 1 + links.length) % links.length;
335-
links[prevIndex].focus();
336-
} else if (e.key === 'Home') {
337-
e.preventDefault();
338-
links[0].focus();
339-
} else if (e.key === 'End') {
340-
e.preventDefault();
341-
links[links.length - 1].focus();
342-
}
343-
});
391+
// Arrow key navigation within menu
392+
dropdown.addEventListener('keydown', (e) => {
393+
if (!dropdown.classList.contains('active')) return;
394+
395+
const links = Array.from(dropdown.querySelectorAll('.page-action-link:not(.disabled)'));
396+
const currentIndex = links.indexOf(document.activeElement);
397+
398+
if (e.key === 'ArrowDown') {
399+
e.preventDefault();
400+
const nextIndex = (currentIndex + 1) % links.length;
401+
links[nextIndex].focus();
402+
} else if (e.key === 'ArrowUp') {
403+
e.preventDefault();
404+
const prevIndex = (currentIndex - 1 + links.length) % links.length;
405+
links[prevIndex].focus();
406+
} else if (e.key === 'Home') {
407+
e.preventDefault();
408+
links[0].focus();
409+
} else if (e.key === 'End') {
410+
e.preventDefault();
411+
links[links.length - 1].focus();
412+
}
413+
});
344414

345-
console.log('Page Actions initialized for:', getCurrentMarkdownPath());
415+
console.log('Page Actions initialized for:', getCurrentMarkdownPath());
416+
})();
346417
});

0 commit comments

Comments
 (0)