简体   繁体   中英

How to use a VSC theme in monaco-editor?

To my understanding monaco-editor and VSCode are using two different formats to define themes.

It seems that an earlier version of VSC primarily used the tmTheme definition format which allowed themes to be converted using this tool (also see a GitHub issue from 2017 ). However, since VSC is now using a new format to define its themes, I'm wondering whether there is a (simple) way to use existing VSC themes in the Monaco editor.

Thanks a lot for your help: :)

PS This GitHub issue comment from 2019 seems to indicate that there is indeed no easy way to do this but hopefully things have changed since then.

The tmTheme format is still supported in theme extensions and converted on import (from what I remember). The primary definition of a theme in an extension, however, is using the approach like shown in dark_plus.json.

It's pretty easy to convert the json format to the structure that's expected by monaco-editor:

export interface Colors { [key: string]: string }
export interface ITokenEntry {
    name?: string;
    scope: string[] | string;
    settings: {
        foreground?: string;
        background?: string;
        fontStyle?: string;
    };
}

// This is the structure of a vscode theme file.
export interface IThemeObject {
    name: string;
    type?: string;
    include?: string;
    colors?: Colors;

    settings?: ITokenEntry[];    // Old style specification.
    tokenColors?: ITokenEntry[]; // This is how it should be done now.
}

These interfaces describe the format of the json file. Older theme definitions use the settings member to define theme colors. In such a case simply set the tokenColors member to the settings member and proceed.

Once the theme has been loaded you can use this static method to load it into monaco-editor:

    /**
     * Updates the theme used by all code editor instances.
     *
     * @param theme The theme name.
     * @param type The base type of the theme.
     * @param values The actual theme values.
     */
    public static updateTheme(theme: string, type: "light" | "dark", values: IThemeObject): void {
        // Convert all color values to CSS hex form.
        const entries: { [key: string]: string } = {};
        for (const [key, value] of Object.entries(values.colors || {})) {
            entries[key] = colorToHex(value) || "";
        }

        const tokenRules: Monaco.ITokenThemeRule[] = [];
        (values.tokenColors || []).forEach((value: ITokenEntry): void => {
            const scopeValue = value.scope || [];
            const scopes = Array.isArray(scopeValue) ? scopeValue : scopeValue.split(",");
            scopes.forEach((scope: string): void => {
                tokenRules.push({
                    token: scope,
                    foreground: colorToHex(value.settings.foreground),
                    background: colorToHex(value.settings.background),
                    fontStyle: value.settings.fontStyle,
                });
            });
        });

        CodeEditor.currentThemeId = theme.replace(/[^a-zA-Z]+/g, "-");
        Monaco.defineTheme(CodeEditor.currentThemeId, {
            base: type === "light" ? "vs" : "vs-dark",
            inherit: true,
            rules: tokenRules,
            colors: entries,
        });

        Monaco.setTheme(CodeEditor.currentThemeId);
    }

CodeEditor is my TS class that wraps the monaco-editor. The function colorToHex is defined as:

import Color from "color";

/**
 * Converts a color string or a color to a hex string.
 *
 * @param color The value to convert.
 *
 * @returns A hex string of the given color, including the alpha value.
 */
export const colorToHex = (color: string | Color | undefined): string | undefined => {
    if (!color) {
        return;
    }

    if (typeof color === "string") {
        color = new Color(color);
    }

    // Hex color values have no alpha component, so we have to add that explicitly.
    if (color.alpha() < 1) {
        let alpha = Math.round((color.alpha() * 255)).toString(16);
        if (alpha.length < 2) {
            alpha = "0" + alpha;
        }

        return color.hex() + alpha;
    } else {
        return color.hex();
    }
};

I created a small, POC repo to demonstrate how this can work. It's based on the monaco-vscode-textmate-theme-converter plugin. You can find the repo here and I published a tool to actually convert a theme here . Note, due to some loading issues you might need to do a hard refresh of the demo app before starting.

I use monaco and I found this open source repo which conatins almost all the regualrly used VSC themes like katzenmitch and cloud, which you could use as a npm package. When I went through the code I found that he used JSON format like for cobalt theme it was this code:

{
  "base": "vs-dark",
  "inherit": true,
  "rules": [
    {
      "background": "002240",
      "token": ""
    },
    {
      "foreground": "e1efff",
      "token": "punctuation - (punctuation.definition.string || punctuation.definition.comment)"
    },
    {
      "foreground": "ff628c",
      "token": "constant"
    },
    {
      "foreground": "ffdd00",
      "token": "entity"
    },
    {
      "foreground": "ff9d00",
      "token": "keyword"
    },
    {
      "foreground": "ffee80",
      "token": "storage"
    },
    {
      "foreground": "3ad900",
      "token": "string -string.unquoted.old-plist -string.unquoted.heredoc"
    },
    {
      "foreground": "3ad900",
      "token": "string.unquoted.heredoc string"
    },
    {
      "foreground": "0088ff",
      "fontStyle": "italic",
      "token": "comment"
    },
    {
      "foreground": "80ffbb",
      "token": "support"
    },
    {
      "foreground": "cccccc",
      "token": "variable"
    },
    {
      "foreground": "ff80e1",
      "token": "variable.language"
    },
    {
      "foreground": "ffee80",
      "token": "meta.function-call"
    },
    {
      "foreground": "f8f8f8",
      "background": "800f00",
      "token": "invalid"
    },
    {
      "foreground": "ffffff",
      "background": "223545",
      "token": "text source"
    },
    {
      "foreground": "ffffff",
      "background": "223545",
      "token": "string.unquoted.heredoc"
    },
    {
      "foreground": "ffffff",
      "background": "223545",
      "token": "source source"
    },
    {
      "foreground": "80fcff",
      "fontStyle": "italic",
      "token": "entity.other.inherited-class"
    },
    {
      "foreground": "9eff80",
      "token": "string.quoted source"
    },
    {
      "foreground": "80ff82",
      "token": "string constant"
    },
    {
      "foreground": "80ffc2",
      "token": "string.regexp"
    },
    {
      "foreground": "edef7d",
      "token": "string variable"
    },
    {
      "foreground": "ffb054",
      "token": "support.function"
    },
    {
      "foreground": "eb939a",
      "token": "support.constant"
    },
    {
      "foreground": "ff1e00",
      "token": "support.type.exception"
    },
    {
      "foreground": "8996a8",
      "token": "meta.preprocessor.c"
    },
    {
      "foreground": "afc4db",
      "token": "meta.preprocessor.c keyword"
    },
    {
      "foreground": "73817d",
      "token": "meta.sgml.html meta.doctype"
    },
    {
      "foreground": "73817d",
      "token": "meta.sgml.html meta.doctype entity"
    },
    {
      "foreground": "73817d",
      "token": "meta.sgml.html meta.doctype string"
    },
    {
      "foreground": "73817d",
      "token": "meta.xml-processing"
    },
    {
      "foreground": "73817d",
      "token": "meta.xml-processing entity"
    },
    {
      "foreground": "73817d",
      "token": "meta.xml-processing string"
    },
    {
      "foreground": "9effff",
      "token": "meta.tag"
    },
    {
      "foreground": "9effff",
      "token": "meta.tag entity"
    },
    {
      "foreground": "9effff",
      "token": "meta.selector.css entity.name.tag"
    },
    {
      "foreground": "ffb454",
      "token": "meta.selector.css entity.other.attribute-name.id"
    },
    {
      "foreground": "5fe461",
      "token": "meta.selector.css entity.other.attribute-name.class"
    },
    {
      "foreground": "9df39f",
      "token": "support.type.property-name.css"
    },
    {
      "foreground": "f6f080",
      "token": "meta.property-group support.constant.property-value.css"
    },
    {
      "foreground": "f6f080",
      "token": "meta.property-value support.constant.property-value.css"
    },
    {
      "foreground": "f6aa11",
      "token": "meta.preprocessor.at-rule keyword.control.at-rule"
    },
    {
      "foreground": "edf080",
      "token": "meta.property-value support.constant.named-color.css"
    },
    {
      "foreground": "edf080",
      "token": "meta.property-value constant"
    },
    {
      "foreground": "eb939a",
      "token": "meta.constructor.argument.css"
    },
    {
      "foreground": "f8f8f8",
      "background": "000e1a",
      "token": "meta.diff"
    },
    {
      "foreground": "f8f8f8",
      "background": "000e1a",
      "token": "meta.diff.header"
    },
    {
      "foreground": "f8f8f8",
      "background": "4c0900",
      "token": "markup.deleted"
    },
    {
      "foreground": "f8f8f8",
      "background": "806f00",
      "token": "markup.changed"
    },
    {
      "foreground": "f8f8f8",
      "background": "154f00",
      "token": "markup.inserted"
    },
    {
      "background": "8fddf630",
      "token": "markup.raw"
    },
    {
      "background": "004480",
      "token": "markup.quote"
    },
    {
      "background": "130d26",
      "token": "markup.list"
    },
    {
      "foreground": "c1afff",
      "fontStyle": "bold",
      "token": "markup.bold"
    },
    {
      "foreground": "b8ffd9",
      "fontStyle": "italic",
      "token": "markup.italic"
    },
    {
      "foreground": "c8e4fd",
      "background": "001221",
      "fontStyle": "bold",
      "token": "markup.heading"
    }
  ],
  "colors": {
    "editor.foreground": "#FFFFFF",
    "editor.background": "#002240",
    "editor.selectionBackground": "#B36539BF",
    "editor.lineHighlightBackground": "#00000059",
    "editorCursor.foreground": "#FFFFFF",
    "editorWhitespace.foreground": "#FFFFFF26"
  }
}

I suggest you could go through it. and make your own theme instead of using default VSC themes but if you still want to use vsc themes there are many repos like this one. Or you could use tools like https://bitwiser.in/monaco-themes/ to generate theme for monaco from tmTheme files that picks colors from theme files and generates a json to be used directly with monaco.editor.defineTheme .

I hope this helped.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM