Skip to content

Commit 8657aca

Browse files
Merge pull request #53 from shirolk/chunk-8
Adds a copy button to code blocks
2 parents 50c0b57 + a75c40e commit 8657aca

File tree

3 files changed

+165
-35
lines changed

3 files changed

+165
-35
lines changed

_includes/scripts.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<script src="{{ '/js/bootstrap.bundle.min.js' | relative_url }}"></script>
22
<script src="{{ '/js/jquery-3.7.1.min.js' | relative_url }}"></script>
3+
<script src="{{ '/js/openchoreo.js' | relative_url }}"></script>
34

45
<button id="toggleDarkMode" style="visibility: hidden;"></button>
56

css/openchoreo-docs.css

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ code.highlighter-rouge.language-plaintext {
7878
}
7979

8080
/* ========= Fenced Code Block (```code```) ========= */
81+
.highlighter-rouge {
82+
position: relative;
83+
}
84+
8185
.highlighter-rouge .highlight {
8286
background-color: #2c3035;
8387
color: #dcdcdc;
@@ -91,7 +95,7 @@ code.highlighter-rouge.language-plaintext {
9195
}
9296

9397
.highlighter-rouge .highlight pre {
94-
padding: 1em;
98+
padding: 1em 2.5rem 1em 1em;
9599
margin: 0;
96100
}
97101

@@ -331,6 +335,83 @@ body.cDarkMode .sidebar {
331335
background-color: #f8f9fa;
332336
border-right: 1px solid #22282d;
333337
}
338+
339+
/* Copy Button Styles with Icon */
340+
.copy-btn {
341+
position: absolute;
342+
top: 0.25rem;
343+
right: 0.25rem;
344+
width: 1.75rem;
345+
height: 1.75rem;
346+
background: rgba(45, 48, 53, 0.9);
347+
border: 1px solid rgba(255, 255, 255, 0.3);
348+
border-radius: 4px;
349+
cursor: pointer;
350+
transition: all 0.2s ease;
351+
display: flex;
352+
align-items: center;
353+
justify-content: center;
354+
padding: 0;
355+
z-index: 10;
356+
backdrop-filter: blur(4px);
357+
}
358+
359+
.copy-btn:hover {
360+
background: rgba(255, 255, 255, 0.2);
361+
border-color: rgba(255, 255, 255, 0.5);
362+
}
363+
364+
.copy-btn:active {
365+
transform: translateY(1px);
366+
}
367+
368+
.copy-btn.copied {
369+
background: #28a745;
370+
border-color: #28a745;
371+
}
372+
373+
.copy-btn svg {
374+
width: 1rem;
375+
height: 1rem;
376+
fill: #dcdcdc;
377+
transition: fill 0.2s ease;
378+
}
379+
380+
.copy-btn:hover svg {
381+
fill: #ffffff;
382+
}
383+
384+
.copy-btn.copied svg {
385+
fill: white;
386+
}
387+
388+
/* Dark mode copy button adjustments */
389+
body.cDarkMode .copy-btn {
390+
background: rgba(255, 255, 255, 0.1);
391+
border: 1px solid rgba(255, 255, 255, 0.3);
392+
}
393+
394+
body.cDarkMode .copy-btn:hover {
395+
background: rgba(255, 255, 255, 0.2);
396+
border-color: rgba(255, 255, 255, 0.5);
397+
}
398+
399+
body.cDarkMode .copy-btn svg {
400+
fill: #dcdcdc;
401+
}
402+
403+
body.cDarkMode .copy-btn:hover svg {
404+
fill: #ffffff;
405+
}
406+
407+
body.cDarkMode .copy-btn.copied {
408+
background: #28a745;
409+
border-color: #28a745;
410+
}
411+
412+
body.cDarkMode .copy-btn.copied svg {
413+
fill: white;
414+
}
334415
/* #mainAccordion a {
335416
color: #B7B7B7;
336417
} */

js/openchoreo.js

Lines changed: 82 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,92 @@
11
$(document).ready(function () {
2-
const toggleButton = document.getElementById('toggleDarkMode');
3-
const darkThemeMedia = window.matchMedia('(prefers-color-scheme: dark)');
42

5-
function applyTheme(theme) {
6-
if (theme === 'dark') {
7-
document.body.classList.add('cDarkMode');
8-
$('.cColourMode').addClass('cDarkModeOn');
9-
} else {
10-
document.body.classList.remove('cDarkMode');
11-
$('.cColourMode').removeClass('cDarkModeOn');
12-
}
13-
}
3+
// Copy icon SVG
4+
const copyIconSVG = `
5+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
6+
<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/>
7+
</svg>
8+
`;
149

15-
const savedTheme = localStorage.getItem('theme');
16-
if (savedTheme) {
17-
applyTheme(savedTheme);
18-
} else {
19-
applyTheme(darkThemeMedia.matches ? 'dark' : 'light');
20-
}
10+
const checkIconSVG = `
11+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
12+
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/>
13+
</svg>
14+
`;
2115

22-
// Toggle via button with ID
23-
if (toggleButton) {
24-
toggleButton.addEventListener('click', () => {
25-
const isDark = document.body.classList.toggle('cDarkMode');
26-
$('.cColourMode').toggleClass('cDarkModeOn', isDark);
27-
localStorage.setItem('theme', isDark ? 'dark' : 'light');
16+
// Add copy buttons to code blocks (exclude inline code)
17+
function addCopyButtons() {
18+
$('.highlighter-rouge').each(function() {
19+
const $container = $(this);
20+
const $codeBlock = $container.find('.highlight');
21+
22+
// Only add copy button to fenced code blocks (with .highlight), not inline code
23+
if ($codeBlock.length > 0 && $container.find('.copy-btn').length === 0) {
24+
const $copyBtn = $(`<button class="copy-btn" title="Copy code">${copyIconSVG}</button>`);
25+
$container.append($copyBtn);
26+
27+
$copyBtn.on('click', function(e) {
28+
e.preventDefault();
29+
e.stopPropagation();
30+
31+
const codeText = $codeBlock.find('code').text().trim();
32+
33+
// Use modern clipboard API if available
34+
if (navigator.clipboard && window.isSecureContext) {
35+
navigator.clipboard.writeText(codeText).then(() => {
36+
showCopySuccess($copyBtn);
37+
}).catch(() => {
38+
fallbackCopyToClipboard(codeText, $copyBtn);
39+
});
40+
} else {
41+
fallbackCopyToClipboard(codeText, $copyBtn);
42+
}
43+
});
44+
}
2845
});
2946
}
3047

31-
// Toggle via .cColourMode click
32-
$('.cColourMode').on('click', function () {
33-
const isDark = $('body').toggleClass('cDarkMode').hasClass('cDarkMode');
34-
$(this).toggleClass('cDarkModeOn', isDark);
35-
localStorage.setItem('theme', isDark ? 'dark' : 'light');
36-
});
48+
function fallbackCopyToClipboard(text, $btn) {
49+
const textArea = document.createElement('textarea');
50+
textArea.value = text;
51+
textArea.style.position = 'fixed';
52+
textArea.style.left = '-999999px';
53+
textArea.style.top = '-999999px';
54+
document.body.appendChild(textArea);
55+
textArea.focus();
56+
textArea.select();
57+
58+
try {
59+
document.execCommand('copy');
60+
showCopySuccess($btn);
61+
} catch (err) {
62+
console.error('Failed to copy text: ', err);
63+
}
64+
65+
textArea.remove();
66+
}
3767

38-
// System theme listener
39-
if (!savedTheme) {
40-
darkThemeMedia.addEventListener('change', (e) => {
41-
applyTheme(e.matches ? 'dark' : 'light');
42-
});
68+
function showCopySuccess($btn) {
69+
$btn.addClass('copied').html(checkIconSVG);
70+
setTimeout(() => {
71+
$btn.removeClass('copied').html(copyIconSVG);
72+
}, 1000);
4373
}
74+
75+
// Initialize copy buttons
76+
addCopyButtons();
77+
78+
// Re-add copy buttons if content is dynamically loaded
79+
const observer = new MutationObserver(function(mutations) {
80+
mutations.forEach(function(mutation) {
81+
if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
82+
setTimeout(addCopyButtons, 100);
83+
}
84+
});
85+
});
86+
87+
// Observe changes to the document body
88+
observer.observe(document.body, {
89+
childList: true,
90+
subtree: true
91+
});
4492
});

0 commit comments

Comments
 (0)