"use client"; import { Button } from "@mdxui/primitives/button"; import { Card } from "@mdxui/primitives/card"; import { cn } from "@mdxui/primitives/lib/utils"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@mdxui/primitives/select"; import { Check, Copy } from "lucide-react"; import type React from "react"; import { useCallback, useEffect, useState } from "react"; import { type BundledLanguage, type BundledTheme, createHighlighter, type Highlighter, } from "shiki"; export interface CodeEditorProps { /** Initial code content */ initialValue?: string; /** Programming language */ language?: BundledLanguage; /** Callback when content changes */ onChange?: (value: string) => void; /** Placeholder text */ placeholder?: string; /** Height of the editor */ height?: string | number; /** Additional CSS class */ className?: string; /** Whether the editor is read-only */ readOnly?: boolean; /** Show line numbers */ showLineNumbers?: boolean; /** Theme for syntax highlighting */ theme?: BundledTheme; /** Enable language selector */ showLanguageSelector?: boolean; /** Available languages for selector */ availableLanguages?: BundledLanguage[]; } const COMMON_LANGUAGES: BundledLanguage[] = [ "typescript", "javascript", "tsx", "jsx", "json", "html", "css", "python", "rust", "go", "java", "markdown", ]; export const CodeEditor: React.FC = ({ initialValue = "", language = "typescript", onChange, placeholder = "// Start coding...", height = 400, className, readOnly = false, showLineNumbers = true, theme = "github-dark", showLanguageSelector = false, availableLanguages = COMMON_LANGUAGES, }) => { const [content, setContent] = useState(initialValue); const [currentLanguage, setCurrentLanguage] = useState(language); const [highlighter, setHighlighter] = useState(null); const [copied, setCopied] = useState(false); useEffect(() => { let mounted = true; createHighlighter({ themes: [theme], langs: availableLanguages, }).then((h) => { if (mounted) { setHighlighter(h); } }); return () => { mounted = false; }; }, [theme, availableLanguages]); const handleChange = useCallback( (value: string) => { setContent(value); onChange?.(value); }, [onChange], ); const handleCopy = useCallback(async () => { try { await navigator.clipboard.writeText(content); setCopied(true); setTimeout(() => setCopied(false), 2000); } catch (err) { console.error("Failed to copy:", err); } }, [content]); const highlightedCode = highlighter ? highlighter.codeToHtml(content || placeholder, { lang: currentLanguage, theme, }) : ""; return (
{showLanguageSelector && ( )} {!showLanguageSelector && ( {currentLanguage} )}