'use client'; import { Editor, type Monaco, type OnMount } from '@monaco-editor/react'; import { Button } from '@workspace/ui/components/button'; import { cn } from '@workspace/ui/lib/utils'; import { useSize } from 'ahooks'; import { EyeIcon, EyeOff, FullscreenIcon, MinimizeIcon } from 'lucide-react'; import { useEffect, useRef, useState } from 'react'; export interface MonacoEditorProps { value?: string; onChange?: (value: string | undefined) => void; onBlur?: (value: string | undefined) => void; title?: string; description?: string; placeholder?: string; render?: (value?: string) => React.ReactNode; onMount?: OnMount; language?: string; className?: string; } // eslint-disable-next-line @typescript-eslint/no-explicit-any function debounce void>(func: T, delay: number) { let timeoutId: ReturnType; return function (...args: Parameters) { clearTimeout(timeoutId); timeoutId = setTimeout(() => func(...args), delay); }; } export function MonacoEditor({ value: propValue, onChange, onBlur, title = 'Editor Title', description, placeholder = 'Start typing...', render, onMount, language = 'markdown', className, }: MonacoEditorProps) { const [internalValue, setInternalValue] = useState(propValue); const [isFullscreen, setIsFullscreen] = useState(false); const [isPreviewVisible, setIsPreviewVisible] = useState(false); const ref = useRef(null); const size = useSize(ref); useEffect(() => { setInternalValue(propValue); }, [propValue]); const debouncedOnChange = useRef( debounce((newValue: string | undefined) => { if (onChange) { onChange(newValue); } }, 300), ).current; const handleEditorDidMount: OnMount = (editor, monaco) => { if (onMount) onMount(editor, monaco); editor.onDidChangeModelContent(() => { const newValue = editor.getValue(); setInternalValue(newValue); debouncedOnChange(newValue); }); editor.onDidBlurEditorWidget(() => { if (onBlur) { onBlur(editor.getValue()); } }); }; const toggleFullscreen = () => setIsFullscreen(!isFullscreen); const togglePreview = () => setIsPreviewVisible(!isPreviewVisible); return (

{title}

{description}

{render && ( )}
{ setInternalValue(newValue); debouncedOnChange(newValue); }} onMount={handleEditorDidMount} className='' options={{ automaticLayout: true, contextmenu: false, folding: false, fontSize: 14, formatOnPaste: true, formatOnType: true, glyphMargin: false, lineNumbers: 'off', minimap: { enabled: false }, overviewRulerLanes: 0, renderLineHighlight: 'none', scrollBeyondLastLine: false, scrollbar: { useShadows: false, vertical: 'hidden', }, tabSize: 2, wordWrap: 'off', }} theme='transparentTheme' beforeMount={(monaco: Monaco) => { monaco.editor.defineTheme('transparentTheme', { base: 'vs-dark', inherit: true, rules: [], colors: { 'editor.background': '#00000000', }, }); }} /> {!internalValue?.trim() && placeholder && (
                  {placeholder}
                
)}
{render && isPreviewVisible && (
{render(internalValue)}
)}
); }