Skip to content

Commit d0dfc8a

Browse files
committed
feat: editor colors
one dark is goated, should be extensible with other themes in the future as well
1 parent 36fbd46 commit d0dfc8a

File tree

2 files changed

+206
-33
lines changed

2 files changed

+206
-33
lines changed
Lines changed: 7 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
1-
import {
2-
defaultHighlightStyle,
3-
syntaxHighlighting,
4-
} from "@codemirror/language";
5-
import type { Extension } from "@codemirror/state";
61
import { EditorState } from "@codemirror/state";
72
import {
83
EditorView,
94
highlightActiveLineGutter,
105
lineNumbers,
116
} from "@codemirror/view";
7+
import { useThemeStore } from "@stores/themeStore";
128
import { useEffect, useRef } from "react";
9+
import { oneDark, oneLight } from "../theme/editorTheme";
1310
import { getLanguageExtension } from "../utils/languages";
1411

1512
interface CodeMirrorEditorProps {
@@ -25,45 +22,22 @@ export function CodeMirrorEditor({
2522
}: CodeMirrorEditorProps) {
2623
const editorRef = useRef<HTMLDivElement>(null);
2724
const viewRef = useRef<EditorView | null>(null);
25+
const isDarkMode = useThemeStore((state) => state.isDarkMode);
2826

2927
useEffect(() => {
3028
if (!editorRef.current) {
3129
return;
3230
}
3331

3432
const languageExtension = filePath ? getLanguageExtension(filePath) : null;
33+
const theme = isDarkMode ? oneDark : oneLight;
3534

36-
const extensions: Extension[] = [
35+
const extensions = [
3736
lineNumbers(),
3837
highlightActiveLineGutter(),
39-
syntaxHighlighting(defaultHighlightStyle),
38+
theme,
4039
EditorView.editable.of(!readOnly),
4140
...(languageExtension ? [languageExtension] : []),
42-
EditorView.theme({
43-
"&": {
44-
height: "100%",
45-
fontSize: "14px",
46-
backgroundColor: "var(--color-background)",
47-
},
48-
".cm-scroller": {
49-
overflow: "auto",
50-
fontFamily: "var(--code-font-family)",
51-
},
52-
".cm-content": {
53-
padding: "16px 0",
54-
},
55-
".cm-line": {
56-
padding: "0 16px",
57-
},
58-
".cm-gutters": {
59-
backgroundColor: "var(--color-background)",
60-
color: "var(--gray-9)",
61-
border: "none",
62-
},
63-
".cm-activeLineGutter": {
64-
backgroundColor: "var(--color-background)",
65-
},
66-
}),
6741
];
6842

6943
const state = EditorState.create({
@@ -80,7 +54,7 @@ export function CodeMirrorEditor({
8054
viewRef.current?.destroy();
8155
viewRef.current = null;
8256
};
83-
}, [content, filePath, readOnly]);
57+
}, [content, filePath, isDarkMode, readOnly]);
8458

8559
return <div ref={editorRef} style={{ height: "100%", width: "100%" }} />;
8660
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import { HighlightStyle, syntaxHighlighting } from "@codemirror/language";
2+
import type { Extension } from "@codemirror/state";
3+
import { EditorView } from "@codemirror/view";
4+
import { tags as t } from "@lezer/highlight";
5+
6+
function withAlpha(hex: string, alpha: number): string {
7+
const a = Math.round(alpha * 255)
8+
.toString(16)
9+
.padStart(2, "0");
10+
return `${hex}${a}`;
11+
}
12+
13+
const dark = {
14+
chalky: "#e5c07b",
15+
coral: "#e06c75",
16+
cyan: "#56b6c2",
17+
invalid: "#ffffff",
18+
ivory: "#abb2bf",
19+
stone: "#7d8799",
20+
malibu: "#61afef",
21+
sage: "#98c379",
22+
whiskey: "#d19a66",
23+
violet: "#c678dd",
24+
background: "#21252b",
25+
highlightBackground: "#2c313a",
26+
tooltipBackground: "#353a42",
27+
selection: "#3E4451",
28+
cursor: "#528bff",
29+
};
30+
31+
const light = {
32+
chalky: "#c18401",
33+
coral: "#e45649",
34+
cyan: "#0184bc",
35+
invalid: "#000000",
36+
ivory: "#383a42",
37+
stone: "#a0a1a7",
38+
malibu: "#4078f2",
39+
sage: "#50a14f",
40+
whiskey: "#986801",
41+
violet: "#a626a4",
42+
background: "#fafafa",
43+
highlightBackground: "#d0d0d0",
44+
tooltipBackground: "#f0f0f0",
45+
selection: "#d7d7d7",
46+
cursor: "#526fff",
47+
};
48+
49+
function createEditorTheme(colors: typeof dark, isDark: boolean) {
50+
return EditorView.theme(
51+
{
52+
"&": {
53+
height: "100%",
54+
fontSize: "13px",
55+
color: colors.ivory,
56+
backgroundColor: "var(--color-background)",
57+
},
58+
".cm-scroller": {
59+
overflow: "auto",
60+
fontFamily: "var(--code-font-family)",
61+
},
62+
".cm-content": {
63+
padding: "16px 0",
64+
caretColor: colors.cursor,
65+
},
66+
".cm-line": {
67+
padding: "0 16px",
68+
},
69+
".cm-cursor, .cm-dropCursor": {
70+
borderLeftColor: colors.cursor,
71+
},
72+
"&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
73+
{
74+
backgroundColor: colors.selection,
75+
},
76+
".cm-panels": {
77+
backgroundColor: colors.background,
78+
color: colors.ivory,
79+
},
80+
".cm-panels.cm-panels-top": {
81+
borderBottom: "2px solid black",
82+
},
83+
".cm-panels.cm-panels-bottom": {
84+
borderTop: "2px solid black",
85+
},
86+
".cm-searchMatch": {
87+
backgroundColor: withAlpha(colors.malibu, 0.35),
88+
outline: `1px solid ${colors.malibu}`,
89+
},
90+
".cm-searchMatch.cm-searchMatch-selected": {
91+
backgroundColor: withAlpha(colors.malibu, 0.2),
92+
},
93+
".cm-activeLine": {
94+
backgroundColor: withAlpha(colors.malibu, 0.04),
95+
},
96+
".cm-selectionMatch": {
97+
backgroundColor: withAlpha(colors.sage, 0.1),
98+
},
99+
"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": {
100+
backgroundColor: withAlpha(colors.malibu, 0.3),
101+
},
102+
".cm-gutters": {
103+
backgroundColor: "var(--color-background)",
104+
color: colors.stone,
105+
border: "none",
106+
},
107+
".cm-activeLineGutter": {
108+
backgroundColor: colors.highlightBackground,
109+
},
110+
".cm-foldPlaceholder": {
111+
backgroundColor: "transparent",
112+
border: "none",
113+
color: colors.stone,
114+
},
115+
".cm-tooltip": {
116+
border: "none",
117+
backgroundColor: colors.tooltipBackground,
118+
},
119+
".cm-tooltip .cm-tooltip-arrow:before": {
120+
borderTopColor: "transparent",
121+
borderBottomColor: "transparent",
122+
},
123+
".cm-tooltip .cm-tooltip-arrow:after": {
124+
borderTopColor: colors.tooltipBackground,
125+
borderBottomColor: colors.tooltipBackground,
126+
},
127+
".cm-tooltip-autocomplete": {
128+
"& > ul > li[aria-selected]": {
129+
backgroundColor: colors.highlightBackground,
130+
color: colors.ivory,
131+
},
132+
},
133+
},
134+
{ dark: isDark },
135+
);
136+
}
137+
138+
function createHighlightStyle(colors: typeof dark) {
139+
return HighlightStyle.define([
140+
{ tag: t.keyword, color: colors.violet },
141+
{
142+
tag: [t.name, t.deleted, t.character, t.propertyName, t.macroName],
143+
color: colors.coral,
144+
},
145+
{ tag: [t.function(t.variableName), t.labelName], color: colors.malibu },
146+
{
147+
tag: [t.color, t.constant(t.name), t.standard(t.name)],
148+
color: colors.whiskey,
149+
},
150+
{ tag: [t.definition(t.name), t.separator], color: colors.ivory },
151+
{
152+
tag: [
153+
t.typeName,
154+
t.className,
155+
t.number,
156+
t.changed,
157+
t.annotation,
158+
t.modifier,
159+
t.self,
160+
t.namespace,
161+
],
162+
color: colors.chalky,
163+
},
164+
{
165+
tag: [
166+
t.operator,
167+
t.operatorKeyword,
168+
t.url,
169+
t.escape,
170+
t.regexp,
171+
t.link,
172+
t.special(t.string),
173+
],
174+
color: colors.cyan,
175+
},
176+
{ tag: [t.meta, t.comment], color: colors.stone },
177+
{ tag: t.strong, fontWeight: "bold" },
178+
{ tag: t.emphasis, fontStyle: "italic" },
179+
{ tag: t.strikethrough, textDecoration: "line-through" },
180+
{ tag: t.link, color: colors.stone, textDecoration: "underline" },
181+
{ tag: t.heading, fontWeight: "bold", color: colors.coral },
182+
{ tag: [t.atom, t.bool, t.special(t.variableName)], color: colors.whiskey },
183+
{
184+
tag: [t.processingInstruction, t.string, t.inserted],
185+
color: colors.sage,
186+
},
187+
{ tag: t.invalid, color: colors.invalid },
188+
]);
189+
}
190+
191+
export const oneDark: Extension = [
192+
createEditorTheme(dark, true),
193+
syntaxHighlighting(createHighlightStyle(dark)),
194+
];
195+
196+
export const oneLight: Extension = [
197+
createEditorTheme(light, false),
198+
syntaxHighlighting(createHighlightStyle(light)),
199+
];

0 commit comments

Comments
 (0)