import { cn } from '@tpmjs/utils/cn'; import { forwardRef, useEffect, useState } from 'react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; // eslint-disable-next-line import/no-internal-modules -- react-syntax-highlighter requires deep import for styles import { prism, vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; import { Icon } from '../Icon/Icon'; import type { CodeBlockProps } from './types'; import { codeBlockCodeVariants, codeBlockContainerVariants, codeBlockCopyButtonVariants, } from './variants'; /** * Hook to detect if dark mode is active by checking for .dark class on html element */ function useDarkMode(): boolean { const [isDark, setIsDark] = useState(false); useEffect(() => { // Check initial state const checkDarkMode = () => { setIsDark(document.documentElement.classList.contains('dark')); }; checkDarkMode(); // Watch for class changes on html element const observer = new MutationObserver(checkDarkMode); observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'], }); return () => observer.disconnect(); }, []); return isDark; } /** * CodeBlock component * * Displays formatted code with syntax highlighting and optional copy functionality. * Uses Prism syntax highlighter for beautiful code display. * Built with React and JSX. * * @example * ```typescript * import { CodeBlock } from '@tpmjs/ui/CodeBlock/CodeBlock'; * * function MyComponent() { * return ( * * ); * } * ``` */ export const CodeBlock = forwardRef( ({ className, code, language = 'text', size = 'md', showCopy = true, theme, ...props }, ref) => { const [copied, setCopied] = useState(false); const isDarkMode = useDarkMode(); // Use provided theme or auto-detect from system const effectiveTheme = theme ?? (isDarkMode ? 'dark' : 'light'); const handleCopy = async () => { try { await navigator.clipboard.writeText(code); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch (err) { // Silently fail if clipboard API is not available console.error('Failed to copy code:', err); } }; // Map language aliases to supported languages const languageMap: Record = { js: 'javascript', ts: 'typescript', jsx: 'jsx', tsx: 'tsx', py: 'python', rb: 'ruby', sh: 'bash', yml: 'yaml', }; const normalizedLanguage = languageMap[language] || language; return (
{code}
{showCopy && ( )}
); } ); CodeBlock.displayName = 'CodeBlock';