afterglow
uicode-blocksource: ok · hit
Code Block
Shiki wired to the phosphor palette, with a copy button. Takes a string of code and highlights it on the server.
npx shadcn add "https://afterglow.thebuilder.dk/r/code-block.json"free
Visual style
terminal-mono
Tags auto-generated by rules on 9/24/2026 — empty means no evidence, not lack of support.
Dependencies
Bundle weight · lighticonslucide-react
Registry dependencies
@afterglow/theme@afterglow/use-copied
Files3 · source cached 10/8/2026, 5:57:56 PM
registry/terminal/lib/shiki.ts
import type { BundledLanguage, ThemeRegistrationRaw } from "shiki";
import { codeToHtml } from "shiki";
// Shiki writes colors straight onto the tokens, so the theme hands it variable
// references instead of hex. A code block then follows the phosphor the page is
// set to, rather than carrying a second palette of its own.
export const CODE_THEME: ThemeRegistrationRaw = {
colors: {
"editor.background": "var(--panel-sunken)",
"editor.foreground": "var(--phosphor)",
},
name: "afterglow",
settings: [
{
scope: ["comment", "punctuation.definition.comment"],
settings: {
fontStyle: "italic",
foreground: "var(--phosphor-dim)",
},
},
{
scope: ["string", "constant.character", "punctuation.definition.string"],
settings: { foreground: "var(--warning)" },
},
{
scope: [
"keyword",
"storage",
"storage.type",
"keyword.control",
"keyword.operator.new",
"keyword.operator.expression",
"variable.language",
],
settings: { foreground: "var(--signal)" },
},
{
scope: [
"constant.numeric",
"constant.language",
"constant.language.boolean",
],
settings: { foreground: "var(--violet)" },
},
{
scope: [
"entity.name.type",
"entity.name.class",
"entity.name.tag",
"support.type",
"support.class",
"support.type.primitive",
],
settings: { foreground: "var(--info)" },
},
{
scope: [
"entity.name.function",
"support.function",
"meta.function-call.generic",
],
settings: { foreground: "var(--phosphor-bright)" },
},
{
scope: [
"punctuation",
"meta.brace",
"keyword.operator",
"entity.other.attribute-name",
],
settings: { foreground: "var(--muted-foreground)" },
},
],
type: "dark",
};
// Shiki's shorthand keeps one highlighter for the process and loads a grammar
// the first time a block asks for it.
export function highlightCode(
code: string,
lang: BundledLanguage
): Promise<string> {
return codeToHtml(code, { lang, theme: CODE_THEME });
}
registry/terminal/ui/code-block.tsx
import type * as React from "react";
import type { BundledLanguage } from "shiki";
import { cn } from "@/lib/utils";
import { highlightCode } from "@/registry/terminal/lib/shiki";
import { CodeBlockCopy } from "@/registry/terminal/ui/code-block-copy";
async function CodeBlock({
className,
code,
label,
lang = "tsx",
title,
...props
}: Omit<React.ComponentProps<"figure">, "children"> & {
code: string;
label?: string;
lang?: BundledLanguage;
title?: string;
}) {
const text = code.trimEnd();
return (
<figure
className={cn("border border-line bg-panel-sunken", className)}
data-slot="code-block"
{...props}
>
{title ? <CodeBlockTitle>{title}</CodeBlockTitle> : null}
<CodeBlockBody
html={await highlightCode(text, lang)}
label={label ?? (title ? `Copy ${title}` : undefined)}
text={text}
/>
</figure>
);
}
function CodeBlockTitle({
className,
...props
}: React.ComponentProps<"figcaption">) {
return (
<figcaption
className={cn(
"border-line border-b px-3 py-2 font-mono text-2xs text-phosphor-dim tracking-terminal-xs",
className
)}
data-slot="code-block-title"
{...props}
/>
);
}
function CodeBlockBody({
className,
html,
label,
text,
...props
}: Omit<React.ComponentProps<"div">, "children"> & {
html: string;
label?: string;
text: string;
}) {
return (
<div
className={cn("relative", className)}
data-slot="code-block-body"
{...props}
>
<div
className="[&_pre]:!bg-transparent overflow-x-auto py-3.5 pr-12 pl-4 font-mono text-xs leading-relaxed [&_pre]:outline-none"
// oxlint-disable-next-line react/no-danger -- Shiki output for code the caller passed in.
dangerouslySetInnerHTML={{ __html: html }}
/>
<CodeBlockCopy
className="absolute top-2.5 right-2.5"
label={label}
text={text}
/>
</div>
);
}
export { CodeBlock, CodeBlockBody, CodeBlockTitle };
registry/terminal/ui/code-block-copy.tsx
"use client";
import { CheckIcon, CopyIcon } from "lucide-react";
import type * as React from "react";
import { cn } from "@/lib/utils";
import { useCopied } from "@/registry/terminal/hooks/use-copied";
function CodeBlockCopy({
className,
label = "Copy the code",
text,
...props
}: Omit<React.ComponentProps<"button">, "children"> & {
label?: string;
text: string;
}) {
const { copied, copy } = useCopied(text);
return (
<button
aria-label={copied ? "Copied" : label}
className={cn(
"grid size-7 shrink-0 place-items-center border border-line bg-panel-sunken text-phosphor-dim outline-none transition-colors hover:border-line-strong hover:text-phosphor-bright focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-phosphor-bright",
className
)}
data-slot="code-block-copy"
onClick={copy}
type="button"
{...props}
>
{copied ? (
<CheckIcon className="size-3.5 text-phosphor" />
) : (
<CopyIcon className="size-3.5" />
)}
</button>
);
}
export { CodeBlockCopy };