Vectrum
Sign In
57 lines · 2.8 KB · TypeScript
import { createTheme } from "@uiw/codemirror-themes";
import { tags as t } from "@lezer/highlight";

/**
 * The editor in MetalUI's code tints (`--mu-syn-*`, the code card's keyword / type / member / string /
 * number / comment / punct / line), the same as the file viewer's Shiki theme (code-view.tsx). The
 * variables follow the colorway; the light/dark split only sets CodeMirror's own base styles.
 */
const syn = (token: string) => `var(--mu-syn-${token})`;

function metalTheme(mode: "light" | "dark") {
  return createTheme({
    theme: mode,
    settings: {
      // The editor sits on the file plate; the gutter is part of it, set off by the line numbers' ink.
      background: "transparent",
      foreground: syn("text"),
      caret: "var(--mu-green-deep)",
      // Green marks intent: the selection and its matches are faint phosphor washes.
      selection: "color-mix(in srgb, var(--mu-green) 28%, transparent)",
      selectionMatch: "color-mix(in srgb, var(--mu-green) 14%, transparent)",
      lineHighlight: "var(--mu-menu-row-hover)",
      gutterBackground: "transparent",
      gutterForeground: syn("line"),
      gutterActiveForeground: "var(--mu-ink2)",
      gutterBorder: "transparent",
      fontFamily: "var(--mu-mono)",
    },
    styles: [
      { tag: [t.comment, t.lineComment, t.blockComment, t.docComment, t.quote], color: syn("comment") },
      {
        tag: [t.keyword, t.modifier, t.operatorKeyword, t.controlKeyword, t.definitionKeyword, t.moduleKeyword, t.self, t.tagName],
        color: syn("keyword"),
      },
      { tag: [t.typeName, t.className, t.namespace, t.standard(t.typeName)], color: syn("type") },
      {
        tag: [t.function(t.variableName), t.function(t.propertyName), t.propertyName, t.attributeName, t.labelName],
        color: syn("member"),
      },
      { tag: [t.string, t.special(t.string), t.regexp, t.character, t.monospace], color: syn("string") },
      { tag: [t.number, t.integer, t.float, t.bool, t.null, t.atom, t.unit, t.constant(t.name), t.escape], color: syn("number") },
      { tag: [t.punctuation, t.bracket, t.operator, t.separator, t.angleBracket, t.derefOperator], color: syn("punct") },
      { tag: [t.variableName, t.name], color: syn("text") },
      { tag: [t.heading, t.heading1, t.heading2, t.heading3], color: syn("keyword"), fontWeight: "600" },
      { tag: t.emphasis, fontStyle: "italic" },
      { tag: t.strong, fontWeight: "600" },
      { tag: [t.link, t.url], color: syn("member"), textDecoration: "underline" },
      { tag: t.strikethrough, textDecoration: "line-through" },
      { tag: [t.inserted], color: syn("string") },
      { tag: [t.deleted, t.invalid], color: "var(--mu-red)" },
      { tag: t.meta, color: syn("punct") },
    ],
  });
}

export const vectrumLight = metalTheme("light");
export const vectrumDark = metalTheme("dark");