CodeBlockSection.tsx84 lines · main
1import { codeToHtml } from 'shiki'
2
3import type { GoCodeBlockSection } from '../schemas'
4import CodeBlockTabs from './CodeBlockTabs'
5import { brivenDark, brivenLight } from './codeThemes'
6
7const lineNumberStyles = `
8 .go-code code { counter-reset: line; }
9 .go-code code .line { counter-increment: line; }
10 .go-code code .line::before {
11 content: counter(line);
12 display: inline-block;
13 width: 2ch;
14 margin-right: 1.5ch;
15 text-align: right;
16 color: hsl(var(--foreground-light));
17 opacity: 0.35;
18 }
19`
20
21async function highlightCode(code: string, language: string) {
22 const [darkHtml, lightHtml] = await Promise.all([
23 codeToHtml(code, { lang: language, theme: brivenDark }),
24 codeToHtml(code, { lang: language, theme: brivenLight }),
25 ])
26 return { darkHtml, lightHtml }
27}
28
29export default async function CodeBlockSection({ section }: { section: GoCodeBlockSection }) {
30 const isMultiFile = section.files && section.files.length > 0
31 const isSingleFileWithName = !isMultiFile && section.filename && section.code
32
33 // Build the highlighted files array
34 const highlightedFiles = isMultiFile
35 ? await Promise.all(
36 section.files!.map(async (file) => {
37 const { darkHtml, lightHtml } = await highlightCode(file.code, file.language ?? 'sql')
38 return { filename: file.filename, darkHtml, lightHtml }
39 })
40 )
41 : section.code
42 ? [
43 {
44 filename: section.filename ?? '',
45 ...(await highlightCode(section.code, section.language ?? 'sql')),
46 },
47 ]
48 : []
49
50 const showTabs =
51 highlightedFiles.length > 1 || (highlightedFiles.length === 1 && isSingleFileWithName)
52
53 return (
54 <div className="max-w-7xl w-full min-w-0 mx-auto px-8">
55 {(section.title || section.description) && (
56 <div className="mb-12">
57 {section.title && (
58 <h2 className="text-2xl sm:text-3xl font-medium text-foreground">{section.title}</h2>
59 )}
60 {section.description && (
61 <p className="text-foreground-lighter mt-3 text-lg">{section.description}</p>
62 )}
63 </div>
64 )}
65 <style dangerouslySetInnerHTML={{ __html: lineNumberStyles }} />
66 <div className="go-code border border-muted rounded-xl overflow-hidden w-full">
67 {showTabs ? (
68 <CodeBlockTabs files={highlightedFiles} />
69 ) : highlightedFiles.length === 1 ? (
70 <>
71 <div
72 className="hidden dark:block px-5 py-4 sm:px-6 sm:py-5 overflow-x-auto text-[13px] leading-[1.6] [&_pre]:bg-transparent! [&_pre]:m-0! [&_code]:font-mono"
73 dangerouslySetInnerHTML={{ __html: highlightedFiles[0].darkHtml }}
74 />
75 <div
76 className="block dark:hidden px-5 py-4 sm:px-6 sm:py-5 overflow-x-auto text-[13px] leading-[1.6] [&_pre]:bg-transparent! [&_pre]:m-0! [&_code]:font-mono"
77 dangerouslySetInnerHTML={{ __html: highlightedFiles[0].lightHtml }}
78 />
79 </>
80 ) : null}
81 </div>
82 </div>
83 )
84}