/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
For commercial licensing, please contact support@quantumnous.com
*/
/* eslint-disable react-refresh/only-export-components */
'use client'
import {
type ComponentProps,
createContext,
type HTMLAttributes,
useContext,
useEffect,
useState,
} from 'react'
import { CheckIcon, CopyIcon } from 'lucide-react'
import {
type BundledLanguage,
codeToHtml,
type ShikiTransformer,
} from 'shiki/bundle/web'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
type CodeBlockProps = HTMLAttributes & {
code: string
language: BundledLanguage
showLineNumbers?: boolean
}
type CodeBlockContextType = {
code: string
}
const CodeBlockContext = createContext({
code: '',
})
const lineNumberTransformer: ShikiTransformer = {
name: 'line-numbers',
line(node, line) {
node.children.unshift({
type: 'element',
tagName: 'span',
properties: {
className: [
'inline-block',
'min-w-10',
'mr-4',
'text-right',
'select-none',
'text-muted-foreground',
],
},
children: [{ type: 'text', value: String(line) }],
})
},
}
export async function highlightCode(
code: string,
language: BundledLanguage,
showLineNumbers = false
) {
const transformers: ShikiTransformer[] = showLineNumbers
? [lineNumberTransformer]
: []
return codeToHtml(code, {
lang: language,
themes: {
light: 'one-light',
dark: 'one-dark-pro',
},
transformers,
})
}
export const CodeBlock = ({
code,
language,
showLineNumbers = false,
className,
children,
...props
}: CodeBlockProps) => {
const [html, setHtml] = useState('')
useEffect(() => {
let cancelled = false
highlightCode(code, language, showLineNumbers).then((next) => {
if (!cancelled) {
setHtml(next)
}
})
return () => {
cancelled = true
}
}, [code, language, showLineNumbers])
return (