TypeScript 97.5%
SQL 1.4%
Python 0.8%
1import * as React from "react";2import { cn } from "@/lib/utils";3import { CopyButton } from "./copy-button";45export type CodeLang = "bash" | "json" | "javascript" | "typescript" | "python" | "go" | "php" | "ruby" | "java" | "csharp" | "html" | "text";67/** Tiny, dependency-free tokenizer good enough for docs and playground snippets. */8export function highlight(code: string, lang: CodeLang): React.ReactNode[] {9 const esc = (s: string) => s;10 if (lang === "text") return [esc(code)];11 const rules: Array<[RegExp, string]> = [];12 if (lang === "json") {13 rules.push([/"(?:[^"\\]|\\.)*"(?=\s*:)/g, "tok-a"], [/"(?:[^"\\]|\\.)*"/g, "tok-s"], [/\b(?:true|false|null)\b/g, "tok-k"], [/-?\b\d+(?:\.\d+)?(?:e[+-]?\d+)?\b/gi, "tok-n"]);14 } else if (lang === "bash") {15 rules.push([/#.*$/gm, "tok-c"], [/'(?:[^'\\]|\\.)*'|"(?:[^"\\]|\\.)*"/g, "tok-s"], [/^\s*(curl|export|echo|pip|npm|pnpm|yarn|go|python|node|fetcha)\b/gm, "tok-k"], [/\s(-{1,2}[a-zA-Z-]+)/g, "tok-a"]);16 } else if (lang === "html") {17 rules.push([/<!--[\s\S]*?-->/g, "tok-c"], [/<\/?[a-zA-Z][^\s>]*/g, "tok-t"], [/"[^"]*"/g, "tok-s"]);18 } else {19 rules.push(20 [/\/\/.*$|#.*$/gm, "tok-c"],21 [/\/\*[\s\S]*?\*\//g, "tok-c"],22 [/`(?:[^`\\]|\\.)*`|"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'/g, "tok-s"],23 [/\b(?:import|from|export|const|let|var|function|async|await|return|new|class|def|if|else|elif|for|while|try|except|catch|finally|throw|raise|print|package|func|use|require|public|static|void|string|var|namespace|using|end|do|puts|nil|None|True|False|true|false|null|undefined|with|as|in|not|and|or|fmt|err)\b/g, "tok-k"],24 [/\b\d+(?:\.\d+)?\b/g, "tok-n"],25 [/\b(?:Fetcha|FetchaClient|fetch|json|text|sessions|create|Println|Fatal|NewRequest|Marshal|env|process|os|getenv|environ|std|Console|WriteLine)\b(?=\s*[\(\.])/g, "tok-a"],26 );27 }28 type Span = { s: number; e: number; c: string };29 const spans: Span[] = [];30 for (const [re, c] of rules) {31 re.lastIndex = 0;32 let m: RegExpExecArray | null;33 while ((m = re.exec(code))) {34 if (!m[0]) {35 re.lastIndex++;36 continue;37 }38 const s = m.index;39 const e = s + m[0].length;40 if (spans.some((x) => s < x.e && e > x.s)) continue;41 spans.push({ s, e, c });42 }43 }44 spans.sort((a, b) => a.s - b.s);45 const out: React.ReactNode[] = [];46 let i = 0;47 for (const sp of spans) {48 if (sp.s > i) out.push(esc(code.slice(i, sp.s)));49 out.push(50 <span key={sp.s} className={sp.c}>51 {code.slice(sp.s, sp.e)}52 </span>,53 );54 i = sp.e;55 }56 if (i < code.length) out.push(esc(code.slice(i)));57 return out;58}5960export function CodeBlock({61 code,62 lang = "bash",63 title,64 className,65 maxHeight,66 lineNumbers,67 copy = true,68}: {69 code: string;70 lang?: CodeLang;71 title?: string;72 className?: string;73 maxHeight?: number | string;74 lineNumbers?: boolean;75 copy?: boolean;76}) {77 const lines = code.replace(/\n$/, "").split("\n");78 return (79 <div className={cn("group relative overflow-hidden rounded-lg border border-border bg-bg-subtle dark:bg-[#0b0f17] text-[12.5px] leading-[1.6]", className)}>80 {title ? (81 <div className="flex items-center justify-between border-b border-border px-3.5 py-1.5 text-[11.5px] text-fg-subtle">82 <span className="font-mono">{title}</span>83 {copy ? <CopyButton value={code} size="icon-sm" className="-mr-1.5 size-7" /> : null}84 </div>85 ) : copy ? (86 <div className="absolute right-1.5 top-1.5 opacity-0 transition-opacity group-hover:opacity-100 focus-within:opacity-100">87 <CopyButton value={code} size="icon-sm" className="size-7 bg-bg/80 backdrop-blur" />88 </div>89 ) : null}90 <pre className="overflow-auto p-3.5 scrollbar-thin font-mono" style={{ maxHeight }}>91 <code>92 {lineNumbers93 ? lines.map((l, i) => (94 <span key={i} className="block">95 <span className="mr-4 inline-block w-6 select-none text-right text-fg-subtle/70">{i + 1}</span>96 {highlight(l, lang)}97 {"\n"}98 </span>99 ))100 : highlight(code, lang)}101 </code>102 </pre>103 </div>104 );105}106107export function InlineCode({ children, className }: { children: React.ReactNode; className?: string }) {108 return <code className={cn("rounded-[4px] border border-border bg-bg-muted px-1.5 py-0.5 font-mono text-[0.85em] text-fg", className)}>{children}</code>;109}110