File size: 4,171 Bytes
6cdce85
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
'use client';

import { useRef, useCallback } from 'react';
import Editor, { OnMount, OnChange } from '@monaco-editor/react';
import type * as Monaco from 'monaco-editor';
import { Loader2 } from 'lucide-react';

interface CodeEditorProps {
  value: string;
  onChange: (value: string) => void;
  language?: string;
  readOnly?: boolean;
  height?: string;
  className?: string;
}

export function CodeEditor({
  value,
  onChange,
  language = 'python',
  readOnly = false,
  height = '100%',
  className,
}: CodeEditorProps) {
  const editorRef = useRef<Monaco.editor.IStandaloneCodeEditor | null>(null);

  const handleEditorMount: OnMount = useCallback((editor) => {
    editorRef.current = editor;
    // Don't auto-focus to prevent unwanted page scroll when selecting problems
  }, []);

  const handleChange: OnChange = useCallback((val) => {
    onChange(val || '');
  }, [onChange]);

  return (
    <div className={className} style={{ height, minHeight: '300px' }}>
      <Editor
        height="100%"
        width="100%"
        language={language}
        value={value}
        onChange={handleChange}
        onMount={handleEditorMount}
        theme="quantum-dark"
        loading={
          <div className="flex items-center justify-center h-full bg-zinc-900">
            <Loader2 className="w-6 h-6 animate-spin text-teal-500" />
          </div>
        }
        beforeMount={(monaco) => {
          monaco.editor.defineTheme('quantum-dark', {
            base: 'vs-dark',
            inherit: true,
            rules: [
              { token: 'comment', foreground: '71717a', fontStyle: 'italic' },
              { token: 'keyword', foreground: 'c4b5fd' },
              { token: 'string', foreground: '86efac' },
              { token: 'number', foreground: 'c4b5fd' },
              { token: 'type', foreground: '93c5fd' },
              { token: 'function', foreground: '93c5fd' },
              { token: 'variable', foreground: 'd4d4d8' },
              { token: 'operator', foreground: 'f0abfc' },
              { token: 'delimiter', foreground: 'a1a1aa' },
            ],
            colors: {
              'editor.background': '#18181b',
              'editor.foreground': '#d4d4d8',
              'editor.lineHighlightBackground': '#27272a',
              'editor.selectionBackground': '#0d9488aa',
              'editor.inactiveSelectionBackground': '#27272a',
              'editorCursor.foreground': '#14b8a6',
              'editorLineNumber.foreground': '#52525b',
              'editorLineNumber.activeForeground': '#a1a1aa',
              'editorIndentGuide.background': '#27272a',
              'editorIndentGuide.activeBackground': '#3f3f46',
              'editor.selectionHighlightBackground': '#0d94882a',
              'editorBracketMatch.background': '#0d94884a',
              'editorBracketMatch.border': '#14b8a6',
              'scrollbar.shadow': '#00000000',
              'scrollbarSlider.background': '#3f3f4680',
              'scrollbarSlider.hoverBackground': '#52525b80',
              'scrollbarSlider.activeBackground': '#71717a80',
            },
          });
        }}
        options={{
          readOnly,
          fontSize: 14,
          fontFamily: "'JetBrains Mono', Consolas, 'Courier New', monospace",
          fontLigatures: true,
          lineHeight: 1.6,
          padding: { top: 16, bottom: 16 },
          minimap: { enabled: false },
          scrollBeyondLastLine: false,
          automaticLayout: true,
          tabSize: 4,
          insertSpaces: true,
          wordWrap: 'on',
          lineNumbers: 'on',
          glyphMargin: false,
          folding: true,
          lineDecorationsWidth: 8,
          lineNumbersMinChars: 4,
          renderLineHighlight: 'line',
          cursorBlinking: 'smooth',
          cursorSmoothCaretAnimation: 'on',
          smoothScrolling: true,
          contextmenu: true,
          quickSuggestions: true,
          suggestOnTriggerCharacters: true,
          acceptSuggestionOnEnter: 'on',
          formatOnPaste: true,
          formatOnType: true,
          bracketPairColorization: { enabled: true },
        }}
      />
    </div>
  );
}