CodeBlock.svelte 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. <script lang="ts">
  2. import { cn } from '$lib/utils/cn.js';
  3. interface Props {
  4. code: string;
  5. language?: string;
  6. class?: string;
  7. }
  8. let { code, language, class: className = '' }: Props = $props();
  9. let copied = $state(false);
  10. async function copy() {
  11. await navigator.clipboard.writeText(code);
  12. copied = true;
  13. setTimeout(() => { copied = false; }, 2000);
  14. }
  15. </script>
  16. <div class={cn('relative rounded-[var(--radius)] border border-[var(--border)] bg-[var(--muted)]/50 overflow-hidden', className)}>
  17. <!-- Header bar -->
  18. <div class="flex items-center justify-between px-4 py-2 border-b border-[var(--border)]">
  19. {#if language}
  20. <span class="text-xs font-mono text-[var(--muted-foreground)] uppercase tracking-wide">{language}</span>
  21. {:else}
  22. <span></span>
  23. {/if}
  24. <button
  25. type="button"
  26. onclick={copy}
  27. aria-label="Copy code"
  28. class={cn(
  29. 'flex items-center gap-1.5 text-xs text-[var(--muted-foreground)] hover:text-[var(--foreground)]',
  30. 'cursor-pointer transition-colors duration-[150ms]',
  31. 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--ring)] rounded'
  32. )}
  33. >
  34. {#if copied}
  35. <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
  36. <path d="M20 6L9 17l-5-5"/>
  37. </svg>
  38. Copied
  39. {:else}
  40. <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
  41. <rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
  42. </svg>
  43. Copy
  44. {/if}
  45. </button>
  46. </div>
  47. <!-- Code -->
  48. <pre class="overflow-x-auto px-4 py-3 text-sm font-mono text-[var(--foreground)] leading-relaxed"><code>{code}</code></pre>
  49. </div>