From 3605d9611946ac19772ff97683511dc602a04411 Mon Sep 17 00:00:00 2001 From: Raunak Raj <71929976+bajrangCoder@users.noreply.github.com> Date: Sun, 2 Aug 2026 13:01:06 +0530 Subject: [PATCH] fix(editor): correct CodeMirror theme colors and add Catppuccin --- src/cm/themes/catppuccin.js | 270 ++++++++++++++++++++++++++++++++ src/cm/themes/dracula.js | 2 +- src/cm/themes/githubDark.js | 60 +++---- src/cm/themes/githubLight.js | 60 +++---- src/cm/themes/index.js | 14 ++ src/cm/themes/solarizedDark.js | 23 +-- src/cm/themes/solarizedLight.js | 21 +-- src/cm/themes/tokyoNight.js | 31 ++-- src/cm/themes/tokyoNightDay.js | 2 +- src/cm/themes/vscodeDark.js | 22 ++- tests/unit/editorThemes.test.js | 64 ++++++++ 11 files changed, 466 insertions(+), 103 deletions(-) create mode 100644 src/cm/themes/catppuccin.js create mode 100644 tests/unit/editorThemes.test.js diff --git a/src/cm/themes/catppuccin.js b/src/cm/themes/catppuccin.js new file mode 100644 index 0000000000..d83e90f3a7 --- /dev/null +++ b/src/cm/themes/catppuccin.js @@ -0,0 +1,270 @@ +import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"; +import { EditorView } from "@codemirror/view"; +import { tags as t } from "@lezer/highlight"; + +const palettes = { + latte: { + dark: false, + rosewater: "#dc8a78", + pink: "#ea76cb", + mauve: "#8839ef", + red: "#d20f39", + peach: "#fe640b", + yellow: "#df8e1d", + green: "#40a02b", + teal: "#179299", + sky: "#04a5e5", + blue: "#1e66f5", + lavender: "#7287fd", + text: "#4c4f69", + subtext0: "#6c6f85", + overlay2: "#7c7f93", + overlay1: "#8c8fa1", + overlay0: "#9ca0b0", + surface2: "#acb0be", + surface1: "#bcc0cc", + surface0: "#ccd0da", + base: "#eff1f5", + mantle: "#e6e9ef", + }, + frappe: { + dark: true, + rosewater: "#f2d5cf", + pink: "#f4b8e4", + mauve: "#ca9ee6", + red: "#e78284", + peach: "#ef9f76", + yellow: "#e5c890", + green: "#a6d189", + teal: "#81c8be", + sky: "#99d1db", + blue: "#8caaee", + lavender: "#babbf1", + text: "#c6d0f5", + subtext0: "#a5adce", + overlay2: "#949cbb", + overlay1: "#838ba7", + overlay0: "#737994", + surface2: "#626880", + surface1: "#51576d", + surface0: "#414559", + base: "#303446", + mantle: "#292c3c", + }, + macchiato: { + dark: true, + rosewater: "#f4dbd6", + pink: "#f5bde6", + mauve: "#c6a0f6", + red: "#ed8796", + peach: "#f5a97f", + yellow: "#eed49f", + green: "#a6da95", + teal: "#8bd5ca", + sky: "#91d7e3", + blue: "#8aadf4", + lavender: "#b7bdf8", + text: "#cad3f5", + subtext0: "#a5adcb", + overlay2: "#939ab7", + overlay1: "#8087a2", + overlay0: "#6e738d", + surface2: "#5b6078", + surface1: "#494d64", + surface0: "#363a4f", + base: "#24273a", + mantle: "#1e2030", + }, + mocha: { + dark: true, + rosewater: "#f5e0dc", + pink: "#f5c2e7", + mauve: "#cba6f7", + red: "#f38ba8", + peach: "#fab387", + yellow: "#f9e2af", + green: "#a6e3a1", + teal: "#94e2d5", + sky: "#89dceb", + blue: "#89b4fa", + lavender: "#b4befe", + text: "#cdd6f4", + subtext0: "#a6adc8", + overlay2: "#9399b2", + overlay1: "#7f849c", + overlay0: "#6c7086", + surface2: "#585b70", + surface1: "#45475a", + surface0: "#313244", + base: "#1e1e2e", + mantle: "#181825", + }, +}; + +function createConfig(name, caption, palette) { + return { + name, + caption, + dark: palette.dark, + background: palette.base, + foreground: palette.text, + selection: `${palette.overlay2}40`, + selectionMatch: `${palette.surface2}4d`, + cursor: palette.rosewater, + dropdownBackground: palette.mantle, + dropdownBorder: palette.overlay0, + activeLine: palette.surface0, + lineNumber: palette.subtext0, + lineNumberActive: palette.mauve, + matchingBracket: `${palette.surface2}47`, + keyword: palette.mauve, + variable: palette.text, + parameter: palette.text, + function: palette.blue, + string: palette.green, + constant: palette.peach, + type: palette.yellow, + class: palette.yellow, + number: palette.peach, + comment: palette.overlay2, + heading: palette.blue, + invalid: palette.red, + regexp: palette.pink, + tag: palette.blue, + operator: palette.sky, + palette, + }; +} + +export const configs = [ + createConfig("catppuccinLatte", "Catppuccin Latte", palettes.latte), + createConfig("catppuccinFrappe", "Catppuccin Frappé", palettes.frappe), + createConfig( + "catppuccinMacchiato", + "Catppuccin Macchiato", + palettes.macchiato, + ), + createConfig("catppuccinMocha", "Catppuccin Mocha", palettes.mocha), +]; + +function createCatppuccinTheme(config) { + const colors = config.palette; + const theme = EditorView.theme( + { + "&": { + color: config.foreground, + backgroundColor: config.background, + }, + ".cm-content": { caretColor: config.cursor }, + ".cm-cursor, .cm-dropCursor": { + borderLeftColor: config.cursor, + }, + "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection": + { backgroundColor: config.selection }, + ".cm-panels": { + backgroundColor: config.dropdownBackground, + color: config.foreground, + }, + ".cm-panels.cm-panels-top": { + borderBottom: `1px solid ${config.dropdownBorder}`, + }, + ".cm-panels.cm-panels-bottom": { + borderTop: `1px solid ${config.dropdownBorder}`, + }, + ".cm-searchMatch": { + backgroundColor: `${colors.blue}59`, + outline: `1px solid ${colors.blue}`, + }, + ".cm-searchMatch.cm-searchMatch-selected": { + backgroundColor: `${colors.blue}2f`, + }, + ".cm-activeLine": { backgroundColor: config.activeLine }, + ".cm-selectionMatch": { backgroundColor: config.selectionMatch }, + "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": { + backgroundColor: config.matchingBracket, + color: config.foreground, + }, + ".cm-gutters": { + backgroundColor: config.background, + color: config.lineNumber, + border: "none", + }, + ".cm-activeLineGutter": { backgroundColor: config.activeLine }, + ".cm-lineNumbers .cm-gutterElement": { color: config.lineNumber }, + ".cm-lineNumbers .cm-activeLineGutter": { + color: config.lineNumberActive, + }, + ".cm-foldPlaceholder": { + backgroundColor: "transparent", + border: "none", + color: colors.overlay0, + }, + ".cm-placeholder": { color: colors.overlay1 }, + ".cm-tooltip": { + border: "none", + backgroundColor: colors.surface0, + color: config.foreground, + }, + ".cm-tooltip-autocomplete": { + "& > ul > li[aria-selected]": { + backgroundColor: colors.surface1, + color: config.foreground, + }, + }, + }, + { dark: config.dark }, + ); + + const highlightStyle = HighlightStyle.define([ + { tag: t.keyword, color: config.keyword }, + { + tag: [t.name, t.definition(t.name), t.deleted, t.character, t.macroName], + color: config.variable, + }, + { + tag: [ + t.function(t.variableName), + t.function(t.propertyName), + t.propertyName, + t.labelName, + ], + color: config.function, + }, + { + tag: [t.color, t.constant(t.name), t.standard(t.name)], + color: config.constant, + }, + { tag: [t.self, t.atom], color: colors.red }, + { + tag: [t.typeName, t.className, t.changed, t.annotation, t.namespace], + color: config.type, + }, + { tag: t.operator, color: config.operator }, + { tag: t.url, color: colors.teal }, + { tag: [t.escape, t.regexp], color: config.regexp }, + { + tag: [t.meta, t.punctuation, t.separator, t.comment], + color: config.comment, + }, + { tag: t.strong, fontWeight: "bold" }, + { tag: t.emphasis, fontStyle: "italic" }, + { tag: t.strikethrough, textDecoration: "line-through" }, + { tag: t.link, color: config.function, textDecoration: "underline" }, + { tag: t.heading, fontWeight: "bold", color: config.heading }, + { tag: t.special(t.variableName), color: colors.lavender }, + { tag: [t.bool, t.number], color: config.number }, + { + tag: [t.processingInstruction, t.string, t.inserted], + color: config.string, + }, + { tag: t.invalid, color: config.invalid }, + ]); + + return [theme, syntaxHighlighting(highlightStyle)]; +} + +export const themes = new Map( + configs.map((config) => [config.name, createCatppuccinTheme(config)]), +); + +export default themes; diff --git a/src/cm/themes/dracula.js b/src/cm/themes/dracula.js index 3da64920da..8a02c06cbc 100644 --- a/src/cm/themes/dracula.js +++ b/src/cm/themes/dracula.js @@ -127,7 +127,7 @@ export const draculaHighlightStyle = HighlightStyle.define([ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number, }, - { tag: [t.typeName], color: config.type, fontStyle: config.type }, + { tag: [t.typeName], color: config.type }, { tag: [t.operator, t.operatorKeyword], color: config.keyword }, { tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp }, { tag: [t.meta, t.comment], color: config.comment }, diff --git a/src/cm/themes/githubDark.js b/src/cm/themes/githubDark.js index b08bbfb662..e1dcf76533 100644 --- a/src/cm/themes/githubDark.js +++ b/src/cm/themes/githubDark.js @@ -1,34 +1,37 @@ import { HighlightStyle, syntaxHighlighting } from "@codemirror/language"; -import { EditorView, lineNumbers } from "@codemirror/view"; +import { EditorView } from "@codemirror/view"; import { tags as t } from "@lezer/highlight"; export const config = { name: "githubDark", dark: true, - background: "#24292e", - foreground: "#d1d5da", + background: "#0d1117", + foreground: "#e6edf3", selection: "#3392FF44", - cursor: "#c8e1ff", - dropdownBackground: "#24292e", - dropdownBorder: "#1b1f23", - activeLine: "#4d566022", - lineNumber: "#444d56", - lineNumberActive: "#e1e4e8", - matchingBracket: "#17E5E650", - keyword: "#f97583", - storage: "#f97583", - variable: "#ffab70", - parameter: "#e1e4e8", - function: "#79b8ff", - string: "#9ecbff", - constant: "#79b8ff", - type: "#79b8ff", - class: "#b392f0", - number: "#79b8ff", - comment: "#6a737d", - heading: "#79b8ff", - invalid: "#f97583", - regexp: "#9ecbff", + cursor: "#e6edf3", + dropdownBackground: "#161b22", + dropdownBorder: "#30363d", + activeLine: "#6e76811a", + lineNumber: "#6e7681", + lineNumberActive: "#e6edf3", + matchingBracket: "#6e76811a", + keyword: "#ff7b72", + storage: "#ff7b72", + variable: "#e6edf3", + parameter: "#e6edf3", + function: "#d2a8ff", + string: "#a5d6ff", + constant: "#79c0ff", + type: "#79c0ff", + class: "#7ee787", + number: "#79c0ff", + comment: "#8b949e", + heading: "#ffa657", + invalid: "#f85149", + regexp: "#a5d6ff", + tag: "#7ee787", + property: "#79c0ff", + operator: "#79c0ff", }; export const githubDarkTheme = EditorView.theme( @@ -111,7 +114,7 @@ export const githubDarkHighlightStyle = HighlightStyle.define([ tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable, }, - { tag: [t.propertyName], color: config.function }, + { tag: [t.propertyName], color: config.property }, { tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string, @@ -127,15 +130,16 @@ export const githubDarkHighlightStyle = HighlightStyle.define([ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number, }, - { tag: [t.typeName], color: config.type, fontStyle: config.type }, - { tag: [t.operator, t.operatorKeyword], color: config.keyword }, + { tag: [t.typeName], color: config.type }, + { tag: [t.operator, t.operatorKeyword], color: config.operator }, { tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp }, { tag: [t.meta, t.comment], color: config.comment }, + { tag: t.tagName, color: config.tag }, { tag: t.strong, fontWeight: "bold" }, { tag: t.emphasis, fontStyle: "italic" }, { tag: t.link, textDecoration: "underline" }, { tag: t.heading, fontWeight: "bold", color: config.heading }, - { tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: config.constant }, { tag: t.invalid, color: config.invalid }, { tag: t.strikethrough, textDecoration: "line-through" }, ]); diff --git a/src/cm/themes/githubLight.js b/src/cm/themes/githubLight.js index 92dcc83a53..e05b03bb27 100644 --- a/src/cm/themes/githubLight.js +++ b/src/cm/themes/githubLight.js @@ -5,30 +5,33 @@ import { tags as t } from "@lezer/highlight"; export const config = { name: "githubLight", dark: false, - background: "#fff", - foreground: "#444d56", - selection: "#0366d625", - cursor: "#044289", - dropdownBackground: "#fff", - dropdownBorder: "#e1e4e8", - activeLine: "#c6c6c622", - lineNumber: "#1b1f234d", - lineNumberActive: "#24292e", - matchingBracket: "#34d05840", - keyword: "#d73a49", - storage: "#d73a49", - variable: "#e36209", - parameter: "#24292e", - function: "#005cc5", - string: "#032f62", - constant: "#005cc5", - type: "#005cc5", - class: "#6f42c1", - number: "#005cc5", - comment: "#6a737d", - heading: "#005cc5", - invalid: "#cb2431", - regexp: "#032f62", + background: "#ffffff", + foreground: "#1f2328", + selection: "#0969da33", + cursor: "#0969da", + dropdownBackground: "#ffffff", + dropdownBorder: "#d0d7de", + activeLine: "#afb8c133", + lineNumber: "#8c959f", + lineNumberActive: "#1f2328", + matchingBracket: "#2da44e40", + keyword: "#cf222e", + storage: "#cf222e", + variable: "#1f2328", + parameter: "#1f2328", + function: "#8250df", + string: "#0a3069", + constant: "#0550ae", + type: "#0550ae", + class: "#116329", + number: "#0550ae", + comment: "#656d76", + heading: "#0550ae", + invalid: "#82071e", + regexp: "#0a3069", + tag: "#116329", + property: "#0550ae", + operator: "#0550ae", }; export const githubLightTheme = EditorView.theme( @@ -111,7 +114,7 @@ export const githubLightHighlightStyle = HighlightStyle.define([ tag: [t.name, t.deleted, t.character, t.macroName], color: config.variable, }, - { tag: [t.propertyName], color: config.function }, + { tag: [t.propertyName], color: config.property }, { tag: [t.processingInstruction, t.string, t.inserted, t.special(t.string)], color: config.string, @@ -127,15 +130,16 @@ export const githubLightHighlightStyle = HighlightStyle.define([ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number, }, - { tag: [t.typeName], color: config.type, fontStyle: config.type }, - { tag: [t.operator, t.operatorKeyword], color: config.keyword }, + { tag: [t.typeName], color: config.type }, + { tag: [t.operator, t.operatorKeyword], color: config.operator }, { tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp }, { tag: [t.meta, t.comment], color: config.comment }, + { tag: t.tagName, color: config.tag }, { tag: t.strong, fontWeight: "bold" }, { tag: t.emphasis, fontStyle: "italic" }, { tag: t.link, textDecoration: "underline" }, { tag: t.heading, fontWeight: "bold", color: config.heading }, - { tag: [t.atom, t.bool, t.special(t.variableName)], color: config.variable }, + { tag: [t.atom, t.bool, t.special(t.variableName)], color: config.constant }, { tag: t.invalid, color: config.invalid }, { tag: t.strikethrough, textDecoration: "line-through" }, ]); diff --git a/src/cm/themes/index.js b/src/cm/themes/index.js index 342d1a0231..dbcd67eba1 100644 --- a/src/cm/themes/index.js +++ b/src/cm/themes/index.js @@ -2,6 +2,10 @@ import { EditorState } from "@codemirror/state"; import { oneDark } from "@codemirror/theme-one-dark"; import aura, { config as auraConfig } from "./aura"; import ayuDark, { config as ayuDarkConfig } from "./ayuDark"; +import { + configs as catppuccinConfigs, + themes as catppuccinThemes, +} from "./catppuccin"; import cobalt, { config as cobaltConfig } from "./cobalt"; import dracula, { config as draculaConfig } from "./dracula"; import githubDark, { config as githubDarkConfig } from "./githubDark"; @@ -276,6 +280,16 @@ addTheme( vscodeDarkConfig, ); +for (const config of catppuccinConfigs) { + addTheme( + config.name, + config.caption, + config.dark, + () => catppuccinThemes.get(config.name), + config, + ); +} + export default { getThemes, getThemeById, diff --git a/src/cm/themes/solarizedDark.js b/src/cm/themes/solarizedDark.js index aab15ece38..088a200a64 100644 --- a/src/cm/themes/solarizedDark.js +++ b/src/cm/themes/solarizedDark.js @@ -6,14 +6,15 @@ export const config = { name: "solarizedDark", dark: true, background: "#002B36", - foreground: "#93A1A1", - selection: "#274642", + foreground: "#839496", + selection: "#004454AA", + selectionMatch: "#005A6FAA", cursor: "#D30102", - dropdownBackground: "#002B36", + dropdownBackground: "#00212B", dropdownBorder: "#2AA19899", - activeLine: "#005b7022", - lineNumber: "#93A1A1", - lineNumberActive: "#949494", + activeLine: "#00CAFE11", + lineNumber: "#839496", + lineNumberActive: "#93A1A1", matchingBracket: "#073642", keyword: "#859900", storage: "#93A1A1", @@ -22,13 +23,14 @@ export const config = { function: "#268BD2", string: "#2AA198", constant: "#CB4B16", - type: "#CB4B16", - class: "#CB4B16", + type: "#859900", + class: "#268BD2", number: "#D33682", comment: "#586E75", heading: "#268BD2", invalid: "#DC322F", regexp: "#DC322F", + tag: "#268BD2", }; export const solarizedDarkTheme = EditorView.theme( @@ -60,7 +62,7 @@ export const solarizedDarkTheme = EditorView.theme( }, ".cm-activeLine": { backgroundColor: config.activeLine }, - ".cm-selectionMatch": { backgroundColor: config.selection }, + ".cm-selectionMatch": { backgroundColor: config.selectionMatch }, "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": { backgroundColor: config.matchingBracket, @@ -127,10 +129,11 @@ export const solarizedDarkHighlightStyle = HighlightStyle.define([ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number, }, - { tag: [t.typeName], color: config.type, fontStyle: config.type }, + { tag: [t.typeName], color: config.type }, { tag: [t.operator, t.operatorKeyword], color: config.keyword }, { tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp }, { tag: [t.meta, t.comment], color: config.comment }, + { tag: t.tagName, color: config.tag }, { tag: t.strong, fontWeight: "bold" }, { tag: t.emphasis, fontStyle: "italic" }, { tag: t.link, textDecoration: "underline" }, diff --git a/src/cm/themes/solarizedLight.js b/src/cm/themes/solarizedLight.js index 022899460e..01dd678f5a 100644 --- a/src/cm/themes/solarizedLight.js +++ b/src/cm/themes/solarizedLight.js @@ -6,14 +6,15 @@ export const config = { name: "solarizedLight", dark: false, background: "#FDF6E3", - foreground: "#586E75", + foreground: "#657B83", selection: "#EEE8D5", + selectionMatch: "#EEE8D5", cursor: "#657B83", - dropdownBackground: "#FDF6E3", + dropdownBackground: "#EEE8D5", dropdownBorder: "#D3AF86", - activeLine: "#d5bd5c22", - lineNumber: "#586E75", - lineNumberActive: "#567983", + activeLine: "#3D392D11", + lineNumber: "#657B83", + lineNumberActive: "#586E75", matchingBracket: "#EEE8D5", keyword: "#859900", storage: "#586E75", @@ -22,13 +23,14 @@ export const config = { function: "#268BD2", string: "#2AA198", constant: "#CB4B16", - type: "#CB4B16", - class: "#CB4B16", + type: "#859900", + class: "#268BD2", number: "#D33682", comment: "#93A1A1", heading: "#268BD2", invalid: "#DC322F", regexp: "#DC322F", + tag: "#268BD2", }; export const solarizedLightTheme = EditorView.theme( @@ -60,7 +62,7 @@ export const solarizedLightTheme = EditorView.theme( }, ".cm-activeLine": { backgroundColor: config.activeLine }, - ".cm-selectionMatch": { backgroundColor: config.selection }, + ".cm-selectionMatch": { backgroundColor: config.selectionMatch }, "&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": { backgroundColor: config.matchingBracket, @@ -127,10 +129,11 @@ export const solarizedLightHighlightStyle = HighlightStyle.define([ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number, }, - { tag: [t.typeName], color: config.type, fontStyle: config.type }, + { tag: [t.typeName], color: config.type }, { tag: [t.operator, t.operatorKeyword], color: config.keyword }, { tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp }, { tag: [t.meta, t.comment], color: config.comment }, + { tag: t.tagName, color: config.tag }, { tag: t.strong, fontWeight: "bold" }, { tag: t.emphasis, fontStyle: "italic" }, { tag: t.link, textDecoration: "underline" }, diff --git a/src/cm/themes/tokyoNight.js b/src/cm/themes/tokyoNight.js index 33af938477..efc55889c4 100644 --- a/src/cm/themes/tokyoNight.js +++ b/src/cm/themes/tokyoNight.js @@ -6,29 +6,31 @@ export const config = { name: "tokyoNight", dark: true, background: "#1a1b26", - foreground: "#787c99", + foreground: "#a9b1d6", selection: "#515c7e40", cursor: "#c0caf5", dropdownBackground: "#1a1b26", - dropdownBorder: "#787c99", - activeLine: "#43455c22", - lineNumber: "#363b54", - lineNumberActive: "#737aa2", - matchingBracket: "#16161e", + dropdownBorder: "#414868", + activeLine: "#24283b", + lineNumber: "#414868", + lineNumberActive: "#a9b1d6", + matchingBracket: "#24283b", keyword: "#bb9af7", storage: "#bb9af7", variable: "#c0caf5", - parameter: "#c0caf5", + parameter: "#cfc9c2", function: "#7aa2f7", string: "#9ece6a", - constant: "#bb9af7", - type: "#0db9d7", + constant: "#ff9e64", + type: "#2ac3de", class: "#c0caf5", number: "#ff9e64", - comment: "#444b6a", - heading: "#89ddff", - invalid: "#ff5370", + comment: "#565f89", + heading: "#7dcfff", + invalid: "#f7768e", regexp: "#b4f9f8", + tag: "#f7768e", + operator: "#bb9af7", }; export const tokyoNightTheme = EditorView.theme( @@ -127,10 +129,11 @@ export const tokyoNightHighlightStyle = HighlightStyle.define([ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number, }, - { tag: [t.typeName], color: config.type, fontStyle: config.type }, - { tag: [t.operator, t.operatorKeyword], color: config.keyword }, + { tag: [t.typeName], color: config.type }, + { tag: [t.operator, t.operatorKeyword], color: config.operator }, { tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp }, { tag: [t.meta, t.comment], color: config.comment }, + { tag: t.tagName, color: config.tag }, { tag: t.strong, fontWeight: "bold" }, { tag: t.emphasis, fontStyle: "italic" }, { tag: t.link, textDecoration: "underline" }, diff --git a/src/cm/themes/tokyoNightDay.js b/src/cm/themes/tokyoNightDay.js index e9f948ffe6..75a9fcf820 100644 --- a/src/cm/themes/tokyoNightDay.js +++ b/src/cm/themes/tokyoNightDay.js @@ -127,7 +127,7 @@ export const tokyoNightDayHighlightStyle = HighlightStyle.define([ tag: [t.number, t.changed, t.annotation, t.modifier, t.self, t.namespace], color: config.number, }, - { tag: [t.typeName], color: config.type, fontStyle: config.type }, + { tag: [t.typeName], color: config.type }, { tag: [t.operator, t.operatorKeyword], color: config.keyword }, { tag: [t.url, t.escape, t.regexp, t.link], color: config.regexp }, { tag: [t.meta, t.comment], color: config.comment }, diff --git a/src/cm/themes/vscodeDark.js b/src/cm/themes/vscodeDark.js index ee631e1752..2045621f5f 100644 --- a/src/cm/themes/vscodeDark.js +++ b/src/cm/themes/vscodeDark.js @@ -5,17 +5,17 @@ import { tags as t } from "@lezer/highlight"; export const config = { name: "vscodeDark", dark: true, - background: "#1e1e1e", - foreground: "#9cdcfe", + background: "#1f1f1f", + foreground: "#cccccc", selection: "#6199ff2f", selectionMatch: "#72a1ff59", cursor: "#c6c6c6", - dropdownBackground: "#1e1e1e", - dropdownBorder: "#3c3c3c", - activeLine: "#ffffff0f", - lineNumber: "#838383", - lineNumberActive: "#ffffff", - matchingBracket: "#515c6a", + dropdownBackground: "#202020", + dropdownBorder: "#454545", + activeLine: "#282828", + lineNumber: "#6e7681", + lineNumberActive: "#cccccc", + matchingBracket: "#add6ff26", keyword: "#569cd6", variable: "#9cdcfe", parameter: "#9cdcfe", @@ -157,10 +157,8 @@ export const vscodeDarkHighlightStyle = HighlightStyle.define([ color: config.variable, }, { tag: t.heading, fontWeight: "bold", color: config.heading }, - { - tag: [t.typeName, t.className, t.tagName, t.standard(t.tagName)], - color: config.type, - }, + { tag: [t.typeName, t.className], color: config.type }, + { tag: [t.tagName, t.standard(t.tagName)], color: config.keyword }, { tag: [t.function(t.variableName), t.function(t.propertyName)], color: config.function, diff --git a/tests/unit/editorThemes.test.js b/tests/unit/editorThemes.test.js new file mode 100644 index 0000000000..0b2dee35e0 --- /dev/null +++ b/tests/unit/editorThemes.test.js @@ -0,0 +1,64 @@ +import { EditorState } from "@codemirror/state"; +import { describe, expect, it } from "vitest"; +import { + getThemeConfig, + getThemeExtensions, + getThemes, +} from "cm/themes"; + +describe("built-in editor themes", () => { + it("uses the base editor foreground for plain VS Code text", () => { + expect(getThemeConfig("vscodeDark")).toMatchObject({ + background: "#1f1f1f", + foreground: "#cccccc", + variable: "#9cdcfe", + }); + }); + + it("uses the current GitHub Dark palette", () => { + expect(getThemeConfig("githubDark")).toMatchObject({ + background: "#0d1117", + foreground: "#e6edf3", + variable: "#e6edf3", + }); + }); + + it("uses current canonical foregrounds for the audited themes", () => { + expect(getThemeConfig("githubLight")).toMatchObject({ + background: "#ffffff", + foreground: "#1f2328", + }); + expect(getThemeConfig("solarizedDark")).toMatchObject({ + background: "#002B36", + foreground: "#839496", + }); + expect(getThemeConfig("solarizedLight")).toMatchObject({ + background: "#FDF6E3", + foreground: "#657B83", + }); + expect(getThemeConfig("tokyoNight")).toMatchObject({ + background: "#1a1b26", + foreground: "#a9b1d6", + }); + }); + + it("registers every official Catppuccin CodeMirror flavor", () => { + const ids = getThemes().map((theme) => theme.id); + for (const id of [ + "catppuccinlatte", + "catppuccinfrappe", + "catppuccinmacchiato", + "catppuccinmocha", + ]) { + expect(ids).toContain(id); + } + }); + + it("constructs every registered editor theme", () => { + for (const { id } of getThemes()) { + expect(() => + EditorState.create({ extensions: getThemeExtensions(id) }), + ).not.toThrow(); + } + }); +});