'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 DraculaTheme from 'monaco-themes/themes/Dracula.json' with { type: 'json' }; 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; beforeMount?: (monaco: Monaco) => void; language?: string; className?: string; showLineNumbers?: boolean; } // 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, beforeMount, language = 'markdown', className, showLineNumbers = false, }: 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: showLineNumbers ? 'on' : '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: DraculaTheme.base as 'vs' | 'vs-dark' | 'hc-black', inherit: DraculaTheme.inherit, rules: DraculaTheme.rules, colors: { ...DraculaTheme.colors, 'editor.background': '#00000000', }, }); if (beforeMount) { beforeMount(monaco); } }} /> {!internalValue?.trim() && placeholder && (
                  {placeholder}
                
)}
{render && isPreviewVisible && (
{render(internalValue)}
)}
); }