Skip to content

Themes API

The Themes API provides comprehensive functionality for creating, managing, and applying custom themes within the development environment.

Overview

The Themes API enables you to:

  • Create custom color themes and syntax highlighting
  • Manage theme switching and preferences
  • Provide theme customization options
  • Handle light/dark mode transitions
  • Create icon themes and file associations
  • Implement theme inheritance and variants
  • Provide theme preview and testing capabilities
  • Handle theme packaging and distribution

Basic Usage

Theme Management

typescript
import { TraeAPI } from '@trae/api';

// Theme manager implementation
class ThemeManager {
  private themes: Map<string, ThemeInfo> = new Map();
  private currentTheme: string | null = null;
  private themeChangeListeners: ((theme: ThemeInfo) => void)[] = [];
  private customThemes: Map<string, CustomTheme> = new Map();

  constructor() {
    this.loadBuiltInThemes();
    this.loadCustomThemes();
    this.setupThemeListeners();
    this.applyStoredTheme();
  }

  private loadBuiltInThemes(): void {
    // Load built-in themes
    const builtInThemes: ThemeInfo[] = [
      {
        id: 'dark-default',
        label: 'Dark (Default)',
        description: 'Default dark theme',
        type: 'dark',
        isBuiltIn: true,
        colors: {
          'editor.background': '#1e1e1e',
          'editor.foreground': '#d4d4d4',
          'activityBar.background': '#333333',
          'sideBar.background': '#252526',
          'statusBar.background': '#007acc',
          'titleBar.activeBackground': '#3c3c3c'
        },
        tokenColors: [
          {
            scope: 'comment',
            settings: {
              foreground: '#6A9955',
              fontStyle: 'italic'
            }
          },
          {
            scope: 'keyword',
            settings: {
              foreground: '#569CD6'
            }
          },
          {
            scope: 'string',
            settings: {
              foreground: '#CE9178'
            }
          }
        ]
      },
      {
        id: 'light-default',
        label: 'Light (Default)',
        description: 'Default light theme',
        type: 'light',
        isBuiltIn: true,
        colors: {
          'editor.background': '#ffffff',
          'editor.foreground': '#000000',
          'activityBar.background': '#f3f3f3',
          'sideBar.background': '#f8f8f8',
          'statusBar.background': '#007acc',
          'titleBar.activeBackground': '#dddddd'
        },
        tokenColors: [
          {
            scope: 'comment',
            settings: {
              foreground: '#008000',
              fontStyle: 'italic'
            }
          },
          {
            scope: 'keyword',
            settings: {
              foreground: '#0000ff'
            }
          },
          {
            scope: 'string',
            settings: {
              foreground: '#a31515'
            }
          }
        ]
      },
      {
        id: 'high-contrast',
        label: 'High Contrast',
        description: 'High contrast theme for accessibility',
        type: 'hc',
        isBuiltIn: true,
        colors: {
          'editor.background': '#000000',
          'editor.foreground': '#ffffff',
          'activityBar.background': '#000000',
          'sideBar.background': '#000000',
          'statusBar.background': '#000000',
          'titleBar.activeBackground': '#000000'
        },
        tokenColors: [
          {
            scope: 'comment',
            settings: {
              foreground: '#7ca668'
            }
          },
          {
            scope: 'keyword',
            settings: {
              foreground: '#569cd6'
            }
          },
          {
            scope: 'string',
            settings: {
              foreground: '#ce9178'
            }
          }
        ]
      }
    ];

    builtInThemes.forEach(theme => {
      this.themes.set(theme.id, theme);
    });

    console.log(`Loaded ${builtInThemes.length} built-in themes`);
  }

  private async loadCustomThemes(): Promise<void> {
    try {
      // Load custom themes from workspace and global storage
      const workspaceThemes = await this.loadWorkspaceThemes();
      const globalThemes = await this.loadGlobalThemes();
      
      [...workspaceThemes, ...globalThemes].forEach(theme => {
        this.themes.set(theme.id, theme);
      });
      
      console.log(`Loaded ${workspaceThemes.length + globalThemes.length} custom themes`);
    } catch (error) {
      console.error('Failed to load custom themes:', error);
    }
  }

  private async loadWorkspaceThemes(): Promise<ThemeInfo[]> {
    const themes: ThemeInfo[] = [];
    
    if (!TraeAPI.workspace.workspaceFolders) {
      return themes;
    }
    
    for (const folder of TraeAPI.workspace.workspaceFolders) {
      const themeFiles = await TraeAPI.workspace.findFiles(
        new TraeAPI.RelativePattern(folder, '**/*.json'),
        '**/node_modules/**'
      );
      
      for (const file of themeFiles) {
        if (file.fsPath.includes('themes') || file.fsPath.endsWith('-theme.json')) {
          try {
            const content = await TraeAPI.workspace.fs.readFile(file);
            const themeData = JSON.parse(content.toString());
            
            if (this.isValidTheme(themeData)) {
              const theme = this.parseThemeFile(file, themeData);
              themes.push(theme);
            }
          } catch (error) {
            console.warn(`Failed to load theme file ${file.fsPath}:`, error);
          }
        }
      }
    }
    
    return themes;
  }

  private async loadGlobalThemes(): Promise<ThemeInfo[]> {
    // Load themes from global storage or user settings
    const themes: ThemeInfo[] = [];
    
    try {
      const globalThemeData = TraeAPI.workspace.getConfiguration('themes').get('custom', []);
      
      for (const themeData of globalThemeData) {
        if (this.isValidTheme(themeData)) {
          const theme: ThemeInfo = {
            id: themeData.id || `custom-${Date.now()}`,
            label: themeData.label || 'Custom Theme',
            description: themeData.description || '',
            type: themeData.type || 'dark',
            isBuiltIn: false,
            colors: themeData.colors || {},
            tokenColors: themeData.tokenColors || []
          };
          
          themes.push(theme);
        }
      }
    } catch (error) {
      console.warn('Failed to load global themes:', error);
    }
    
    return themes;
  }

  private setupThemeListeners(): void {
    // Listen for configuration changes
    TraeAPI.workspace.onDidChangeConfiguration(event => {
      if (event.affectsConfiguration('workbench.colorTheme') ||
          event.affectsConfiguration('themes')) {
        this.handleConfigurationChange();
      }
    });

    // Listen for file system changes for theme files
    const themeWatcher = TraeAPI.workspace.createFileSystemWatcher('**/*theme*.json');
    themeWatcher.onDidCreate(uri => this.handleThemeFileChange(uri));
    themeWatcher.onDidChange(uri => this.handleThemeFileChange(uri));
    themeWatcher.onDidDelete(uri => this.handleThemeFileDelete(uri));
  }

  private async applyStoredTheme(): Promise<void> {
    const storedTheme = TraeAPI.workspace.getConfiguration('workbench').get('colorTheme');
    
    if (storedTheme && this.themes.has(storedTheme as string)) {
      await this.applyTheme(storedTheme as string);
    } else {
      // Apply default theme based on system preference
      const prefersDark = await this.getSystemThemePreference();
      const defaultTheme = prefersDark ? 'dark-default' : 'light-default';
      await this.applyTheme(defaultTheme);
    }
  }

  private async getSystemThemePreference(): Promise<boolean> {
    // Check system theme preference
    try {
      if (typeof window !== 'undefined' && window.matchMedia) {
        return window.matchMedia('(prefers-color-scheme: dark)').matches;
      }
    } catch (error) {
      console.warn('Failed to detect system theme preference:', error);
    }
    
    return true; // Default to dark
  }

  // Apply theme
  async applyTheme(themeId: string): Promise<boolean> {
    const theme = this.themes.get(themeId);
    if (!theme) {
      console.error(`Theme not found: ${themeId}`);
      return false;
    }

    try {
      console.log(`Applying theme: ${theme.label}`);
      
      // Apply color theme
      await this.applyColorTheme(theme);
      
      // Apply syntax highlighting
      await this.applySyntaxHighlighting(theme);
      
      // Update current theme
      this.currentTheme = themeId;
      
      // Save to configuration
      await TraeAPI.workspace.getConfiguration('workbench').update(
        'colorTheme',
        themeId,
        TraeAPI.ConfigurationTarget.Global
      );
      
      // Notify listeners
      this.notifyThemeChange(theme);
      
      console.log(`Theme applied successfully: ${theme.label}`);
      return true;
    } catch (error) {
      console.error(`Failed to apply theme ${themeId}:`, error);
      TraeAPI.window.showErrorMessage(`Failed to apply theme: ${error.message}`);
      return false;
    }
  }

  private async applyColorTheme(theme: ThemeInfo): Promise<void> {
    // Apply color theme to the editor and UI
    const colors = theme.colors;
    
    // Apply colors to CSS custom properties or theme system
    if (typeof document !== 'undefined') {
      const root = document.documentElement;
      
      Object.entries(colors).forEach(([key, value]) => {
        const cssVar = `--theme-${key.replace(/\./g, '-')}`;
        root.style.setProperty(cssVar, value);
      });
      
      // Set theme type class
      root.className = root.className.replace(/theme-(light|dark|hc)/g, '');
      root.classList.add(`theme-${theme.type}`);
    }
  }

  private async applySyntaxHighlighting(theme: ThemeInfo): Promise<void> {
    // Apply syntax highlighting rules
    const tokenColors = theme.tokenColors;
    
    // Generate CSS for syntax highlighting
    const syntaxCSS = this.generateSyntaxCSS(tokenColors);
    
    // Apply syntax CSS
    if (typeof document !== 'undefined') {
      let syntaxStyleElement = document.getElementById('theme-syntax-highlighting');
      
      if (!syntaxStyleElement) {
        syntaxStyleElement = document.createElement('style');
        syntaxStyleElement.id = 'theme-syntax-highlighting';
        document.head.appendChild(syntaxStyleElement);
      }
      
      syntaxStyleElement.textContent = syntaxCSS;
    }
  }

  private generateSyntaxCSS(tokenColors: TokenColor[]): string {
    let css = '';
    
    tokenColors.forEach((tokenColor, index) => {
      const scopes = Array.isArray(tokenColor.scope) ? tokenColor.scope : [tokenColor.scope];
      
      scopes.forEach(scope => {
        const selector = `.token.${scope.replace(/\./g, '-')}`;
        const styles: string[] = [];
        
        if (tokenColor.settings.foreground) {
          styles.push(`color: ${tokenColor.settings.foreground}`);
        }
        
        if (tokenColor.settings.background) {
          styles.push(`background-color: ${tokenColor.settings.background}`);
        }
        
        if (tokenColor.settings.fontStyle) {
          if (tokenColor.settings.fontStyle.includes('italic')) {
            styles.push('font-style: italic');
          }
          if (tokenColor.settings.fontStyle.includes('bold')) {
            styles.push('font-weight: bold');
          }
          if (tokenColor.settings.fontStyle.includes('underline')) {
            styles.push('text-decoration: underline');
          }
        }
        
        if (styles.length > 0) {
          css += `${selector} { ${styles.join('; ')} }\n`;
        }
      });
    });
    
    return css;
  }

  // Theme creation and customization
  async createCustomTheme(options: ThemeCreationOptions): Promise<string> {
    const themeId = `custom-${options.name.toLowerCase().replace(/\s+/g, '-')}-${Date.now()}`;
    
    const customTheme: CustomTheme = {
      id: themeId,
      name: options.name,
      description: options.description || '',
      baseTheme: options.baseTheme || 'dark-default',
      customizations: options.customizations || {},
      author: options.author || 'User',
      version: '1.0.0',
      created: new Date(),
      modified: new Date()
    };
    
    // Generate theme from base theme and customizations
    const theme = await this.generateThemeFromCustomization(customTheme);
    
    // Store custom theme
    this.customThemes.set(themeId, customTheme);
    this.themes.set(themeId, theme);
    
    // Save to storage
    await this.saveCustomTheme(customTheme);
    
    console.log(`Custom theme created: ${options.name}`);
    TraeAPI.window.showInformationMessage(`Custom theme "${options.name}" created successfully!`);
    
    return themeId;
  }

  private async generateThemeFromCustomization(customTheme: CustomTheme): Promise<ThemeInfo> {
    const baseTheme = this.themes.get(customTheme.baseTheme);
    if (!baseTheme) {
      throw new Error(`Base theme not found: ${customTheme.baseTheme}`);
    }
    
    // Clone base theme
    const theme: ThemeInfo = {
      id: customTheme.id,
      label: customTheme.name,
      description: customTheme.description,
      type: baseTheme.type,
      isBuiltIn: false,
      colors: { ...baseTheme.colors },
      tokenColors: [...baseTheme.tokenColors]
    };
    
    // Apply customizations
    const customizations = customTheme.customizations;
    
    // Apply color customizations
    if (customizations.colors) {
      Object.assign(theme.colors, customizations.colors);
    }
    
    // Apply token color customizations
    if (customizations.tokenColors) {
      // Merge or replace token colors
      customizations.tokenColors.forEach(customToken => {
        const existingIndex = theme.tokenColors.findIndex(token => 
          token.scope === customToken.scope
        );
        
        if (existingIndex >= 0) {
          // Update existing token color
          theme.tokenColors[existingIndex] = { ...theme.tokenColors[existingIndex], ...customToken };
        } else {
          // Add new token color
          theme.tokenColors.push(customToken);
        }
      });
    }
    
    // Apply semantic highlighting customizations
    if (customizations.semanticHighlighting) {
      theme.semanticHighlighting = customizations.semanticHighlighting;
    }
    
    return theme;
  }

  async updateCustomTheme(themeId: string, updates: Partial<ThemeCreationOptions>): Promise<boolean> {
    const customTheme = this.customThemes.get(themeId);
    if (!customTheme) {
      console.error(`Custom theme not found: ${themeId}`);
      return false;
    }
    
    try {
      // Update custom theme
      if (updates.name) customTheme.name = updates.name;
      if (updates.description) customTheme.description = updates.description;
      if (updates.customizations) {
        customTheme.customizations = { ...customTheme.customizations, ...updates.customizations };
      }
      customTheme.modified = new Date();
      
      // Regenerate theme
      const theme = await this.generateThemeFromCustomization(customTheme);
      this.themes.set(themeId, theme);
      
      // Save to storage
      await this.saveCustomTheme(customTheme);
      
      // Reapply if current theme
      if (this.currentTheme === themeId) {
        await this.applyTheme(themeId);
      }
      
      console.log(`Custom theme updated: ${customTheme.name}`);
      return true;
    } catch (error) {
      console.error(`Failed to update custom theme ${themeId}:`, error);
      return false;
    }
  }

  async deleteCustomTheme(themeId: string): Promise<boolean> {
    const customTheme = this.customThemes.get(themeId);
    if (!customTheme) {
      console.error(`Custom theme not found: ${themeId}`);
      return false;
    }
    
    try {
      // Switch to default theme if deleting current theme
      if (this.currentTheme === themeId) {
        await this.applyTheme('dark-default');
      }
      
      // Remove from storage
      await this.removeCustomTheme(themeId);
      
      // Remove from memory
      this.customThemes.delete(themeId);
      this.themes.delete(themeId);
      
      console.log(`Custom theme deleted: ${customTheme.name}`);
      TraeAPI.window.showInformationMessage(`Custom theme "${customTheme.name}" deleted successfully!`);
      
      return true;
    } catch (error) {
      console.error(`Failed to delete custom theme ${themeId}:`, error);
      return false;
    }
  }

  // Theme import/export
  async exportTheme(themeId: string): Promise<string | undefined> {
    const theme = this.themes.get(themeId);
    if (!theme) {
      console.error(`Theme not found: ${themeId}`);
      return undefined;
    }
    
    try {
      const exportData = {
        name: theme.label,
        type: theme.type,
        colors: theme.colors,
        tokenColors: theme.tokenColors,
        semanticHighlighting: theme.semanticHighlighting,
        metadata: {
          id: theme.id,
          description: theme.description,
          exportedAt: new Date().toISOString(),
          version: '1.0.0'
        }
      };
      
      const exportJson = JSON.stringify(exportData, null, 2);
      
      // Save to file
      const fileName = `${theme.label.replace(/\s+/g, '-').toLowerCase()}-theme.json`;
      const uri = await TraeAPI.window.showSaveDialog({
        defaultUri: TraeAPI.Uri.file(fileName),
        filters: {
          'Theme Files': ['json']
        }
      });
      
      if (uri) {
        await TraeAPI.workspace.fs.writeFile(uri, Buffer.from(exportJson));
        TraeAPI.window.showInformationMessage(`Theme exported to ${uri.fsPath}`);
        return uri.fsPath;
      }
      
      return undefined;
    } catch (error) {
      console.error(`Failed to export theme ${themeId}:`, error);
      TraeAPI.window.showErrorMessage(`Failed to export theme: ${error.message}`);
      return undefined;
    }
  }

  async importTheme(filePath?: string): Promise<string | undefined> {
    try {
      let uri: TraeAPI.Uri;
      
      if (filePath) {
        uri = TraeAPI.Uri.file(filePath);
      } else {
        const uris = await TraeAPI.window.showOpenDialog({
          canSelectFiles: true,
          canSelectMany: false,
          filters: {
            'Theme Files': ['json']
          }
        });
        
        if (!uris || uris.length === 0) {
          return undefined;
        }
        
        uri = uris[0];
      }
      
      // Read theme file
      const content = await TraeAPI.workspace.fs.readFile(uri);
      const themeData = JSON.parse(content.toString());
      
      if (!this.isValidTheme(themeData)) {
        throw new Error('Invalid theme file format');
      }
      
      // Create theme
      const themeId = `imported-${themeData.name.toLowerCase().replace(/\s+/g, '-')}-${Date.now()}`;
      const theme: ThemeInfo = {
        id: themeId,
        label: themeData.name,
        description: themeData.description || themeData.metadata?.description || '',
        type: themeData.type || 'dark',
        isBuiltIn: false,
        colors: themeData.colors || {},
        tokenColors: themeData.tokenColors || [],
        semanticHighlighting: themeData.semanticHighlighting
      };
      
      // Add to themes
      this.themes.set(themeId, theme);
      
      // Save as custom theme
      const customTheme: CustomTheme = {
        id: themeId,
        name: theme.label,
        description: theme.description,
        baseTheme: 'dark-default',
        customizations: {
          colors: theme.colors,
          tokenColors: theme.tokenColors,
          semanticHighlighting: theme.semanticHighlighting
        },
        author: themeData.metadata?.author || 'Unknown',
        version: themeData.metadata?.version || '1.0.0',
        created: new Date(),
        modified: new Date()
      };
      
      this.customThemes.set(themeId, customTheme);
      await this.saveCustomTheme(customTheme);
      
      console.log(`Theme imported: ${theme.label}`);
      TraeAPI.window.showInformationMessage(`Theme "${theme.label}" imported successfully!`);
      
      return themeId;
    } catch (error) {
      console.error('Failed to import theme:', error);
      TraeAPI.window.showErrorMessage(`Failed to import theme: ${error.message}`);
      return undefined;
    }
  }

  // Theme utilities
  getThemes(): ThemeInfo[] {
    return Array.from(this.themes.values());
  }

  getTheme(themeId: string): ThemeInfo | undefined {
    return this.themes.get(themeId);
  }

  getCurrentTheme(): ThemeInfo | undefined {
    return this.currentTheme ? this.themes.get(this.currentTheme) : undefined;
  }

  getThemesByType(type: 'light' | 'dark' | 'hc'): ThemeInfo[] {
    return Array.from(this.themes.values()).filter(theme => theme.type === type);
  }

  getCustomThemes(): CustomTheme[] {
    return Array.from(this.customThemes.values());
  }

  searchThemes(query: string): ThemeInfo[] {
    const lowercaseQuery = query.toLowerCase();
    return Array.from(this.themes.values()).filter(theme =>
      theme.label.toLowerCase().includes(lowercaseQuery) ||
      theme.description.toLowerCase().includes(lowercaseQuery)
    );
  }

  // Theme preview
  async previewTheme(themeId: string): Promise<boolean> {
    const theme = this.themes.get(themeId);
    if (!theme) {
      console.error(`Theme not found: ${themeId}`);
      return false;
    }
    
    try {
      // Store current theme for restoration
      const previousTheme = this.currentTheme;
      
      // Apply preview theme
      await this.applyTheme(themeId);
      
      // Show preview dialog
      const action = await TraeAPI.window.showInformationMessage(
        `Previewing theme: ${theme.label}`,
        { modal: true },
        'Apply Theme',
        'Restore Previous'
      );
      
      if (action === 'Apply Theme') {
        // Keep the theme
        return true;
      } else {
        // Restore previous theme
        if (previousTheme) {
          await this.applyTheme(previousTheme);
        }
        return false;
      }
    } catch (error) {
      console.error(`Failed to preview theme ${themeId}:`, error);
      return false;
    }
  }

  // Event handling
  onThemeChange(listener: (theme: ThemeInfo) => void): TraeAPI.Disposable {
    this.themeChangeListeners.push(listener);
    
    return {
      dispose: () => {
        const index = this.themeChangeListeners.indexOf(listener);
        if (index >= 0) {
          this.themeChangeListeners.splice(index, 1);
        }
      }
    };
  }

  private notifyThemeChange(theme: ThemeInfo): void {
    this.themeChangeListeners.forEach(listener => {
      try {
        listener(theme);
      } catch (error) {
        console.error('Error in theme change listener:', error);
      }
    });
  }

  private async handleConfigurationChange(): Promise<void> {
    const currentConfigTheme = TraeAPI.workspace.getConfiguration('workbench').get('colorTheme');
    
    if (currentConfigTheme && currentConfigTheme !== this.currentTheme) {
      await this.applyTheme(currentConfigTheme as string);
    }
  }

  private async handleThemeFileChange(uri: TraeAPI.Uri): Promise<void> {
    console.log(`Theme file changed: ${uri.fsPath}`);
    
    // Reload themes
    await this.loadCustomThemes();
  }

  private async handleThemeFileDelete(uri: TraeAPI.Uri): Promise<void> {
    console.log(`Theme file deleted: ${uri.fsPath}`);
    
    // Find and remove theme
    const themeToRemove = Array.from(this.themes.values()).find(theme => 
      !theme.isBuiltIn && uri.fsPath.includes(theme.id)
    );
    
    if (themeToRemove) {
      this.themes.delete(themeToRemove.id);
      this.customThemes.delete(themeToRemove.id);
      
      // Switch to default if current theme was deleted
      if (this.currentTheme === themeToRemove.id) {
        await this.applyTheme('dark-default');
      }
    }
  }

  // Storage methods
  private async saveCustomTheme(customTheme: CustomTheme): Promise<void> {
    try {
      const customThemes = TraeAPI.workspace.getConfiguration('themes').get('custom', []);
      const existingIndex = customThemes.findIndex((t: any) => t.id === customTheme.id);
      
      if (existingIndex >= 0) {
        customThemes[existingIndex] = customTheme;
      } else {
        customThemes.push(customTheme);
      }
      
      await TraeAPI.workspace.getConfiguration('themes').update(
        'custom',
        customThemes,
        TraeAPI.ConfigurationTarget.Global
      );
    } catch (error) {
      console.error('Failed to save custom theme:', error);
    }
  }

  private async removeCustomTheme(themeId: string): Promise<void> {
    try {
      const customThemes = TraeAPI.workspace.getConfiguration('themes').get('custom', []);
      const filteredThemes = customThemes.filter((t: any) => t.id !== themeId);
      
      await TraeAPI.workspace.getConfiguration('themes').update(
        'custom',
        filteredThemes,
        TraeAPI.ConfigurationTarget.Global
      );
    } catch (error) {
      console.error('Failed to remove custom theme:', error);
    }
  }

  // Validation
  private isValidTheme(themeData: any): boolean {
    return (
      themeData &&
      typeof themeData === 'object' &&
      (themeData.name || themeData.label) &&
      (themeData.colors || themeData.tokenColors)
    );
  }

  private parseThemeFile(file: TraeAPI.Uri, themeData: any): ThemeInfo {
    const fileName = file.fsPath.split('/').pop() || '';
    const themeId = `file-${fileName.replace(/\.json$/, '')}-${Date.now()}`;
    
    return {
      id: themeId,
      label: themeData.name || themeData.label || fileName,
      description: themeData.description || '',
      type: themeData.type || 'dark',
      isBuiltIn: false,
      colors: themeData.colors || {},
      tokenColors: themeData.tokenColors || [],
      semanticHighlighting: themeData.semanticHighlighting
    };
  }

  dispose(): void {
    this.themes.clear();
    this.customThemes.clear();
    this.themeChangeListeners.length = 0;
    this.currentTheme = null;
  }
}

// Interfaces
interface ThemeInfo {
  id: string;
  label: string;
  description: string;
  type: 'light' | 'dark' | 'hc';
  isBuiltIn: boolean;
  colors: Record<string, string>;
  tokenColors: TokenColor[];
  semanticHighlighting?: Record<string, any>;
}

interface TokenColor {
  scope: string | string[];
  settings: {
    foreground?: string;
    background?: string;
    fontStyle?: string;
  };
}

interface CustomTheme {
  id: string;
  name: string;
  description: string;
  baseTheme: string;
  customizations: {
    colors?: Record<string, string>;
    tokenColors?: TokenColor[];
    semanticHighlighting?: Record<string, any>;
  };
  author: string;
  version: string;
  created: Date;
  modified: Date;
}

interface ThemeCreationOptions {
  name: string;
  description?: string;
  baseTheme?: string;
  customizations?: {
    colors?: Record<string, string>;
    tokenColors?: TokenColor[];
    semanticHighlighting?: Record<string, any>;
  };
  author?: string;
}

// Initialize theme manager
const themeManager = new ThemeManager();

Icon Themes

typescript
// Icon theme management
class IconThemeManager {
  private iconThemes: Map<string, IconThemeInfo> = new Map();
  private currentIconTheme: string | null = null;
  private iconThemeChangeListeners: ((theme: IconThemeInfo) => void)[] = [];

  constructor() {
    this.loadBuiltInIconThemes();
    this.loadCustomIconThemes();
    this.applyStoredIconTheme();
  }

  private loadBuiltInIconThemes(): void {
    const builtInIconThemes: IconThemeInfo[] = [
      {
        id: 'default-icons',
        label: 'Default Icons',
        description: 'Default file and folder icons',
        isBuiltIn: true,
        iconDefinitions: {
          'file': { iconPath: './icons/file.svg' },
          'folder': { iconPath: './icons/folder.svg' },
          'folder-open': { iconPath: './icons/folder-open.svg' }
        },
        fileExtensions: {
          'js': 'javascript-icon',
          'ts': 'typescript-icon',
          'json': 'json-icon',
          'md': 'markdown-icon'
        },
        fileNames: {
          'package.json': 'npm-icon',
          'README.md': 'readme-icon'
        },
        folderNames: {
          'node_modules': 'node-modules-icon',
          'src': 'source-icon',
          'dist': 'dist-icon'
        }
      }
    ];

    builtInIconThemes.forEach(theme => {
      this.iconThemes.set(theme.id, theme);
    });
  }

  private async loadCustomIconThemes(): Promise<void> {
    // Load custom icon themes from workspace and settings
    try {
      const customIconThemes = TraeAPI.workspace.getConfiguration('iconThemes').get('custom', []);
      
      customIconThemes.forEach((themeData: any) => {
        if (this.isValidIconTheme(themeData)) {
          const theme: IconThemeInfo = {
            id: themeData.id,
            label: themeData.label,
            description: themeData.description || '',
            isBuiltIn: false,
            iconDefinitions: themeData.iconDefinitions || {},
            fileExtensions: themeData.fileExtensions || {},
            fileNames: themeData.fileNames || {},
            folderNames: themeData.folderNames || {}
          };
          
          this.iconThemes.set(theme.id, theme);
        }
      });
    } catch (error) {
      console.error('Failed to load custom icon themes:', error);
    }
  }

  private async applyStoredIconTheme(): Promise<void> {
    const storedIconTheme = TraeAPI.workspace.getConfiguration('workbench').get('iconTheme');
    
    if (storedIconTheme && this.iconThemes.has(storedIconTheme as string)) {
      await this.applyIconTheme(storedIconTheme as string);
    } else {
      await this.applyIconTheme('default-icons');
    }
  }

  async applyIconTheme(themeId: string): Promise<boolean> {
    const theme = this.iconThemes.get(themeId);
    if (!theme) {
      console.error(`Icon theme not found: ${themeId}`);
      return false;
    }

    try {
      console.log(`Applying icon theme: ${theme.label}`);
      
      // Apply icon theme CSS
      await this.applyIconThemeCSS(theme);
      
      // Update current icon theme
      this.currentIconTheme = themeId;
      
      // Save to configuration
      await TraeAPI.workspace.getConfiguration('workbench').update(
        'iconTheme',
        themeId,
        TraeAPI.ConfigurationTarget.Global
      );
      
      // Notify listeners
      this.notifyIconThemeChange(theme);
      
      console.log(`Icon theme applied successfully: ${theme.label}`);
      return true;
    } catch (error) {
      console.error(`Failed to apply icon theme ${themeId}:`, error);
      return false;
    }
  }

  private async applyIconThemeCSS(theme: IconThemeInfo): Promise<void> {
    // Generate CSS for icon theme
    const iconCSS = this.generateIconCSS(theme);
    
    // Apply icon CSS
    if (typeof document !== 'undefined') {
      let iconStyleElement = document.getElementById('icon-theme-styles');
      
      if (!iconStyleElement) {
        iconStyleElement = document.createElement('style');
        iconStyleElement.id = 'icon-theme-styles';
        document.head.appendChild(iconStyleElement);
      }
      
      iconStyleElement.textContent = iconCSS;
    }
  }

  private generateIconCSS(theme: IconThemeInfo): string {
    let css = '';
    
    // File extension icons
    Object.entries(theme.fileExtensions).forEach(([extension, iconId]) => {
      const iconDef = theme.iconDefinitions[iconId];
      if (iconDef) {
        css += `.file-icon[data-extension="${extension}"] { background-image: url(${iconDef.iconPath}); }\n`;
      }
    });
    
    // File name icons
    Object.entries(theme.fileNames).forEach(([fileName, iconId]) => {
      const iconDef = theme.iconDefinitions[iconId];
      if (iconDef) {
        css += `.file-icon[data-filename="${fileName}"] { background-image: url(${iconDef.iconPath}); }\n`;
      }
    });
    
    // Folder name icons
    Object.entries(theme.folderNames).forEach(([folderName, iconId]) => {
      const iconDef = theme.iconDefinitions[iconId];
      if (iconDef) {
        css += `.folder-icon[data-foldername="${folderName}"] { background-image: url(${iconDef.iconPath}); }\n`;
      }
    });
    
    return css;
  }

  getIconThemes(): IconThemeInfo[] {
    return Array.from(this.iconThemes.values());
  }

  getCurrentIconTheme(): IconThemeInfo | undefined {
    return this.currentIconTheme ? this.iconThemes.get(this.currentIconTheme) : undefined;
  }

  onIconThemeChange(listener: (theme: IconThemeInfo) => void): TraeAPI.Disposable {
    this.iconThemeChangeListeners.push(listener);
    
    return {
      dispose: () => {
        const index = this.iconThemeChangeListeners.indexOf(listener);
        if (index >= 0) {
          this.iconThemeChangeListeners.splice(index, 1);
        }
      }
    };
  }

  private notifyIconThemeChange(theme: IconThemeInfo): void {
    this.iconThemeChangeListeners.forEach(listener => {
      try {
        listener(theme);
      } catch (error) {
        console.error('Error in icon theme change listener:', error);
      }
    });
  }

  private isValidIconTheme(themeData: any): boolean {
    return (
      themeData &&
      typeof themeData === 'object' &&
      themeData.id &&
      themeData.label &&
      themeData.iconDefinitions
    );
  }

  dispose(): void {
    this.iconThemes.clear();
    this.iconThemeChangeListeners.length = 0;
    this.currentIconTheme = null;
  }
}

interface IconThemeInfo {
  id: string;
  label: string;
  description: string;
  isBuiltIn: boolean;
  iconDefinitions: Record<string, { iconPath: string }>;
  fileExtensions: Record<string, string>;
  fileNames: Record<string, string>;
  folderNames: Record<string, string>;
}

// Initialize icon theme manager
const iconThemeManager = new IconThemeManager();

API Reference

Core Interfaces

typescript
interface ThemeAPI {
  // Theme management
  getThemes(): ThemeInfo[];
  getCurrentTheme(): ThemeInfo | undefined;
  applyTheme(themeId: string): Promise<boolean>;
  
  // Custom themes
  createCustomTheme(options: ThemeCreationOptions): Promise<string>;
  updateCustomTheme(themeId: string, updates: Partial<ThemeCreationOptions>): Promise<boolean>;
  deleteCustomTheme(themeId: string): Promise<boolean>;
  
  // Import/Export
  exportTheme(themeId: string): Promise<string | undefined>;
  importTheme(filePath?: string): Promise<string | undefined>;
  
  // Events
  onThemeChange(listener: (theme: ThemeInfo) => void): Disposable;
  
  // Icon themes
  getIconThemes(): IconThemeInfo[];
  getCurrentIconTheme(): IconThemeInfo | undefined;
  applyIconTheme(themeId: string): Promise<boolean>;
  onIconThemeChange(listener: (theme: IconThemeInfo) => void): Disposable;
}

Best Practices

  1. Theme Structure: Follow standard theme file structure and naming conventions
  2. Color Accessibility: Ensure sufficient contrast ratios for accessibility
  3. Performance: Optimize theme loading and application for better performance
  4. Consistency: Maintain consistent color schemes across all UI elements
  5. User Experience: Provide smooth theme transitions and preview capabilities
  6. Documentation: Document custom theme properties and usage
  7. Testing: Test themes across different file types and UI states
  8. Compatibility: Ensure themes work across different platforms and configurations

Your Ultimate AI-Powered IDE Learning Guide