import { memo, useEffect, useState } from 'react'; import { bundledLanguages, codeToHtml, isSpecialLang, type BundledLanguage, type BundledTheme, type SpecialLanguage, } from 'shiki'; import { classNames } from '~/utils/classNames'; import { createScopedLogger } from '~/utils/logger'; import styles from './CodeBlock.module.scss'; const logger = createScopedLogger('CodeBlock'); interface CodeBlockProps { code: string; language?: BundledLanguage; theme?: BundledTheme | SpecialLanguage; } export const CodeBlock = memo(({ code, language, theme }: CodeBlockProps) => { const [html, setHTML] = useState(undefined); const [copied, setCopied] = useState(false); const copyToClipboard = () => { if (copied) { return; } navigator.clipboard.writeText(code); setCopied(true); setTimeout(() => { setCopied(false); }, 2000); }; useEffect(() => { if (language && !isSpecialLang(language) && !(language in bundledLanguages)) { logger.warn(`Unsupported language '${language}'`); } logger.trace(`Language = ${language}`); const processCode = async () => { setHTML(await codeToHtml(code, { lang: language ?? 'plaintext', theme: theme ?? 'dark-plus' })); }; processCode(); }, [code]); return (
); });