Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions docs/src/components/docs/CodeCopyScript.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<script>
// Function to add copy buttons to all pre blocks
function addCopyButtons() {
// Select all pre tags in the docs content area
const preBlocks = document.querySelectorAll('.docs-content pre');

preBlocks.forEach((pre) => {
// Check if already processed to avoid duplicates
if (pre.parentNode?.classList.contains('code-wrapper')) return;

// Create wrapper
const wrapper = document.createElement('div');
wrapper.className = 'code-wrapper';

// Insert wrapper before pre, then move pre into wrapper
pre.parentNode?.insertBefore(wrapper, pre);
wrapper.appendChild(pre);

// Create copy button
const button = document.createElement('button');
button.className = 'copy-button';
button.ariaLabel = 'Copy code to clipboard';

// Icon SVG (copy icon)
button.innerHTML = `
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
`;

// Add click handler
button.addEventListener('click', async () => {
const code = pre.querySelector('code')?.innerText || pre.innerText;

try {
await navigator.clipboard.writeText(code);

// success state
button.classList.add('copied');
button.innerHTML = `
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
<span class="sr-only">Copied!</span>
`;

// Reset after 2 seconds
setTimeout(() => {
button.classList.remove('copied');
button.innerHTML = `
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
`;
}, 2000);
} catch (err) {
console.error('Failed to copy code:', err);
}
});

wrapper.appendChild(button);
});
}

// Run on load
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', addCopyButtons);
} else {
addCopyButtons();
}

// Re-run on view transitions if enabled (Astro default)
document.addEventListener('astro:page-load', addCopyButtons);
</script>

3 changes: 2 additions & 1 deletion docs/src/layouts/DocsLayout.astro
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import Breadcrumbs from '../components/docs/Breadcrumbs.astro';
import DocsPagination from '../components/docs/DocsPagination.astro';
import ThemeScript from '../components/ThemeScript.astro';
import Analytics from '../components/Analytics.astro';
import CodeCopyScript from '../components/docs/CodeCopyScript.astro';
import '../styles/custom.css';
import '../styles/docs.css';

Expand All @@ -38,6 +39,7 @@ const defaultOgImage = '/odin/odin-og-image.png';
<Analytics />

<ThemeScript />
<CodeCopyScript />

<!-- Primary Meta Tags -->
<title>{title} | Odin Documentation</title>
Expand Down Expand Up @@ -167,4 +169,3 @@ const defaultOgImage = '/odin/odin-og-image.png';
</script>
</body>
</html>

51 changes: 49 additions & 2 deletions docs/src/styles/docs.css
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@
padding: 1.25rem 1.5rem;
border-radius: var(--radius-md);
overflow-x: auto;
margin: 1.5rem 0;
margin: 0; /* Reset margin as wrapper handles it */
font-family: var(--font-mono);
font-size: 0.875rem;
line-height: 1.7;
Expand Down Expand Up @@ -364,6 +364,54 @@
transform: scale(0.95);
}

/* ============================================
CODE BLOCK & COPY BUTTON
============================================ */

.code-wrapper {
position: relative;
margin: 1.5rem 0;
}

.copy-button {
position: absolute;
top: 0.5rem;
right: 0.5rem;
padding: 0.4rem;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
border-radius: var(--radius-sm);
cursor: pointer;
color: #a0a0a0;
transition: all 0.2s ease;
display: flex;
align-items: center;
justify-content: center;
z-index: 5;
}

.copy-button:hover {
background: rgba(255, 255, 255, 0.2);
color: #fff;
border-color: rgba(255, 255, 255, 0.4);
}

.copy-button svg {
width: 16px;
height: 16px;
}

.copy-button.copied {
background: rgba(16, 185, 129, 0.2);
border-color: rgba(16, 185, 129, 0.4);
color: #34d399;
}

/* Ensure code blocks have enough padding on the right for the button */
.docs-content pre {
padding-right: 3rem;
}

/* ============================================
RESPONSIVE LAYOUT
============================================ */
Expand Down Expand Up @@ -463,4 +511,3 @@
background: rgba(255, 255, 255, 0.1);
border-color: rgba(255, 255, 255, 0.15);
}

Loading