'use client' import { useState } from 'react' import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter' import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism' import { cn } from '@/lib/utils' import { colors, effects } from '@/lib/theme' import { Copy, Check } from 'lucide-react' /** * Supported syntax highlighting languages for code blocks. * * @remarks * This list includes the most commonly used languages in the codebase. * Languages are validated and normalized to ensure proper syntax highlighting. */ const SUPPORTED_LANGUAGES = [ 'typescript', 'javascript', 'tsx', 'jsx', 'ts', 'js', 'json', 'bash', 'shell', 'css', 'scss', 'html', 'markdown', 'yaml', 'sql', ] as const type SupportedLanguage = typeof SUPPORTED_LANGUAGES[number] /** * Normalizes language identifiers to their canonical forms. * * @param language - Raw language identifier from code fence * @returns Normalized language identifier for syntax highlighting * * @remarks * **Normalization rules:** * - 'ts' → 'typescript' * - 'js' → 'javascript' * - Invalid languages → 'typescript' (safe default) * - All other valid languages → unchanged * * This ensures consistent syntax highlighting even when JSDoc * examples use shorthand language identifiers. * * @example * ```ts * normalizeLanguage('ts') // Returns: 'typescript' * normalizeLanguage('tsx') // Returns: 'tsx' * normalizeLanguage('invalid') // Returns: 'typescript' * ``` * * @private */ function normalizeLanguage(language: string): SupportedLanguage { const normalized = language.toLowerCase() // Map common shorthands to full names if (normalized === 'ts') return 'typescript' if (normalized === 'js') return 'javascript' // Validate against supported languages if (SUPPORTED_LANGUAGES.includes(normalized as SupportedLanguage)) { return normalized as SupportedLanguage } // Default to typescript for unknown languages return 'typescript' } interface CodeBlockProps { code: string language?: string title?: string showLineNumbers?: boolean className?: string } export default function CodeBlock({ code, language = 'typescript', title, showLineNumbers = false, className, }: CodeBlockProps) { const [copied, setCopied] = useState(false) const normalizedLanguage = normalizeLanguage(language) const handleCopy = async () => { await navigator.clipboard.writeText(code) setCopied(true) setTimeout(() => setCopied(false), 2000) } return (
{title && (
{title} {normalizedLanguage}
)}
{code}
) }