Skip to content

Commit e9fd94c

Browse files
committed
feat: add dynamic theme support and register custom themes (Dracula, Monokai, GitHub Dark)
1 parent f850f6f commit e9fd94c

File tree

1 file changed

+114
-1
lines changed

1 file changed

+114
-1
lines changed

src/components/CodeEditor/CodeEditor.tsx

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,103 @@ import { useRef, useEffect } from "react";
22
import Editor, { type OnMount } from "@monaco-editor/react";
33
import { emmetHTML, emmetCSS } from "emmet-monaco-es";
44
import type * as Monaco from "monaco-editor";
5+
import type { EditorTheme } from "../SettingsModal/SettingsModal";
6+
7+
// Custom theme definitions for Monaco
8+
const CUSTOM_THEMES: Record<string, Monaco.editor.IStandaloneThemeData> = {
9+
dracula: {
10+
base: "vs-dark",
11+
inherit: true,
12+
rules: [
13+
{ token: "", foreground: "f8f8f2", background: "282a36" },
14+
{ token: "comment", foreground: "6272a4", fontStyle: "italic" },
15+
{ token: "string", foreground: "f1fa8c" },
16+
{ token: "keyword", foreground: "ff79c6" },
17+
{ token: "number", foreground: "bd93f9" },
18+
{ token: "type", foreground: "8be9fd", fontStyle: "italic" },
19+
{ token: "function", foreground: "50fa7b" },
20+
{ token: "variable", foreground: "f8f8f2" },
21+
{ token: "constant", foreground: "bd93f9" },
22+
{ token: "tag", foreground: "ff79c6" },
23+
{ token: "attribute.name", foreground: "50fa7b" },
24+
{ token: "attribute.value", foreground: "f1fa8c" },
25+
],
26+
colors: {
27+
"editor.background": "#282a36",
28+
"editor.foreground": "#f8f8f2",
29+
"editor.lineHighlightBackground": "#44475a",
30+
"editor.selectionBackground": "#44475a",
31+
"editorCursor.foreground": "#f8f8f2",
32+
"editorWhitespace.foreground": "#3B3A32",
33+
"editorIndentGuide.activeBackground": "#9D550FB0",
34+
"editor.selectionHighlightBorder": "#222218",
35+
},
36+
},
37+
monokai: {
38+
base: "vs-dark",
39+
inherit: true,
40+
rules: [
41+
{ token: "", foreground: "f8f8f2", background: "272822" },
42+
{ token: "comment", foreground: "75715e", fontStyle: "italic" },
43+
{ token: "string", foreground: "e6db74" },
44+
{ token: "keyword", foreground: "f92672" },
45+
{ token: "number", foreground: "ae81ff" },
46+
{ token: "type", foreground: "66d9ef", fontStyle: "italic" },
47+
{ token: "function", foreground: "a6e22e" },
48+
{ token: "variable", foreground: "f8f8f2" },
49+
{ token: "constant", foreground: "ae81ff" },
50+
{ token: "tag", foreground: "f92672" },
51+
{ token: "attribute.name", foreground: "a6e22e" },
52+
{ token: "attribute.value", foreground: "e6db74" },
53+
],
54+
colors: {
55+
"editor.background": "#272822",
56+
"editor.foreground": "#f8f8f2",
57+
"editor.lineHighlightBackground": "#3e3d32",
58+
"editor.selectionBackground": "#49483e",
59+
"editorCursor.foreground": "#f8f8f0",
60+
"editorWhitespace.foreground": "#3B3A32",
61+
"editorIndentGuide.activeBackground": "#9D550FB0",
62+
"editor.selectionHighlightBorder": "#222218",
63+
},
64+
},
65+
"github-dark": {
66+
base: "vs-dark",
67+
inherit: true,
68+
rules: [
69+
{ token: "", foreground: "c9d1d9", background: "0d1117" },
70+
{ token: "comment", foreground: "8b949e", fontStyle: "italic" },
71+
{ token: "string", foreground: "a5d6ff" },
72+
{ token: "keyword", foreground: "ff7b72" },
73+
{ token: "number", foreground: "79c0ff" },
74+
{ token: "type", foreground: "ffa657" },
75+
{ token: "function", foreground: "d2a8ff" },
76+
{ token: "variable", foreground: "c9d1d9" },
77+
{ token: "constant", foreground: "79c0ff" },
78+
{ token: "tag", foreground: "7ee787" },
79+
{ token: "attribute.name", foreground: "79c0ff" },
80+
{ token: "attribute.value", foreground: "a5d6ff" },
81+
],
82+
colors: {
83+
"editor.background": "#0d1117",
84+
"editor.foreground": "#c9d1d9",
85+
"editor.lineHighlightBackground": "#161b22",
86+
"editor.selectionBackground": "#264f78",
87+
"editorCursor.foreground": "#c9d1d9",
88+
"editorWhitespace.foreground": "#484f58",
89+
"editorIndentGuide.activeBackground": "#30363d",
90+
"editor.selectionHighlightBorder": "#17191e",
91+
},
92+
},
93+
};
594

695
interface CodeEditorProps {
796
value: string;
897
language: string;
998
onChange: (value: string | undefined) => void;
1099
showMinimap?: boolean;
11100
wordWrap?: boolean;
101+
theme?: EditorTheme;
12102
onSave?: () => void;
13103
}
14104

@@ -18,10 +108,13 @@ function CodeEditor({
18108
onChange,
19109
showMinimap = true,
20110
wordWrap = true,
111+
theme = "vs-dark",
21112
onSave
22113
}: CodeEditorProps) {
23114
const editorRef = useRef<Monaco.editor.IStandaloneCodeEditor | null>(null);
115+
const monacoRef = useRef<typeof Monaco | null>(null);
24116
const disposeEmmetRef = useRef<(() => void) | null>(null);
117+
const themesRegistered = useRef(false);
25118

26119
// Map our language names to Monaco language IDs
27120
const getMonacoLanguage = (lang: string) => {
@@ -35,7 +128,20 @@ function CodeEditor({
35128

36129
const handleEditorMount: OnMount = (editor, monaco) => {
37130
editorRef.current = editor;
131+
monacoRef.current = monaco;
132+
133+
// Register custom themes only once
134+
if (!themesRegistered.current) {
135+
Object.entries(CUSTOM_THEMES).forEach(([themeName, themeData]) => {
136+
monaco.editor.defineTheme(themeName, themeData);
137+
});
138+
themesRegistered.current = true;
139+
}
38140

141+
// Apply initial theme (for custom themes that need registration first)
142+
if (CUSTOM_THEMES[theme]) {
143+
monaco.editor.setTheme(theme);
144+
}
39145
// Enable Emmet for HTML and CSS
40146
try {
41147
const disposeHtml = emmetHTML(monaco);
@@ -79,6 +185,13 @@ function CodeEditor({
79185
}
80186
}, [showMinimap, wordWrap]);
81187

188+
// Update theme dynamically
189+
useEffect(() => {
190+
if (monacoRef.current) {
191+
monacoRef.current.editor.setTheme(theme);
192+
}
193+
}, [theme]);
194+
82195
// Cleanup on unmount
83196
useEffect(() => {
84197
return () => {
@@ -92,7 +205,7 @@ function CodeEditor({
92205
<Editor
93206
height="100%"
94207
language={getMonacoLanguage(language)}
95-
theme="vs-dark"
208+
theme={theme}
96209
value={value}
97210
onChange={onChange}
98211
onMount={handleEditorMount}

0 commit comments

Comments
 (0)