AI-agenter
Code block
Kode med filnavn og kopiknap. Tag din egen syntaksfarvning med, eller lad være.
Forhåndsvisning
Brug
code-block-demo.tsx
import { CodeBlock } from '@/components/code-block'
const code = `export function total(items: { price: number; qty: number }[]) {
return items.reduce((sum, i) => sum + i.price * i.qty, 0)
}`
export default function CodeBlockDemo() {
return <CodeBlock filename="lib/total.ts" code={code} lineNumbers />
}Kilde
code-block.tsx
import { Check, Copy } from 'lucide-react'
import { useEffect, useState, type ReactNode } from 'react'
import { cn } from '@/lib/utils'
export type CodeBlockProps = {
/** The code as text. This is what the copy button copies. */
code: string
/** Shown in the header, e.g. "tsx" or "bash". */
language?: string
/** Shown in the header instead of the language, e.g. "src/app.tsx". */
filename?: string
/** Already highlighted markup (from Shiki or similar). Without it the plain code is shown. */
children?: ReactNode
/** Number the lines. */
lineNumbers?: boolean
className?: string
}
/**
* Code with a header and a copy button. It does no highlighting itself, so it adds no weight;
* pass highlighted markup as children if you want colours.
*/
export function CodeBlock({ code, language, filename, children, lineNumbers = false, className }: CodeBlockProps) {
const [copied, setCopied] = useState(false)
useEffect(() => {
if (!copied) return
const id = setTimeout(() => setCopied(false), 1800)
return () => clearTimeout(id)
}, [copied])
async function copy() {
try {
await navigator.clipboard.writeText(code)
setCopied(true)
} catch {
// The clipboard can be blocked (an insecure page, a denied permission). The code can still be selected by hand.
}
}
const lines = code.replace(/\n$/, '').split('\n')
return (
<figure className={cn('overflow-hidden rounded-2xl border bg-card text-card-foreground', className)}>
<figcaption className="flex items-center justify-between gap-3 border-b bg-muted/50 py-1 pr-1 pl-4">
<span className="truncate font-mono text-[0.8125rem] text-muted-foreground">{filename ?? language ?? 'Code'}</span>
<button
type="button"
onClick={copy}
className="inline-flex min-h-11 shrink-0 items-center gap-1.5 rounded-xl px-3 text-sm text-muted-foreground transition-colors duration-150 hover:bg-muted hover:text-foreground"
>
{copied ? <Check className="size-4 text-success" aria-hidden /> : <Copy className="size-4" aria-hidden />}
<span aria-live="polite">{copied ? 'Copied' : 'Copy'}</span>
</button>
</figcaption>
<pre className="max-h-[28rem] overflow-auto py-3 font-mono text-[0.8125rem] leading-6" tabIndex={0}>
{children ? (
<div className="px-4">{children}</div>
) : lineNumbers ? (
<code className="grid grid-cols-[auto_1fr]">
{lines.map((line, i) => (
<span key={i} className="contents">
<span className="pr-4 pl-4 text-right text-muted-foreground select-none" aria-hidden>
{i + 1}
</span>
<span className="pr-4">{line || ' '}</span>
</span>
))}
</code>
) : (
<code className="block px-4">{code}</code>
)}
</pre>
</figure>
)
}Installér
npx shadcn@latest add https://mikkelmanniche.dk/lab/r/code-block.jsonMed registret tilføjet (components.json)
npx shadcn@latest add @manniche/code-block