import { useCallback, useMemo, useState } from 'react'; import { CheckOutlined, CopyOutlined } from '@ant-design/icons'; import { javascript } from '@codemirror/lang-javascript'; import { Button } from '@signozhq/button'; import { dracula } from '@uiw/codemirror-theme-dracula'; import { githubLight } from '@uiw/codemirror-theme-github'; import CodeMirror, { EditorState, EditorView, Extension, } from '@uiw/react-codemirror'; import cx from 'classnames'; import { useIsDarkMode } from 'hooks/useDarkMode'; import './CodeBlock.styles.scss'; export type CodeBlockLanguage = | 'javascript' | 'typescript' | 'js' | 'ts' | 'json' | 'bash' | 'shell' | 'text'; export type CodeBlockTheme = 'light' | 'dark' | 'auto'; interface CodeBlockProps { /** The code content to display */ value: string; /** Language for syntax highlighting */ language?: CodeBlockLanguage; /** Theme: 'light' | 'dark' | 'auto' (follows app dark mode when 'auto') */ theme?: CodeBlockTheme; /** Show line numbers */ lineNumbers?: boolean; /** Show copy button */ showCopyButton?: boolean; /** Custom class name for the container */ className?: string; /** Max height in pixels - enables scrolling when content exceeds */ maxHeight?: number | string; /** Callback when copy is clicked */ onCopy?: (copiedText: string) => void; } const LANGUAGE_EXTENSION_MAP: Record< CodeBlockLanguage, ReturnType | undefined > = { javascript: javascript({ jsx: true }), typescript: javascript({ jsx: true }), js: javascript({ jsx: true }), ts: javascript({ jsx: true }), json: javascript(), // JSON is valid JS; proper json() would require @codemirror/lang-json bash: undefined, shell: undefined, text: undefined, }; function CodeBlock({ value, language = 'text', theme: themeProp = 'auto', lineNumbers = true, showCopyButton = true, className, maxHeight, onCopy, }: CodeBlockProps): JSX.Element { const isDarkMode = useIsDarkMode(); const [isCopied, setIsCopied] = useState(false); const resolvedDark = themeProp === 'auto' ? isDarkMode : themeProp === 'dark'; const theme = resolvedDark ? dracula : githubLight; const extensions = useMemo((): Extension[] => { const langExtension = LANGUAGE_EXTENSION_MAP[language]; return [ EditorState.readOnly.of(true), EditorView.editable.of(false), EditorView.lineWrapping, ...(langExtension ? [langExtension] : []), ]; }, [language]); const handleCopy = useCallback((): void => { navigator.clipboard.writeText(value).then(() => { setIsCopied(true); onCopy?.(value); setTimeout(() => setIsCopied(false), 2000); }); }, [value, onCopy]); return (
{showCopyButton && ( )}
); } export default CodeBlock;