---
import Prism from '../lib/prism';
interface Props {
code: string;
lang?: string;
filename?: string;
}
const { code, lang = '', filename = '' } = Astro.props;
function escapeHtml(text: string): string {
return text
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
function highlight(code: string, lang: string): string {
if (lang && Prism.languages[lang]) {
return Prism.highlight(code, Prism.languages[lang], lang);
}
// Try to auto-detect based on content
if (code.includes('model ') && code.includes('@')) {
return Prism.highlight(code, Prism.languages['prax'], 'prax');
}
if (code.includes('fn ') || code.includes('let ') || code.includes('impl ')) {
return Prism.highlight(code, Prism.languages['rust'], 'rust');
}
return escapeHtml(code);
}
const highlightedCode = highlight(code, lang);
---
<div class="code-block group">
<div class="code-header">
{filename && <span class="filename">{filename}</span>}
<div class="header-actions">
{lang && <span class="language-badge">{lang}</span>}
<button type="button" class="copy-button" title="Copy code" aria-label="Copy code">
<svg class="icon icon-copy" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"
></path>
</svg>
<svg class="icon icon-check hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"
></path>
</svg>
</button>
</div>
</div>
<pre class="code-pre"><code class="code-content" set:html={highlightedCode} /></pre>
</div>
<script>
if (!(window as any).__praxCopyBound) {
(window as any).__praxCopyBound = true;
document.addEventListener('click', async (event) => {
const button = (event.target as HTMLElement).closest<HTMLButtonElement>('.copy-button');
if (!button) return;
const block = button.closest('.code-block');
const code = block?.querySelector('.code-content')?.textContent ?? '';
try {
await navigator.clipboard.writeText(code);
} catch (err) {
console.error('Failed to copy code:', err);
return;
}
button.classList.add('copied');
button.title = 'Copied!';
button.querySelector('.icon-copy')?.classList.add('hidden');
button.querySelector('.icon-check')?.classList.remove('hidden');
setTimeout(() => {
button.classList.remove('copied');
button.title = 'Copy code';
button.querySelector('.icon-copy')?.classList.remove('hidden');
button.querySelector('.icon-check')?.classList.add('hidden');
}, 2000);
});
}
</script>