Skip to content

主题 API

概述

Trae 主题 API 提供了强大的主题系统,允许开发者创建、管理和应用自定义主题。通过主题 API,可以定义颜色方案、图标主题、语法高亮等视觉元素。

核心概念

主题类型

typescript
enum ColorThemeKind {
  Light = 1,
  Dark = 2,
  HighContrast = 3,
  HighContrastLight = 4
}

interface ColorTheme {
  readonly kind: ColorThemeKind;
}

interface IconTheme {
  readonly id: string;
}

interface ProductIconTheme {
  readonly id: string;
}

颜色定义

typescript
class ThemeColor {
  readonly id: string;
  
  constructor(id: string);
  
  static readonly foreground: ThemeColor;
  static readonly errorForeground: ThemeColor;
  static readonly descriptionForeground: ThemeColor;
  static readonly iconForeground: ThemeColor;
  static readonly focusBorder: ThemeColor;
  static readonly contrastBorder: ThemeColor;
  static readonly contrastActiveBorder: ThemeColor;
  static readonly selection: ThemeColor;
  static readonly textSeparatorForeground: ThemeColor;
  static readonly textLinkForeground: ThemeColor;
  static readonly textLinkActiveForeground: ThemeColor;
  static readonly textPreformatForeground: ThemeColor;
  static readonly textBlockQuoteBackground: ThemeColor;
  static readonly textBlockQuoteBorder: ThemeColor;
  static readonly textCodeBlockBackground: ThemeColor;
}

class ThemeIcon {
  readonly id: string;
  readonly color?: ThemeColor;
  
  constructor(id: string, color?: ThemeColor);
  
  static readonly file: ThemeIcon;
  static readonly folder: ThemeIcon;
  static readonly folderOpened: ThemeIcon;
  static readonly rootFolder: ThemeIcon;
  static readonly rootFolderOpened: ThemeIcon;
  static readonly folderExpanded: ThemeIcon;
  static readonly folderCollapsed: ThemeIcon;
}

API 参考

主题管理

基础主题操作

typescript
import { window, workspace, ColorThemeKind, ThemeColor, ThemeIcon } from '@trae/api';

// 获取当前颜色主题
const getCurrentColorTheme = (): ColorTheme => {
  return window.activeColorTheme;
};

// 监听主题变化
const onDidChangeActiveColorTheme = (
  listener: (theme: ColorTheme) => void
): Disposable => {
  return window.onDidChangeActiveColorTheme(listener);
};

// 获取当前图标主题
const getCurrentIconTheme = (): IconTheme | undefined => {
  return window.activeIconTheme;
};

// 监听图标主题变化
const onDidChangeActiveIconTheme = (
  listener: (theme: IconTheme | undefined) => void
): Disposable => {
  return window.onDidChangeActiveIconTheme(listener);
};

// 检查主题类型
const isLightTheme = (theme: ColorTheme): boolean => {
  return theme.kind === ColorThemeKind.Light || 
         theme.kind === ColorThemeKind.HighContrastLight;
};

const isDarkTheme = (theme: ColorTheme): boolean => {
  return theme.kind === ColorThemeKind.Dark;
};

const isHighContrastTheme = (theme: ColorTheme): boolean => {
  return theme.kind === ColorThemeKind.HighContrast || 
         theme.kind === ColorThemeKind.HighContrastLight;
};

// 主题适配函数
const getThemeAdaptiveColor = (lightColor: string, darkColor: string): string => {
  const theme = window.activeColorTheme;
  return isLightTheme(theme) ? lightColor : darkColor;
};

const getThemeAdaptiveIcon = (lightIcon: string, darkIcon: string): string => {
  const theme = window.activeColorTheme;
  return isLightTheme(theme) ? lightIcon : darkIcon;
};

主题监听器

typescript
class ThemeManager {
  private disposables: Disposable[] = [];
  private themeChangeListeners: Set<(theme: ColorTheme) => void> = new Set();
  
  constructor() {
    this.setupThemeListeners();
  }
  
  private setupThemeListeners(): void {
    this.disposables.push(
      window.onDidChangeActiveColorTheme(theme => {
        this.onThemeChanged(theme);
      }),
      
      window.onDidChangeActiveIconTheme(theme => {
        this.onIconThemeChanged(theme);
      })
    );
  }
  
  private onThemeChanged(theme: ColorTheme): void {
    console.log(`Theme changed to: ${ColorThemeKind[theme.kind]}`);
    
    // 通知所有监听器
    this.themeChangeListeners.forEach(listener => {
      try {
        listener(theme);
      } catch (error) {
        console.error('Error in theme change listener:', error);
      }
    });
    
    // 更新主题相关的 UI 元素
    this.updateThemeBasedUI(theme);
  }
  
  private onIconThemeChanged(theme: IconTheme | undefined): void {
    console.log(`Icon theme changed to: ${theme?.id || 'none'}`);
    this.updateIconThemeBasedUI(theme);
  }
  
  private updateThemeBasedUI(theme: ColorTheme): void {
    // 更新状态栏项目颜色
    this.updateStatusBarColors(theme);
    
    // 更新 Webview 主题
    this.updateWebviewThemes(theme);
    
    // 更新装饰器颜色
    this.updateDecorationColors(theme);
  }
  
  private updateIconThemeBasedUI(theme: IconTheme | undefined): void {
    // 更新自定义图标
    this.updateCustomIcons(theme);
  }
  
  private updateStatusBarColors(theme: ColorTheme): void {
    // 根据主题更新状态栏项目的颜色
    const isDark = isDarkTheme(theme);
    const color = isDark ? '#ffffff' : '#000000';
    
    // 更新状态栏项目
    // 这里需要具体的状态栏项目引用
  }
  
  private updateWebviewThemes(theme: ColorTheme): void {
    // 向 Webview 发送主题更新消息
    const themeData = {
      kind: theme.kind,
      isDark: isDarkTheme(theme),
      isLight: isLightTheme(theme),
      isHighContrast: isHighContrastTheme(theme)
    };
    
    // 发送到所有 Webview
    // webviewPanel.webview.postMessage({ type: 'themeChanged', theme: themeData });
  }
  
  private updateDecorationColors(theme: ColorTheme): void {
    // 更新文本装饰器的颜色
    const decorationType = window.createTextEditorDecorationType({
      backgroundColor: isLightTheme(theme) ? '#ffeb3b33' : '#ffeb3b22',
      border: `1px solid ${isLightTheme(theme) ? '#ffeb3b' : '#ffeb3b88'}`
    });
    
    // 应用装饰器
    // editor.setDecorations(decorationType, ranges);
  }
  
  private updateCustomIcons(theme: IconTheme | undefined): void {
    // 根据图标主题更新自定义图标
    if (theme) {
      console.log(`Updating icons for theme: ${theme.id}`);
    }
  }
  
  // 公共方法
  addThemeChangeListener(listener: (theme: ColorTheme) => void): Disposable {
    this.themeChangeListeners.add(listener);
    
    return {
      dispose: () => {
        this.themeChangeListeners.delete(listener);
      }
    };
  }
  
  getCurrentThemeInfo(): ThemeInfo {
    const colorTheme = window.activeColorTheme;
    const iconTheme = window.activeIconTheme;
    
    return {
      colorTheme: {
        kind: colorTheme.kind,
        kindName: ColorThemeKind[colorTheme.kind],
        isDark: isDarkTheme(colorTheme),
        isLight: isLightTheme(colorTheme),
        isHighContrast: isHighContrastTheme(colorTheme)
      },
      iconTheme: iconTheme ? {
        id: iconTheme.id
      } : undefined
    };
  }
  
  dispose(): void {
    this.disposables.forEach(d => d.dispose());
    this.themeChangeListeners.clear();
  }
}

interface ThemeInfo {
  colorTheme: {
    kind: ColorThemeKind;
    kindName: string;
    isDark: boolean;
    isLight: boolean;
    isHighContrast: boolean;
  };
  iconTheme?: {
    id: string;
  };
}

颜色主题定义

主题贡献点

typescript
// package.json 中的主题定义
const themeContribution = {
  "contributes": {
    "themes": [
      {
        "label": "My Dark Theme",
        "uiTheme": "vs-dark",
        "path": "./themes/dark-theme.json"
      },
      {
        "label": "My Light Theme",
        "uiTheme": "vs",
        "path": "./themes/light-theme.json"
      },
      {
        "label": "My High Contrast Theme",
        "uiTheme": "hc-black",
        "path": "./themes/high-contrast-theme.json"
      }
    ],
    "iconThemes": [
      {
        "id": "my-icon-theme",
        "label": "My Icon Theme",
        "path": "./themes/icon-theme.json"
      }
    ],
    "productIconThemes": [
      {
        "id": "my-product-icon-theme",
        "label": "My Product Icon Theme",
        "path": "./themes/product-icon-theme.json"
      }
    ]
  }
};

颜色主题文件结构

typescript
// dark-theme.json
const darkTheme = {
  "name": "My Dark Theme",
  "type": "dark",
  "colors": {
    // 编辑器颜色
    "editor.background": "#1e1e1e",
    "editor.foreground": "#d4d4d4",
    "editor.selectionBackground": "#264f78",
    "editor.lineHighlightBackground": "#2a2d2e",
    "editor.cursor": "#ffffff",
    
    // 侧边栏颜色
    "sideBar.background": "#252526",
    "sideBar.foreground": "#cccccc",
    "sideBar.border": "#2b2b2b",
    
    // 活动栏颜色
    "activityBar.background": "#2c2c2c",
    "activityBar.foreground": "#ffffff",
    "activityBar.border": "#2b2b2b",
    
    // 状态栏颜色
    "statusBar.background": "#007acc",
    "statusBar.foreground": "#ffffff",
    "statusBar.noFolderBackground": "#68217a",
    
    // 标题栏颜色
    "titleBar.activeBackground": "#3c3c3c",
    "titleBar.activeForeground": "#cccccc",
    "titleBar.inactiveBackground": "#3c3c3c",
    "titleBar.inactiveForeground": "#cccccc99",
    
    // 面板颜色
    "panel.background": "#1e1e1e",
    "panel.border": "#2b2b2b",
    "panelTitle.activeForeground": "#ffffff",
    "panelTitle.inactiveForeground": "#cccccc99",
    
    // 终端颜色
    "terminal.background": "#1e1e1e",
    "terminal.foreground": "#d4d4d4",
    "terminal.ansiBlack": "#000000",
    "terminal.ansiRed": "#cd3131",
    "terminal.ansiGreen": "#0dbc79",
    "terminal.ansiYellow": "#e5e510",
    "terminal.ansiBlue": "#2472c8",
    "terminal.ansiMagenta": "#bc3fbc",
    "terminal.ansiCyan": "#11a8cd",
    "terminal.ansiWhite": "#e5e5e5",
    
    // 按钮颜色
    "button.background": "#0e639c",
    "button.foreground": "#ffffff",
    "button.hoverBackground": "#1177bb",
    
    // 输入框颜色
    "input.background": "#3c3c3c",
    "input.foreground": "#cccccc",
    "input.border": "#3c3c3c",
    "input.placeholderForeground": "#cccccc99",
    
    // 下拉框颜色
    "dropdown.background": "#3c3c3c",
    "dropdown.foreground": "#cccccc",
    "dropdown.border": "#3c3c3c",
    
    // 列表颜色
    "list.activeSelectionBackground": "#094771",
    "list.activeSelectionForeground": "#ffffff",
    "list.inactiveSelectionBackground": "#37373d",
    "list.inactiveSelectionForeground": "#cccccc",
    "list.hoverBackground": "#2a2d2e",
    "list.hoverForeground": "#cccccc",
    
    // 树视图颜色
    "tree.indentGuidesStroke": "#585858",
    
    // 标签页颜色
    "tab.activeBackground": "#1e1e1e",
    "tab.activeForeground": "#ffffff",
    "tab.inactiveBackground": "#2d2d30",
    "tab.inactiveForeground": "#cccccc99",
    "tab.border": "#2b2b2b",
    
    // 编辑器组颜色
    "editorGroup.border": "#2b2b2b",
    "editorGroupHeader.tabsBackground": "#252526",
    
    // 滚动条颜色
    "scrollbar.shadow": "#000000",
    "scrollbarSlider.background": "#79797966",
    "scrollbarSlider.hoverBackground": "#646464b3",
    "scrollbarSlider.activeBackground": "#bfbfbf66",
    
    // 徽章颜色
    "badge.background": "#007acc",
    "badge.foreground": "#ffffff",
    
    // 进度条颜色
    "progressBar.background": "#0e70c0",
    
    // 编辑器小部件颜色
    "editorWidget.background": "#252526",
    "editorWidget.border": "#454545",
    "editorWidget.resizeBorder": "#007acc",
    
    // 查找小部件颜色
    "editorFind.currentMatchHighlight": "#515c6a",
    "editorFind.otherMatchesHighlight": "#515c6a",
    "editorFind.rangeHighlight": "#3a3d41",
    
    // 悬停小部件颜色
    "editorHoverWidget.background": "#252526",
    "editorHoverWidget.border": "#454545",
    
    // 建议小部件颜色
    "editorSuggestWidget.background": "#252526",
    "editorSuggestWidget.border": "#454545",
    "editorSuggestWidget.selectedBackground": "#094771",
    
    // 错误和警告颜色
    "editorError.foreground": "#f14c4c",
    "editorWarning.foreground": "#ffb454",
    "editorInfo.foreground": "#3794ff",
    "editorHint.foreground": "#eeeeeeb3"
  },
  "tokenColors": [
    {
      "name": "Comment",
      "scope": [
        "comment",
        "punctuation.definition.comment"
      ],
      "settings": {
        "fontStyle": "italic",
        "foreground": "#6A9955"
      }
    },
    {
      "name": "Variables",
      "scope": [
        "variable",
        "string constant.other.placeholder"
      ],
      "settings": {
        "foreground": "#9CDCFE"
      }
    },
    {
      "name": "Colors",
      "scope": [
        "constant.other.color"
      ],
      "settings": {
        "foreground": "#ffffff"
      }
    },
    {
      "name": "Invalid",
      "scope": [
        "invalid",
        "invalid.illegal"
      ],
      "settings": {
        "foreground": "#f44747"
      }
    },
    {
      "name": "Keyword, Storage",
      "scope": [
        "keyword",
        "storage.type",
        "storage.modifier"
      ],
      "settings": {
        "foreground": "#569CD6"
      }
    },
    {
      "name": "Operator, Misc",
      "scope": [
        "keyword.control",
        "constant.other.color",
        "punctuation",
        "meta.tag",
        "punctuation.definition.tag",
        "punctuation.separator.inheritance.php",
        "punctuation.definition.tag.html",
        "punctuation.definition.tag.begin.html",
        "punctuation.definition.tag.end.html",
        "punctuation.section.embedded",
        "keyword.other.template",
        "keyword.other.substitution"
      ],
      "settings": {
        "foreground": "#D4D4D4"
      }
    },
    {
      "name": "Tag",
      "scope": [
        "entity.name.tag",
        "meta.tag.sgml",
        "markup.deleted.git_gutter"
      ],
      "settings": {
        "foreground": "#569CD6"
      }
    },
    {
      "name": "Function, Special Method",
      "scope": [
        "entity.name.function",
        "meta.function-call",
        "variable.function",
        "support.function",
        "keyword.other.special-method"
      ],
      "settings": {
        "foreground": "#DCDCAA"
      }
    },
    {
      "name": "Block Level Variables",
      "scope": [
        "meta.block variable.other"
      ],
      "settings": {
        "foreground": "#9CDCFE"
      }
    },
    {
      "name": "Other Variable, String Link",
      "scope": [
        "support.other.variable",
        "string.other.link"
      ],
      "settings": {
        "foreground": "#9CDCFE"
      }
    },
    {
      "name": "Number, Constant, Function Argument, Tag Attribute, Embedded",
      "scope": [
        "constant.numeric",
        "constant.language",
        "support.constant",
        "constant.character",
        "constant.escape",
        "variable.parameter",
        "keyword.other.unit",
        "keyword.other"
      ],
      "settings": {
        "foreground": "#B5CEA8"
      }
    },
    {
      "name": "String, Symbols, Inherited Class, Markup Heading",
      "scope": [
        "string",
        "constant.other.symbol",
        "constant.other.key",
        "entity.other.inherited-class",
        "markup.heading",
        "markup.inserted.git_gutter",
        "meta.group.braces.curly constant.other.object.key.js string.unquoted.label.js"
      ],
      "settings": {
        "foreground": "#CE9178"
      }
    },
    {
      "name": "Class, Support",
      "scope": [
        "entity.name",
        "support.type",
        "support.class",
        "support.other.namespace.use.php",
        "meta.use.php",
        "support.other.namespace.php",
        "markup.changed.git_gutter",
        "support.type.sys-types"
      ],
      "settings": {
        "foreground": "#4EC9B0"
      }
    },
    {
      "name": "Entity Types",
      "scope": [
        "support.type"
      ],
      "settings": {
        "foreground": "#4EC9B0"
      }
    },
    {
      "name": "CSS Class and Support",
      "scope": [
        "source.css support.type.property-name",
        "source.sass support.type.property-name",
        "source.scss support.type.property-name",
        "source.less support.type.property-name",
        "source.stylus support.type.property-name",
        "source.postcss support.type.property-name"
      ],
      "settings": {
        "foreground": "#9CDCFE"
      }
    },
    {
      "name": "Sub-methods",
      "scope": [
        "entity.name.module.js",
        "variable.import.parameter.js",
        "variable.other.class.js"
      ],
      "settings": {
        "foreground": "#FF8C00"
      }
    },
    {
      "name": "Language methods",
      "scope": [
        "variable.language"
      ],
      "settings": {
        "fontStyle": "italic",
        "foreground": "#569CD6"
      }
    },
    {
      "name": "entity.name.method.js",
      "scope": [
        "entity.name.method.js"
      ],
      "settings": {
        "fontStyle": "italic",
        "foreground": "#DCDCAA"
      }
    },
    {
      "name": "meta.method.js",
      "scope": [
        "meta.class-method.js entity.name.function.js",
        "variable.function.constructor"
      ],
      "settings": {
        "foreground": "#DCDCAA"
      }
    },
    {
      "name": "Attributes",
      "scope": [
        "entity.other.attribute-name"
      ],
      "settings": {
        "foreground": "#9CDCFE"
      }
    },
    {
      "name": "HTML Attributes",
      "scope": [
        "text.html.basic entity.other.attribute-name.html",
        "text.html.basic entity.other.attribute-name"
      ],
      "settings": {
        "fontStyle": "italic",
        "foreground": "#9CDCFE"
      }
    },
    {
      "name": "CSS Classes",
      "scope": [
        "entity.other.attribute-name.class"
      ],
      "settings": {
        "foreground": "#FFCB6B"
      }
    },
    {
      "name": "CSS ID's",
      "scope": [
        "source.sass keyword.control"
      ],
      "settings": {
        "foreground": "#569CD6"
      }
    },
    {
      "name": "Inserted",
      "scope": [
        "markup.inserted"
      ],
      "settings": {
        "foreground": "#B5CEA8"
      }
    },
    {
      "name": "Deleted",
      "scope": [
        "markup.deleted"
      ],
      "settings": {
        "foreground": "#CE9178"
      }
    },
    {
      "name": "Changed",
      "scope": [
        "markup.changed"
      ],
      "settings": {
        "foreground": "#569CD6"
      }
    },
    {
      "name": "Regular Expressions",
      "scope": [
        "string.regexp"
      ],
      "settings": {
        "foreground": "#D16969"
      }
    },
    {
      "name": "Escape Characters",
      "scope": [
        "constant.character.escape"
      ],
      "settings": {
        "foreground": "#D7BA7D"
      }
    },
    {
      "name": "URL",
      "scope": [
        "*url*",
        "*link*",
        "*uri*"
      ],
      "settings": {
        "fontStyle": "underline"
      }
    },
    {
      "name": "Decorators",
      "scope": [
        "tag.decorator.js entity.name.tag.js",
        "tag.decorator.js punctuation.definition.tag.js"
      ],
      "settings": {
        "fontStyle": "italic",
        "foreground": "#569CD6"
      }
    },
    {
      "name": "ES7 Bind Operator",
      "scope": [
        "source.js constant.other.object.key.js string.unquoted.label.js"
      ],
      "settings": {
        "fontStyle": "italic",
        "foreground": "#FF8C00"
      }
    },
    {
      "name": "JSON Key - Level 0",
      "scope": [
        "source.json meta.structure.dictionary.json support.type.property-name.json"
      ],
      "settings": {
        "foreground": "#9CDCFE"
      }
    },
    {
      "name": "JSON Key - Level 1",
      "scope": [
        "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json support.type.property-name.json"
      ],
      "settings": {
        "foreground": "#FFCB6B"
      }
    },
    {
      "name": "JSON Key - Level 2",
      "scope": [
        "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json support.type.property-name.json"
      ],
      "settings": {
        "foreground": "#F78C6C"
      }
    },
    {
      "name": "JSON Key - Level 3",
      "scope": [
        "source.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json meta.structure.dictionary.value.json meta.structure.dictionary.json support.type.property-name.json"
      ],
      "settings": {
        "foreground": "#FF5370"
      }
    },
    {
      "name": "Markdown - Plain",
      "scope": [
        "text.html.markdown",
        "punctuation.definition.list_item.markdown"
      ],
      "settings": {
        "foreground": "#CCCCCC"
      }
    },
    {
      "name": "Markdown - Markup Raw Inline",
      "scope": [
        "text.html.markdown markup.inline.raw.markdown"
      ],
      "settings": {
        "foreground": "#569CD6"
      }
    },
    {
      "name": "Markdown - Markup Raw Inline Punctuation",
      "scope": [
        "text.html.markdown markup.inline.raw.markdown punctuation.definition.raw.markdown"
      ],
      "settings": {
        "foreground": "#65737E"
      }
    },
    {
      "name": "Markdown - Heading",
      "scope": [
        "markdown.heading",
        "markup.heading | markup.heading entity.name",
        "markup.heading.markdown punctuation.definition.heading.markdown"
      ],
      "settings": {
        "foreground": "#569CD6"
      }
    },
    {
      "name": "Markup - Italic",
      "scope": [
        "markup.italic"
      ],
      "settings": {
        "fontStyle": "italic",
        "foreground": "#F78C6C"
      }
    },
    {
      "name": "Markup - Bold",
      "scope": [
        "markup.bold",
        "markup.bold string"
      ],
      "settings": {
        "fontStyle": "bold",
        "foreground": "#F78C6C"
      }
    },
    {
      "name": "Markup - Bold-Italic",
      "scope": [
        "markup.bold markup.italic",
        "markup.italic markup.bold",
        "markup.quote markup.bold",
        "markup.bold markup.italic string",
        "markup.italic markup.bold string",
        "markup.quote markup.bold string"
      ],
      "settings": {
        "fontStyle": "bold italic",
        "foreground": "#F78C6C"
      }
    },
    {
      "name": "Markup - Underline",
      "scope": [
        "markup.underline"
      ],
      "settings": {
        "fontStyle": "underline",
        "foreground": "#F78C6C"
      }
    },
    {
      "name": "Markdown - Blockquote",
      "scope": [
        "markup.quote punctuation.definition.blockquote.markdown"
      ],
      "settings": {
        "foreground": "#65737E"
      }
    },
    {
      "name": "Markup - Quote",
      "scope": [
        "markup.quote"
      ],
      "settings": {
        "fontStyle": "italic"
      }
    },
    {
      "name": "Markdown - Link",
      "scope": [
        "string.other.link.title.markdown"
      ],
      "settings": {
        "foreground": "#DCDCAA"
      }
    },
    {
      "name": "Markdown - Link Description",
      "scope": [
        "string.other.link.description.title.markdown"
      ],
      "settings": {
        "foreground": "#569CD6"
      }
    },
    {
      "name": "Markdown - Link Anchor",
      "scope": [
        "constant.other.reference.link.markdown"
      ],
      "settings": {
        "foreground": "#FFCB6B"
      }
    },
    {
      "name": "Markup - Raw Block",
      "scope": [
        "markup.raw.block"
      ],
      "settings": {
        "foreground": "#569CD6"
      }
    },
    {
      "name": "Markdown - Raw Block Fenced",
      "scope": [
        "markup.raw.block.fenced.markdown"
      ],
      "settings": {
        "foreground": "#00000050"
      }
    },
    {
      "name": "Markdown - Fenced Bode Block",
      "scope": [
        "punctuation.definition.fenced.markdown"
      ],
      "settings": {
        "foreground": "#00000050"
      }
    },
    {
      "name": "Markdown - Fenced Bode Block Variable",
      "scope": [
        "markup.raw.block.fenced.markdown",
        "variable.language.fenced.markdown",
        "punctuation.section.class.end"
      ],
      "settings": {
        "foreground": "#CCCCCC"
      }
    },
    {
      "name": "Markdown - Fenced Language",
      "scope": [
        "variable.language.fenced.markdown"
      ],
      "settings": {
        "foreground": "#65737E"
      }
    },
    {
      "name": "Markdown - Separator",
      "scope": [
        "meta.separator"
      ],
      "settings": {
        "fontStyle": "bold",
        "foreground": "#65737E"
      }
    },
    {
      "name": "Markup - Table",
      "scope": [
        "markup.table"
      ],
      "settings": {
        "foreground": "#CCCCCC"
      }
    }
  ]
};

图标主题文件结构

typescript
// icon-theme.json
const iconTheme = {
  "iconDefinitions": {
    "_file": {
      "iconPath": "./icons/file.svg"
    },
    "_folder": {
      "iconPath": "./icons/folder.svg"
    },
    "_folderOpen": {
      "iconPath": "./icons/folder-open.svg"
    },
    "_js": {
      "iconPath": "./icons/javascript.svg"
    },
    "_ts": {
      "iconPath": "./icons/typescript.svg"
    },
    "_json": {
      "iconPath": "./icons/json.svg"
    },
    "_html": {
      "iconPath": "./icons/html.svg"
    },
    "_css": {
      "iconPath": "./icons/css.svg"
    },
    "_md": {
      "iconPath": "./icons/markdown.svg"
    },
    "_git": {
      "iconPath": "./icons/git.svg"
    },
    "_npm": {
      "iconPath": "./icons/npm.svg"
    },
    "_node_modules": {
      "iconPath": "./icons/node_modules.svg"
    }
  },
  "file": "_file",
  "folder": "_folder",
  "folderExpanded": "_folderOpen",
  "fileExtensions": {
    "js": "_js",
    "ts": "_ts",
    "json": "_json",
    "html": "_html",
    "htm": "_html",
    "css": "_css",
    "scss": "_css",
    "sass": "_css",
    "less": "_css",
    "md": "_md",
    "markdown": "_md"
  },
  "fileNames": {
    "package.json": "_npm",
    "package-lock.json": "_npm",
    "yarn.lock": "_npm",
    ".gitignore": "_git",
    ".gitattributes": "_git",
    "README.md": "_md",
    "CHANGELOG.md": "_md",
    "LICENSE": "_file"
  },
  "folderNames": {
    "node_modules": "_node_modules",
    ".git": "_git",
    "src": "_folder",
    "dist": "_folder",
    "build": "_folder",
    "public": "_folder",
    "assets": "_folder",
    "images": "_folder",
    "styles": "_folder",
    "components": "_folder",
    "pages": "_folder",
    "utils": "_folder",
    "lib": "_folder",
    "libs": "_folder",
    "test": "_folder",
    "tests": "_folder",
    "spec": "_folder",
    "docs": "_folder",
    "documentation": "_folder"
  },
  "folderNamesExpanded": {
    "node_modules": "_node_modules",
    ".git": "_git",
    "src": "_folderOpen",
    "dist": "_folderOpen",
    "build": "_folderOpen",
    "public": "_folderOpen",
    "assets": "_folderOpen",
    "images": "_folderOpen",
    "styles": "_folderOpen",
    "components": "_folderOpen",
    "pages": "_folderOpen",
    "utils": "_folderOpen",
    "lib": "_folderOpen",
    "libs": "_folderOpen",
    "test": "_folderOpen",
    "tests": "_folderOpen",
    "spec": "_folderOpen",
    "docs": "_folderOpen",
    "documentation": "_folderOpen"
  },
  "languageIds": {
    "javascript": "_js",
    "typescript": "_ts",
    "json": "_json",
    "jsonc": "_json",
    "html": "_html",
    "css": "_css",
    "scss": "_css",
    "sass": "_css",
    "less": "_css",
    "markdown": "_md"
  },
  "light": {
    "fileExtensions": {
      "js": "_js_light",
      "ts": "_ts_light"
    },
    "file": "_file_light",
    "folder": "_folder_light"
  },
  "highContrast": {
    "fileExtensions": {
      "js": "_js_hc",
      "ts": "_ts_hc"
    },
    "file": "_file_hc",
    "folder": "_folder_hc"
  }
};

主题工具类

主题颜色工具

typescript
class ThemeColorUtils {
  // 颜色转换工具
  static hexToRgb(hex: string): { r: number; g: number; b: number } | null {
    const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
      r: parseInt(result[1], 16),
      g: parseInt(result[2], 16),
      b: parseInt(result[3], 16)
    } : null;
  }
  
  static rgbToHex(r: number, g: number, b: number): string {
    return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
  }
  
  static hexToHsl(hex: string): { h: number; s: number; l: number } | null {
    const rgb = this.hexToRgb(hex);
    if (!rgb) return null;
    
    const { r, g, b } = rgb;
    const rNorm = r / 255;
    const gNorm = g / 255;
    const bNorm = b / 255;
    
    const max = Math.max(rNorm, gNorm, bNorm);
    const min = Math.min(rNorm, gNorm, bNorm);
    const diff = max - min;
    
    let h = 0;
    let s = 0;
    const l = (max + min) / 2;
    
    if (diff !== 0) {
      s = l > 0.5 ? diff / (2 - max - min) : diff / (max + min);
      
      switch (max) {
        case rNorm:
          h = (gNorm - bNorm) / diff + (gNorm < bNorm ? 6 : 0);
          break;
        case gNorm:
          h = (bNorm - rNorm) / diff + 2;
          break;
        case bNorm:
          h = (rNorm - gNorm) / diff + 4;
          break;
      }
      h /= 6;
    }
    
    return {
      h: Math.round(h * 360),
      s: Math.round(s * 100),
      l: Math.round(l * 100)
    };
  }
  
  // 颜色操作工具
  static lighten(hex: string, amount: number): string {
    const hsl = this.hexToHsl(hex);
    if (!hsl) return hex;
    
    hsl.l = Math.min(100, hsl.l + amount);
    return this.hslToHex(hsl.h, hsl.s, hsl.l);
  }
  
  static darken(hex: string, amount: number): string {
    const hsl = this.hexToHsl(hex);
    if (!hsl) return hex;
    
    hsl.l = Math.max(0, hsl.l - amount);
    return this.hslToHex(hsl.h, hsl.s, hsl.l);
  }
  
  static saturate(hex: string, amount: number): string {
    const hsl = this.hexToHsl(hex);
    if (!hsl) return hex;
    
    hsl.s = Math.min(100, hsl.s + amount);
    return this.hslToHex(hsl.h, hsl.s, hsl.l);
  }
  
  static desaturate(hex: string, amount: number): string {
    const hsl = this.hexToHsl(hex);
    if (!hsl) return hex;
    
    hsl.s = Math.max(0, hsl.s - amount);
    return this.hslToHex(hsl.h, hsl.s, hsl.l);
  }
  
  static adjustHue(hex: string, amount: number): string {
    const hsl = this.hexToHsl(hex);
    if (!hsl) return hex;
    
    hsl.h = (hsl.h + amount) % 360;
    if (hsl.h < 0) hsl.h += 360;
    
    return this.hslToHex(hsl.h, hsl.s, hsl.l);
  }
  
  static mix(color1: string, color2: string, weight = 50): string {
    const rgb1 = this.hexToRgb(color1);
    const rgb2 = this.hexToRgb(color2);
    
    if (!rgb1 || !rgb2) return color1;
    
    const w = weight / 100;
    const r = Math.round(rgb1.r * (1 - w) + rgb2.r * w);
    const g = Math.round(rgb1.g * (1 - w) + rgb2.g * w);
    const b = Math.round(rgb1.b * (1 - w) + rgb2.b * w);
    
    return this.rgbToHex(r, g, b);
  }
  
  static getContrastRatio(color1: string, color2: string): number {
    const rgb1 = this.hexToRgb(color1);
    const rgb2 = this.hexToRgb(color2);
    
    if (!rgb1 || !rgb2) return 1;
    
    const l1 = this.getLuminance(rgb1.r, rgb1.g, rgb1.b);
    const l2 = this.getLuminance(rgb2.r, rgb2.g, rgb2.b);
    
    const lighter = Math.max(l1, l2);
    const darker = Math.min(l1, l2);
    
    return (lighter + 0.05) / (darker + 0.05);
  }
  
  static isAccessible(foreground: string, background: string, level: 'AA' | 'AAA' = 'AA'): boolean {
    const ratio = this.getContrastRatio(foreground, background);
    return level === 'AA' ? ratio >= 4.5 : ratio >= 7;
  }
  
  private static hslToHex(h: number, s: number, l: number): string {
    const hNorm = h / 360;
    const sNorm = s / 100;
    const lNorm = l / 100;
    
    const c = (1 - Math.abs(2 * lNorm - 1)) * sNorm;
    const x = c * (1 - Math.abs((hNorm * 6) % 2 - 1));
    const m = lNorm - c / 2;
    
    let r = 0, g = 0, b = 0;
    
    if (0 <= hNorm && hNorm < 1/6) {
      r = c; g = x; b = 0;
    } else if (1/6 <= hNorm && hNorm < 2/6) {
      r = x; g = c; b = 0;
    } else if (2/6 <= hNorm && hNorm < 3/6) {
      r = 0; g = c; b = x;
    } else if (3/6 <= hNorm && hNorm < 4/6) {
      r = 0; g = x; b = c;
    } else if (4/6 <= hNorm && hNorm < 5/6) {
      r = x; g = 0; b = c;
    } else if (5/6 <= hNorm && hNorm < 1) {
      r = c; g = 0; b = x;
    }
    
    r = Math.round((r + m) * 255);
    g = Math.round((g + m) * 255);
    b = Math.round((b + m) * 255);
    
    return this.rgbToHex(r, g, b);
  }
  
  private static getLuminance(r: number, g: number, b: number): number {
    const [rNorm, gNorm, bNorm] = [r, g, b].map(c => {
      c = c / 255;
      return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
    });
    
    return 0.2126 * rNorm + 0.7152 * gNorm + 0.0722 * bNorm;
  }
  
  // 主题颜色生成器
  static generateColorPalette(baseColor: string): ColorPalette {
    return {
      primary: baseColor,
      primaryLight: this.lighten(baseColor, 20),
      primaryDark: this.darken(baseColor, 20),
      secondary: this.adjustHue(baseColor, 180),
      secondaryLight: this.lighten(this.adjustHue(baseColor, 180), 20),
      secondaryDark: this.darken(this.adjustHue(baseColor, 180), 20),
      accent: this.adjustHue(baseColor, 60),
      accentLight: this.lighten(this.adjustHue(baseColor, 60), 20),
      accentDark: this.darken(this.adjustHue(baseColor, 60), 20),
      neutral: this.desaturate(baseColor, 80),
      neutralLight: this.lighten(this.desaturate(baseColor, 80), 40),
      neutralDark: this.darken(this.desaturate(baseColor, 80), 40)
    };
  }
  
  static generateThemeColors(palette: ColorPalette, isDark = false): ThemeColors {
    const base = isDark ? {
      background: '#1e1e1e',
      foreground: '#d4d4d4',
      surface: '#252526',
      border: '#2b2b2b'
    } : {
      background: '#ffffff',
      foreground: '#333333',
      surface: '#f8f8f8',
      border: '#e0e0e0'
    };
    
    return {
      ...base,
      primary: palette.primary,
      primaryHover: isDark ? palette.primaryLight : palette.primaryDark,
      secondary: palette.secondary,
      secondaryHover: isDark ? palette.secondaryLight : palette.secondaryDark,
      accent: palette.accent,
      accentHover: isDark ? palette.accentLight : palette.accentDark,
      success: isDark ? '#4caf50' : '#2e7d32',
      warning: isDark ? '#ff9800' : '#f57c00',
      error: isDark ? '#f44336' : '#c62828',
      info: isDark ? '#2196f3' : '#1565c0'
    };
  }
}

interface ColorPalette {
  primary: string;
  primaryLight: string;
  primaryDark: string;
  secondary: string;
  secondaryLight: string;
  secondaryDark: string;
  accent: string;
  accentLight: string;
  accentDark: string;
  neutral: string;
  neutralLight: string;
  neutralDark: string;
}

interface ThemeColors {
  background: string;
  foreground: string;
  surface: string;
  border: string;
  primary: string;
  primaryHover: string;
  secondary: string;
  secondaryHover: string;
  accent: string;
  accentHover: string;
  success: string;
  warning: string;
  error: string;
  info: string;
}

动态主题切换

主题切换器

typescript
class ThemeSwitcher {
  private currentTheme: ColorTheme;
  private themePresets: Map<string, ThemePreset> = new Map();
  private disposables: Disposable[] = [];
  
  constructor() {
    this.currentTheme = window.activeColorTheme;
    this.setupThemePresets();
    this.setupEventListeners();
  }
  
  private setupThemePresets(): void {
    this.themePresets.set('auto', {
      name: 'Auto (System)',
      description: 'Follow system theme',
      apply: () => this.applySystemTheme()
    });
    
    this.themePresets.set('light', {
      name: 'Light Theme',
      description: 'Light color scheme',
      apply: () => this.applyTheme('Default Light+')
    });
    
    this.themePresets.set('dark', {
      name: 'Dark Theme',
      description: 'Dark color scheme',
      apply: () => this.applyTheme('Default Dark+')
    });
    
    this.themePresets.set('highContrast', {
      name: 'High Contrast',
      description: 'High contrast theme',
      apply: () => this.applyTheme('Default High Contrast')
    });
  }
  
  private setupEventListeners(): void {
    this.disposables.push(
      window.onDidChangeActiveColorTheme(theme => {
        this.currentTheme = theme;
        this.onThemeChanged(theme);
      })
    );
  }
  
  private onThemeChanged(theme: ColorTheme): void {
    // 保存主题偏好
    this.saveThemePreference(theme);
    
    // 通知主题变化
    this.notifyThemeChange(theme);
  }
  
  private async applyTheme(themeId: string): Promise<void> {
    try {
      await commands.executeCommand('workbench.action.selectTheme', themeId);
    } catch (error) {
      console.error(`Failed to apply theme: ${themeId}`, error);
      window.showErrorMessage(`Failed to apply theme: ${themeId}`);
    }
  }
  
  private async applySystemTheme(): Promise<void> {
    // 检测系统主题
    const isSystemDark = await this.detectSystemTheme();
    const themeId = isSystemDark ? 'Default Dark+' : 'Default Light+';
    await this.applyTheme(themeId);
  }
  
  private async detectSystemTheme(): Promise<boolean> {
    // 这里需要实现系统主题检测逻辑
    // 在实际实现中,可能需要使用平台特定的 API
    return window.activeColorTheme.kind === ColorThemeKind.Dark;
  }
  
  private saveThemePreference(theme: ColorTheme): void {
    const config = workspace.getConfiguration('workbench');
    const themeKind = ColorThemeKind[theme.kind].toLowerCase();
    
    // 保存到用户设置
    config.update('preferredTheme', themeKind, ConfigurationTarget.Global);
  }
  
  private notifyThemeChange(theme: ColorTheme): void {
    const themeInfo = {
      kind: theme.kind,
      kindName: ColorThemeKind[theme.kind],
      isDark: isDarkTheme(theme),
      isLight: isLightTheme(theme),
      isHighContrast: isHighContrastTheme(theme)
    };
    
    // 发送主题变化事件
    this.emitThemeChangeEvent(themeInfo);
  }
  
  private emitThemeChangeEvent(themeInfo: any): void {
    // 这里可以实现自定义事件发射逻辑
    console.log('Theme changed:', themeInfo);
  }
  
  // 公共方法
  async switchToTheme(presetName: string): Promise<void> {
    const preset = this.themePresets.get(presetName);
    if (!preset) {
      throw new Error(`Theme preset not found: ${presetName}`);
    }
    
    await preset.apply();
  }
  
  getAvailableThemes(): ThemePreset[] {
    return Array.from(this.themePresets.values());
  }
  
  getCurrentTheme(): ColorTheme {
    return this.currentTheme;
  }
  
  isCurrentTheme(kind: ColorThemeKind): boolean {
    return this.currentTheme.kind === kind;
  }
  
  async toggleTheme(): Promise<void> {
    const isDark = isDarkTheme(this.currentTheme);
    const targetPreset = isDark ? 'light' : 'dark';
    await this.switchToTheme(targetPreset);
  }
  
  dispose(): void {
    this.disposables.forEach(d => d.dispose());
    this.themePresets.clear();
  }
}

interface ThemePreset {
  name: string;
  description: string;
  apply: () => Promise<void>;
}

Webview 主题集成

Webview 主题支持

typescript
class WebviewThemeProvider {
  private webviewPanels: Set<WebviewPanel> = new Set();
  private disposables: Disposable[] = [];
  
  constructor() {
    this.setupThemeListener();
  }
  
  private setupThemeListener(): void {
    this.disposables.push(
      window.onDidChangeActiveColorTheme(theme => {
        this.updateWebviewThemes(theme);
      })
    );
  }
  
  registerWebviewPanel(panel: WebviewPanel): void {
    this.webviewPanels.add(panel);
    
    // 发送当前主题
    this.sendThemeToWebview(panel, window.activeColorTheme);
    
    // 清理
    panel.onDidDispose(() => {
      this.webviewPanels.delete(panel);
    });
  }
  
  private updateWebviewThemes(theme: ColorTheme): void {
    this.webviewPanels.forEach(panel => {
      this.sendThemeToWebview(panel, theme);
    });
  }
  
  private sendThemeToWebview(panel: WebviewPanel, theme: ColorTheme): void {
    const themeData = this.getWebviewThemeData(theme);
    
    panel.webview.postMessage({
      type: 'themeChanged',
      theme: themeData
    });
  }
  
  private getWebviewThemeData(theme: ColorTheme): WebviewThemeData {
    const isDark = isDarkTheme(theme);
    const isLight = isLightTheme(theme);
    const isHighContrast = isHighContrastTheme(theme);
    
    return {
      kind: theme.kind,
      kindName: ColorThemeKind[theme.kind],
      isDark,
      isLight,
      isHighContrast,
      colors: this.getThemeColors(theme),
      cssVariables: this.generateCSSVariables(theme)
    };
  }
  
  private getThemeColors(theme: ColorTheme): Record<string, string> {
    // 这里需要从当前主题中提取颜色
    // 实际实现中可能需要访问主题文件或使用 API
    const isDark = isDarkTheme(theme);
    
    return {
      background: isDark ? '#1e1e1e' : '#ffffff',
      foreground: isDark ? '#d4d4d4' : '#333333',
      primary: isDark ? '#007acc' : '#0066cc',
      secondary: isDark ? '#68217a' : '#5a1a6b',
      success: isDark ? '#4caf50' : '#2e7d32',
      warning: isDark ? '#ff9800' : '#f57c00',
      error: isDark ? '#f44336' : '#c62828',
      info: isDark ? '#2196f3' : '#1565c0',
      border: isDark ? '#2b2b2b' : '#e0e0e0',
      surface: isDark ? '#252526' : '#f8f8f8'
    };
  }
  
  private generateCSSVariables(theme: ColorTheme): string {
    const colors = this.getThemeColors(theme);
    
    return Object.entries(colors)
      .map(([key, value]) => `--trae-${key}: ${value};`)
      .join('\n');
  }
  
  getWebviewHTML(content: string): string {
    const theme = window.activeColorTheme;
    const themeData = this.getWebviewThemeData(theme);
    
    return `
      <!DOCTYPE html>
      <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Trae Webview</title>
        <style>
          :root {
            ${themeData.cssVariables}
          }
          
          body {
            background-color: var(--trae-background);
            color: var(--trae-foreground);
            font-family: var(--vscode-font-family);
            font-size: var(--vscode-font-size);
            margin: 0;
            padding: 16px;
          }
          
          .theme-${themeData.kindName.toLowerCase().replace(/\s+/g, '-')} {
            /* 主题特定样式 */
          }
          
          .button {
            background-color: var(--trae-primary);
            color: var(--trae-background);
            border: none;
            padding: 8px 16px;
            border-radius: 4px;
            cursor: pointer;
            font-family: inherit;
          }
          
          .button:hover {
            opacity: 0.8;
          }
          
          .card {
            background-color: var(--trae-surface);
            border: 1px solid var(--trae-border);
            border-radius: 4px;
            padding: 16px;
            margin: 8px 0;
          }
          
          .text-success { color: var(--trae-success); }
          .text-warning { color: var(--trae-warning); }
          .text-error { color: var(--trae-error); }
          .text-info { color: var(--trae-info); }
        </style>
      </head>
      <body class="theme-${themeData.kindName.toLowerCase().replace(/\s+/g, '-')}">
        ${content}
        
        <script>
          // 主题变化监听
          window.addEventListener('message', event => {
            const message = event.data;
            
            if (message.type === 'themeChanged') {
              updateTheme(message.theme);
            }
          });
          
          function updateTheme(themeData) {
            // 更新 CSS 变量
            const root = document.documentElement;
            Object.entries(themeData.colors).forEach(([key, value]) => {
              root.style.setProperty(\`--trae-\${key}\`, value);
            });
            
            // 更新 body 类名
            document.body.className = \`theme-\${themeData.kindName.toLowerCase().replace(/\\s+/g, '-')}\`;
            
            // 触发自定义事件
            document.dispatchEvent(new CustomEvent('themeChanged', {
              detail: themeData
            }));
          }
          
          // 初始主题数据
          const initialTheme = ${JSON.stringify(themeData)};
          updateTheme(initialTheme);
        </script>
      </body>
      </html>
    `;
  }
  
  dispose(): void {
    this.disposables.forEach(d => d.dispose());
    this.webviewPanels.clear();
  }
}

interface WebviewThemeData {
  kind: ColorThemeKind;
  kindName: string;
  isDark: boolean;
  isLight: boolean;
  isHighContrast: boolean;
  colors: Record<string, string>;
  cssVariables: string;
}

最佳实践

主题适配

typescript
// 1. 使用主题颜色而不是硬编码颜色
const decorationType = window.createTextEditorDecorationType({
  backgroundColor: new ThemeColor('editor.selectionHighlightBackground'),
  border: `1px solid ${new ThemeColor('editor.selectionHighlightBorder')}`
});

// 2. 响应主题变化
const themeListener = window.onDidChangeActiveColorTheme(theme => {
  // 更新 UI 元素
  updateStatusBarColors(theme);
  updateDecorationColors(theme);
});

// 3. 提供主题特定的资源
const getIconPath = (iconName: string): string => {
  const theme = window.activeColorTheme;
  const themeFolder = isLightTheme(theme) ? 'light' : 'dark';
  return path.join(extensionPath, 'icons', themeFolder, `${iconName}.svg`);
};

// 4. 使用条件样式
const getButtonStyle = (): string => {
  const theme = window.activeColorTheme;
  
  if (isHighContrastTheme(theme)) {
    return 'high-contrast-button';
  } else if (isDarkTheme(theme)) {
    return 'dark-button';
  } else {
    return 'light-button';
  }
};

性能优化

typescript
// 1. 缓存主题相关计算
class ThemeCache {
  private cache = new Map<string, any>();
  private currentThemeKind: ColorThemeKind;
  
  constructor() {
    this.currentThemeKind = window.activeColorTheme.kind;
    
    window.onDidChangeActiveColorTheme(theme => {
      if (theme.kind !== this.currentThemeKind) {
        this.cache.clear();
        this.currentThemeKind = theme.kind;
      }
    });
  }
  
  get<T>(key: string, factory: () => T): T {
    if (!this.cache.has(key)) {
      this.cache.set(key, factory());
    }
    return this.cache.get(key);
  }
}

// 2. 批量更新 UI
class ThemeUIUpdater {
  private updateQueue: (() => void)[] = [];
  private updateScheduled = false;
  
  scheduleUpdate(updateFn: () => void): void {
    this.updateQueue.push(updateFn);
    
    if (!this.updateScheduled) {
      this.updateScheduled = true;
      setImmediate(() => {
        this.flushUpdates();
      });
    }
  }
  
  private flushUpdates(): void {
    const updates = this.updateQueue.splice(0);
    updates.forEach(update => {
      try {
        update();
      } catch (error) {
        console.error('Theme update error:', error);
      }
    });
    
    this.updateScheduled = false;
  }
}

错误处理

typescript
class ThemeErrorHandler {
  static handleThemeError(error: Error, context: string): void {
    console.error(`Theme error in ${context}:`, error);
    
    // 记录错误
    this.logError(error, context);
    
    // 尝试恢复
    this.attemptRecovery(context);
  }
  
  private static logError(error: Error, context: string): void {
    // 发送错误报告
    // telemetry.sendError('theme-error', { context, error: error.message });
  }
  
  private static attemptRecovery(context: string): void {
    switch (context) {
      case 'theme-switch':
        // 尝试切换到默认主题
        commands.executeCommand('workbench.action.selectTheme', 'Default Dark+');
        break;
      case 'webview-theme':
        // 重新发送主题数据
        // webviewThemeProvider.updateAllWebviews();
        break;
    }
  }
}

相关 API

示例项目

您的终极 AI 驱动 IDE 学习指南