import { useRef, useState } from 'react'; import CodeMirror, { EditorView } from '@uiw/react-codemirror'; import { json } from '@codemirror/lang-json'; import { xml } from '@codemirror/lang-xml'; import { oneDark } from '@codemirror/theme-one-dark'; import { Eye, Pencil, Save } from 'lucide-react'; import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism'; export type CodeMode = 'text' | 'json' | 'xml'; interface CodeEditorProps { text: string; mode: CodeMode; readOnly?: boolean; onSave?: (text: string) => Promise; } const cmTheme = EditorView.theme({ '&': { height: '100%', background: '#0a0a0a' }, '.cm-scroller': { overflow: 'auto', fontFamily: 'ui-monospace,monospace', fontSize: '12px', lineHeight: '1.5' }, '.cm-content': { padding: '12px 0' }, '.cm-focused': { outline: 'none' }, }); const langExt: Record = { json: json(), xml: xml(), text: [], }; const syntaxLang: Record = { text: 'text', json: 'json', xml: 'xml', }; function prettify(text: string, mode: CodeMode): string { if (mode === 'json') { try { return JSON.stringify(JSON.parse(text), null, 2); } catch { /* fall through */ } } return text; } export default function CodeEditor({ text, mode, readOnly = false, onSave }: CodeEditorProps) { const [editMode, setEditMode] = useState(false); const [saving, setSaving] = useState(false); const editorViewRef = useRef(null); const displayText = prettify(text, mode); const extensions = [langExt[mode], cmTheme].flat(); const handleSave = async () => { if (!editorViewRef.current || !onSave) return; setSaving(true); try { await onSave(editorViewRef.current.state.doc.toString()); } finally { setSaving(false); } }; if (!editMode) { return (
{!readOnly && (
)}
{displayText}
); } return (
{onSave && ( )} Ctrl+Z/Y undo · Ctrl+F search
{ editorViewRef.current = view; }} />
); }