Editor Component
The Editor component is responsible for rendering the Monaco Editor, which provides a rich text editing experience for Markdown.
Props
value: string: The current Markdown content to be displayed in the editor.onChange: (value: string) => void: A callback function that is called whenever the content of the editor changes.minimapEnabled: boolean: A boolean to control the visibility of the minimap.wordWrapEnabled?: boolean: An optional boolean to control word wrapping. It defaults totrue.
State
theme: Manages the theme of the Monaco Editor, switching between'vs-dark'and'vs-light'based on the application's theme.
Key Features
Theme Synchronization
The editor's theme is automatically synchronized with the application's theme (light/dark mode). This is achieved using a MutationObserver that watches for changes to the class attribute of the <html> element. When the dark class is added or removed, the editor's theme is updated accordingly.
Editor Options
The component exposes props to control editor features like the minimap and word wrapping. These options are dynamically updated using a useEffect hook that listens for changes to the minimapEnabled and wordWrapEnabled props.
Code Breakdown
import { useRef, useEffect, useState } from 'react';
import MonacoEditor, { type OnMount } from '@monaco-editor/react';
import type { editor } from 'monaco-editor';
interface EditorProps {
  value: string;
  onChange: (value: string) => void;
  minimapEnabled: boolean;
  wordWrapEnabled?: boolean;
}
const Editor: React.FC<EditorProps> = ({
  value,
  onChange,
  minimapEnabled,
  wordWrapEnabled = true
}) => {
  const editorRef = useRef<editor.IStandaloneCodeEditor | null>(null);
  const [theme, setTheme] = useState<'vs-dark' | 'vs-light'>('vs-dark');
  const handleEditorDidMount: OnMount = (editor) => {
    editorRef.current = editor;
  };
  useEffect(() => {
    if (editorRef.current) {
      editorRef.current.updateOptions({
        minimap: {
          enabled: minimapEnabled,
        },
        wordWrap: wordWrapEnabled ? 'on' : 'off',
      });
    }
  }, [minimapEnabled, wordWrapEnabled]);
  useEffect(() => {
    const updateTheme = () => {
      const isDark = document.documentElement.classList.contains('dark');
      setTheme(isDark ? 'vs-dark' : 'vs-light');
    };
    updateTheme();
    const observer = new MutationObserver(() => updateTheme());
    observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
    return () => observer.disconnect();
  }, []);
  return (
    <div className="h-full w-full">
      <MonacoEditor
        height="100%"
        defaultLanguage="markdown"
        value={value}
        onChange={(value) => onChange(value || '')}
        onMount={handleEditorDidMount}
        theme={theme}
        options={{
          wordWrap: wordWrapEnabled ? 'on' : 'off',
          minimap: {
            enabled: minimapEnabled,
          },
          // ... other options
        }}
      />
    </div>
  );
};
export default Editor;
editorRef: AuseRefhook to hold a reference to the Monaco Editor instance. This allows direct interaction with the editor's API.handleEditorDidMount: A callback function that runs when the editor is first mounted. It saves the editor instance toeditorRef.useEffectfor Options: This effect runs wheneverminimapEnabledorwordWrapEnabledprops change. It calls the editor'supdateOptionsmethod to apply the new settings.useEffectfor Theme: This effect sets up theMutationObserverto listen for theme changes on the main document and updates the editor's theme state accordingly. It also cleans up the observer when the component unmounts.