Prologue·🎬 Verdict first

Vibe Themer, reviewed

Vibe Themer is a VS Code extension that turns a sentence into a color theme. You type a vibe. "Cozy autumn evening." "Cyberpunk neon city." The extension streams a full theme back from the OpenAI API and applies each color the instant it lands. The editor repaints in front of you, line by line.

It ships on the VS Code Marketplace at v1.1.0, publisher mseeks. The README wears its origin as a badge: every line AI-authored, "not a single manual edit." This document reads the result back. The goal is to understand what it does, then find where the seams show.

The verdict: a C+. The happy path delivers — type a vibe, watch a coherent theme stream in, live. The build is clean, the packaging is correct, and the documentation is unusually thorough for the size. Underneath are the marks of code written without a human in the review loop: an OpenAI key that gets logged in plaintext the first time generation fails, a 1,248-line prompt that pays rent on ~19% duplicated text, a configuration-scope reader that silently does nothing, and zero automated tests hiding behind a green npm test. None of it stops the extension from working. All of it is what a second pair of eyes catches.

The scope is small: ~2,600 lines of TypeScript across 16 files, plus a 1,248-line prompt that is, in a real sense, the actual product. Every file is rendered in full below, so fold them open as you read. The build is green. tsc --noEmit, ESLint, and esbuild all pass, so nothing here is a compile error. The findings are about behavior and structure, and about the distance between what the code says it is and what it does.

Prologue·🎬 Map + table

The shape of the whole thing

The codebase aims for a three-layer architecture, spelled out in docs/ARCHITECTURE.md and the Copilot instructions. An Extension layer registers commands. A Service layer orchestrates workflows. A pure Core layer holds the domain logic. Files even advertise their layer in the name: *Service.ts, *Core.ts.

One path is load-bearing. The Change Theme command runs the streaming workflow: pick a prompt, ensure a client, then stream lines and write each one straight to VS Code settings. Everything else is a small satellite command, whether that means clearing the key, picking a model, or resetting the theme. Follow the spine and you have the extension.

FileLinesRole
extension.ts125Activation; registers all commands
themeGenerationService.ts433The streaming loop — orchestrates generation
themeCore.ts688Line parser + settings writer + current-state reader
openaiCore.ts424API-key state machine + client lifecycle
openaiService.ts110Thin pass-through wrappers over openaiCore
types/theme.ts146Domain types (discriminated unions)
promptPicker.ts108QuickPick UI for entering a vibe
suggestionCore.ts108Curated example prompts + validation
colorUtils.ts108Hex/RGB/HSL normalization (unreferenced)
commands/*.ts135Clear key · select model · reset theme
utils/*Test.ts250Manual dev commands (not automated tests)
prompts/streamingThemePrompt.txt1,248The system prompt — every theme selector enumerated
The files, by weight. The streaming loop and the prompt are where the product lives.
Act I · A vibe becomes a theme1.0🎬 Orientation

Following the one path that matters

Act I walks the Change Theme command end to end, in execution order: activation, prompt entry, client setup, the prompt itself, the streaming loop, and the parser/writer that turns a line of text into a repainted editor. This is 90% of what the extension does.

Act I · A vibe becomes a theme1.1🎬 Spotlight

Activation: register first, ask questions later

src/extension.ts

activate() runs once when VS Code loads the extension. Its first job is ordering. Register every command synchronously, then fire the async OpenAI setup without awaiting it. The comment block explains why, and the CHANGELOG confirms it was learned the hard way: v1.0.3 and v1.0.7 were both "command not found" fixes for getting this order wrong.

src/extension.ts · 125 lines
src/extension.ts125 lines · TypeScript
⋯ 38 lines hidden (lines 1–38)
1// The module 'vscode' contains the VS Code extensibility API
2import * as vscode from 'vscode';
3import OpenAI from 'openai';
4import {
5 initializeOpenAIClient,
6 getOpenAIClient,
7 getOpenAIClientState,
8 resetOpenAIClient
9} from './services/openaiService';
10import { registerClearApiKeyCommand } from './commands/clearApiKeyCommand';
11import { registerResetThemeCommand } from './commands/resetThemeCommand';
12import { runThemeGenerationWorkflow } from './services/themeGenerationService';
13import { selectOpenAIModel, resetOpenAIModel } from './commands/modelSelectCommand';
14import { testCurrentThemeReading } from './utils/themeStateTest';
15import { testCountParsing } from './utils/countParsingTest';
16import { testContextInjection } from './utils/contextInjectionTest';
17import { testRemoveValues } from './utils/removeValueTest';
18 
19// Reference to the OpenAI client instance
20let openai: OpenAI | undefined;
21 
22// Interface for theme data
23interface ThemeData {
24 name: string;
25 description: string;
26 colors: {
27 primary: string;
28 secondary: string;
29 accent: string;
30 background: string;
31 foreground: string;
32 };
33 tokenColors?: any;
35 
36// Store the last generated theme
37let lastGeneratedTheme: ThemeData | undefined;
38 
39export function activate(context: vscode.ExtensionContext) {
40 // Set development mode context for command visibility
41 const isDevelopment = context.extensionMode === vscode.ExtensionMode.Development;
42 vscode.commands.executeCommand('setContext', 'vibeThemer.development', isDevelopment);
44 // Register commands FIRST to ensure they're available immediately
45 // Command registration should be synchronous and happen before any async operations
47 // Register command to change theme based on natural language description
48 let changeThemeCommand = vscode.commands.registerCommand('vibeThemer.changeTheme', async () => {
49 await runThemeGenerationWorkflow(context, { current: lastGeneratedTheme });
50 // Update our local reference after theme generation
51 openai = getOpenAIClient();
52 });
53 context.subscriptions.push(changeThemeCommand);
⋯ 72 lines hidden (lines 54–125)
54 
55 // Register command to clear API key (using enhanced reset functionality)
56 registerClearApiKeyCommand(context, { current: openai });
58 // Register command to reset theme customizations (using enhanced architecture)
59 registerResetThemeCommand(context, { current: lastGeneratedTheme });
60 
61 // Register command to select OpenAI model
62 let selectModelCommand = vscode.commands.registerCommand('vibeThemer.selectModel', async () => {
63 await selectOpenAIModel(context);
64 });
65 context.subscriptions.push(selectModelCommand);
66 
67 // Register command to reset OpenAI model selection
68 let resetModelCommand = vscode.commands.registerCommand('vibeThemer.resetModel', async () => {
69 await resetOpenAIModel(context);
70 });
71 context.subscriptions.push(resetModelCommand);
72 
73 // Register test command for current theme state reading (development only)
74 if (context.extensionMode === vscode.ExtensionMode.Development) {
75 let testThemeStateCommand = vscode.commands.registerCommand('vibeThemer.testThemeState', async () => {
76 await testCurrentThemeReading();
77 });
78 context.subscriptions.push(testThemeStateCommand);
79 
80 let testCountParsingCommand = vscode.commands.registerCommand('vibeThemer.testCountParsing', async () => {
81 await testCountParsing();
82 });
83 context.subscriptions.push(testCountParsingCommand);
84 
85 let testContextInjectionCommand = vscode.commands.registerCommand('vibeThemer.testContextInjection', async () => {
86 await testContextInjection();
87 });
88 context.subscriptions.push(testContextInjectionCommand);
89 
90 let testRemoveValuesCommand = vscode.commands.registerCommand('vibeThemer.testRemoveValues', async () => {
91 await testRemoveValues();
92 });
93 context.subscriptions.push(testRemoveValuesCommand);
94 }
95 
96 // Initialize OpenAI client AFTER command registration
97 // This async operation happens after commands are registered
98 initializeOpenAIClient(context).then(initialized => {
99 if (!initialized) {
100 // The initialization process already handled user interaction
101 // We can inspect the state to understand what happened
102 const clientState = getOpenAIClientState();
103 if (clientState.status === 'error') {
104 console.log('Extension activation: OpenAI client initialization failed:', clientState.error.message);
105 } else {
106 console.log('Extension activation: OpenAI client initialization was cancelled or failed');
107 }
109 // We don't return here anymore - the extension should still be functional
110 // Users can still try to use commands which will prompt for API key setup
111 }
113 // Get the initialized OpenAI client (may be undefined if not set up)
114 openai = getOpenAIClient();
115 }).catch(error => {
116 console.error('Extension activation: OpenAI client initialization error:', error);
117 // Extension should still be functional even if OpenAI setup fails
118 });
120 
121// This method is called when your extension is deactivated
122export function deactivate() {
123 // Clean up resources if needed
125 

The Change Theme handler is a thin shim. It calls runThemeGenerationWorkflow and passes a mutable { current: ... } reference meant to hold the last generated theme. That ref-cell pattern recurs across the commands, and Act IV shows where it quietly fails. The dev-only test commands register behind an extensionMode === Development check, so end users never see them.

The dev-only command registrations (one carries a latent bug — see Act IV).

src/extension.ts · 125 lines
src/extension.ts125 lines · TypeScript
⋯ 72 lines hidden (lines 1–72)
1// The module 'vscode' contains the VS Code extensibility API
2import * as vscode from 'vscode';
3import OpenAI from 'openai';
4import {
5 initializeOpenAIClient,
6 getOpenAIClient,
7 getOpenAIClientState,
8 resetOpenAIClient
9} from './services/openaiService';
10import { registerClearApiKeyCommand } from './commands/clearApiKeyCommand';
11import { registerResetThemeCommand } from './commands/resetThemeCommand';
12import { runThemeGenerationWorkflow } from './services/themeGenerationService';
13import { selectOpenAIModel, resetOpenAIModel } from './commands/modelSelectCommand';
14import { testCurrentThemeReading } from './utils/themeStateTest';
15import { testCountParsing } from './utils/countParsingTest';
16import { testContextInjection } from './utils/contextInjectionTest';
17import { testRemoveValues } from './utils/removeValueTest';
18 
19// Reference to the OpenAI client instance
20let openai: OpenAI | undefined;
21 
22// Interface for theme data
23interface ThemeData {
24 name: string;
25 description: string;
26 colors: {
27 primary: string;
28 secondary: string;
29 accent: string;
30 background: string;
31 foreground: string;
32 };
33 tokenColors?: any;
35 
36// Store the last generated theme
37let lastGeneratedTheme: ThemeData | undefined;
38 
39export function activate(context: vscode.ExtensionContext) {
40 // Set development mode context for command visibility
41 const isDevelopment = context.extensionMode === vscode.ExtensionMode.Development;
42 vscode.commands.executeCommand('setContext', 'vibeThemer.development', isDevelopment);
44 // Register commands FIRST to ensure they're available immediately
45 // Command registration should be synchronous and happen before any async operations
47 // Register command to change theme based on natural language description
48 let changeThemeCommand = vscode.commands.registerCommand('vibeThemer.changeTheme', async () => {
49 await runThemeGenerationWorkflow(context, { current: lastGeneratedTheme });
50 // Update our local reference after theme generation
51 openai = getOpenAIClient();
52 });
53 context.subscriptions.push(changeThemeCommand);
54 
55 // Register command to clear API key (using enhanced reset functionality)
56 registerClearApiKeyCommand(context, { current: openai });
58 // Register command to reset theme customizations (using enhanced architecture)
59 registerResetThemeCommand(context, { current: lastGeneratedTheme });
60 
61 // Register command to select OpenAI model
62 let selectModelCommand = vscode.commands.registerCommand('vibeThemer.selectModel', async () => {
63 await selectOpenAIModel(context);
64 });
65 context.subscriptions.push(selectModelCommand);
66 
67 // Register command to reset OpenAI model selection
68 let resetModelCommand = vscode.commands.registerCommand('vibeThemer.resetModel', async () => {
69 await resetOpenAIModel(context);
70 });
71 context.subscriptions.push(resetModelCommand);
72 
73 // Register test command for current theme state reading (development only)
74 if (context.extensionMode === vscode.ExtensionMode.Development) {
75 let testThemeStateCommand = vscode.commands.registerCommand('vibeThemer.testThemeState', async () => {
76 await testCurrentThemeReading();
77 });
78 context.subscriptions.push(testThemeStateCommand);
79 
80 let testCountParsingCommand = vscode.commands.registerCommand('vibeThemer.testCountParsing', async () => {
81 await testCountParsing();
82 });
83 context.subscriptions.push(testCountParsingCommand);
84 
85 let testContextInjectionCommand = vscode.commands.registerCommand('vibeThemer.testContextInjection', async () => {
86 await testContextInjection();
87 });
88 context.subscriptions.push(testContextInjectionCommand);
89 
90 let testRemoveValuesCommand = vscode.commands.registerCommand('vibeThemer.testRemoveValues', async () => {
91 await testRemoveValues();
92 });
93 context.subscriptions.push(testRemoveValuesCommand);
94 }
⋯ 31 lines hidden (lines 95–125)
95 
96 // Initialize OpenAI client AFTER command registration
97 // This async operation happens after commands are registered
98 initializeOpenAIClient(context).then(initialized => {
99 if (!initialized) {
100 // The initialization process already handled user interaction
101 // We can inspect the state to understand what happened
102 const clientState = getOpenAIClientState();
103 if (clientState.status === 'error') {
104 console.log('Extension activation: OpenAI client initialization failed:', clientState.error.message);
105 } else {
106 console.log('Extension activation: OpenAI client initialization was cancelled or failed');
107 }
109 // We don't return here anymore - the extension should still be functional
110 // Users can still try to use commands which will prompt for API key setup
111 }
113 // Get the initialized OpenAI client (may be undefined if not set up)
114 openai = getOpenAIClient();
115 }).catch(error => {
116 console.error('Extension activation: OpenAI client initialization error:', error);
117 // Extension should still be functional even if OpenAI setup fails
118 });
120 
121// This method is called when your extension is deactivated
122export function deactivate() {
123 // Clean up resources if needed
125 
Act I · A vibe becomes a theme1.2🎬 Follow the data

Getting the vibe: QuickPick + curated seeds

src/utils/promptPicker.tssrc/core/suggestionCore.ts

The workflow opens with showThemePromptPicker(), a VS Code QuickPick seeded with six random curated examples. It also takes free text. Type, and your input becomes the first item, so you can submit anything at all. Pick an item, or accept what you typed, and the promise resolves. Dismiss it and you get undefined, which the caller reads as a cancel.

src/utils/promptPicker.ts · 108 lines
src/utils/promptPicker.ts108 lines · TypeScript
⋯ 43 lines hidden (lines 1–43)
1/**
2 * Theme prompt picker utilities.
3 * Pure functional approach to creating VS Code QuickPick interfaces.
4 * Isolated from business logic for clean separation of concerns.
5 */
6 
7import * as vscode from 'vscode';
8import { ThemePromptSuggestion } from '../types/theme.js';
9import { CURATED_SUGGESTIONS, getRandomCuratedSuggestions } from '../core/suggestionCore.js';
10 
11/**
12 * Creates a QuickPick item from a theme suggestion.
13 * Pure function that transforms domain objects to VS Code UI objects.
14 * Keeps it clean - just the vibe, no clutter.
15 */
16const createQuickPickItem = (suggestion: ThemePromptSuggestion): vscode.QuickPickItem => ({
17 label: suggestion.label
18 // No description, no detail - just the pure creative prompt
19});
20 
21/**
22 * Validates a theme prompt input.
23 * Pure function that applies business rules for theme descriptions.
24 */
25const validateThemePrompt = (prompt: string): string | null => {
26 const trimmed = prompt.trim();
27 
28 if (!trimmed) {
29 return 'Please enter a theme description';
30 }
31 
32 if (trimmed.length < 3) {
33 return 'Please provide a more detailed description for better results';
34 }
35 
36 return null;
37};
38 
39/**
40 * Shows a theme prompt picker with curated suggestions.
41 * Users can select a suggestion or type their own custom prompt.
42 * Returns the selected/typed prompt or undefined if cancelled.
43 */
44export const showThemePromptPicker = async (): Promise<string | undefined> => {
45 const quickPick = vscode.window.createQuickPick<vscode.QuickPickItem>();
46 
47 // Configure the picker
48 quickPick.title = '🎨 Create New Theme or Modify Current Theme';
49 quickPick.placeholder = '✨ Describe any vibe or mood... (modification requires existing vibe theme)';
50 quickPick.canSelectMany = false;
51 
52 // Show curated suggestions as starting options
53 const suggestions = getRandomCuratedSuggestions(6);
54 quickPick.items = suggestions.map(createQuickPickItem);
55 
56 return new Promise<string | undefined>((resolve) => {
57 let resolved = false;
58 
59 // Handle item selection
60 quickPick.onDidAccept(() => {
61 if (resolved) {
62 return;
63 }
64 
65 const selectedItem = quickPick.selectedItems[0];
66 const promptValue = selectedItem ? selectedItem.label : quickPick.value.trim();
67 
68 // Validate the prompt
69 const validationError = validateThemePrompt(promptValue);
70 if (validationError) {
71 vscode.window.showErrorMessage(validationError);
72 return; // Don't resolve, let user try again
73 }
74 
75 resolved = true;
76 resolve(promptValue);
77 quickPick.dispose();
78 });
⋯ 30 lines hidden (lines 79–108)
79 
80 // Handle cancellation
81 quickPick.onDidHide(() => {
82 if (resolved) {
83 return;
84 }
85 resolved = true;
86 resolve(undefined);
87 quickPick.dispose();
88 });
89 
90 // Handle input changes - allow free typing
91 quickPick.onDidChangeValue((value) => {
92 const trimmedValue = value.trim();
93 if (trimmedValue && !suggestions.some((s: ThemePromptSuggestion) => s.label === trimmedValue)) {
94 // Show current input as first option for custom prompts
95 const customItem: vscode.QuickPickItem = {
96 label: trimmedValue
97 // Clean custom prompt, no extra metadata
98 };
99 quickPick.items = [customItem, ...suggestions.map(createQuickPickItem)];
100 } else if (!trimmedValue) {
101 // Back to suggestions only
102 quickPick.items = suggestions.map(createQuickPickItem);
103 }
104 });
105 
106 quickPick.show();
107 });
108};

The seeds come from suggestionCore.ts: a frozen array of curated prompts, plus pure helpers that validate and shuffle them. It is the tidiest module in the repo. Small, pure, well-typed. It is also the foundation for a feature that never arrived, which the docs act picks up.

The curated suggestion list and its as const typing.

src/core/suggestionCore.ts · 108 lines
src/core/suggestionCore.ts108 lines · TypeScript
⋯ 13 lines hidden (lines 1–13)
1/**
2 * Core domain logic for theme prompt suggestions.
3 * Pure functions that provide curated fallback examples and validation.
4 * Following the functional programming principles of the Vibe Themer architecture.
5 */
6 
7import { ThemePromptSuggestion } from '../types/theme.js';
8 
9/**
10 * Curated theme prompt suggestions extracted from README examples.
11 * These showcase Vibe Themer's creative personality and serve as fallback
12 * when AI suggestion generation is unavailable.
13 */
14export const CURATED_SUGGESTIONS: readonly ThemePromptSuggestion[] = [
15 // Primary creative examples from quick start guide
16 {
17 label: "warm sunset over mountains",
18 description: "Golden hour vibes with mountain silhouettes",
19 source: 'curated_fallback'
20 },
21 {
22 label: "minimal dark forest",
23 description: "Clean aesthetic with nature-inspired tones",
24 source: 'curated_fallback'
25 },
26 {
27 label: "vibrant retro 80s",
28 description: "Neon colors and nostalgic energy",
29 source: 'curated_fallback'
30 },
31 {
32 label: "the feeling of finding a $20 bill in old jeans",
33 description: "Unexpected joy and comfort",
34 source: 'curated_fallback'
35 },
36 {
37 label: "existential dread but make it cozy",
38 description: "Philosophical depths with warm comfort",
39 source: 'curated_fallback'
40 }
41] as const;
⋯ 67 lines hidden (lines 42–108)
42 
43/**
44 * Validates that a theme prompt suggestion has required properties.
45 * Pure function that returns boolean result for type safety.
46 */
47export const isValidSuggestion = (suggestion: unknown): suggestion is ThemePromptSuggestion => {
48 if (typeof suggestion !== 'object' || suggestion === null) {
49 return false;
50 }
52 const s = suggestion as Record<string, unknown>;
54 return (
55 typeof s.label === 'string' &&
56 s.label.trim().length > 0 &&
57 (s.description === undefined || typeof s.description === 'string') &&
58 (s.source === 'ai_generated' || s.source === 'curated_fallback')
59 );
60};
61 
62/**
63 * Filters and validates an array of suggestions, removing any invalid entries.
64 * Pure function that ensures type safety and data integrity.
65 */
66export const validateSuggestions = (suggestions: unknown[]): readonly ThemePromptSuggestion[] => {
67 return suggestions.filter(isValidSuggestion);
68};
69 
70/**
71 * Gets a random subset of curated suggestions for variety.
72 * Pure function that takes a seed number for deterministic testing.
73 */
74export const getRandomCuratedSuggestions = (
75 count: number = 6,
76 seed?: number
77): readonly ThemePromptSuggestion[] => {
78 if (count <= 0 || count > CURATED_SUGGESTIONS.length) {
79 return CURATED_SUGGESTIONS;
80 }
82 // Use simple deterministic shuffling if seed provided, otherwise random
83 const shuffled = [...CURATED_SUGGESTIONS];
85 if (seed !== undefined) {
86 // Simple deterministic shuffle using seed
87 for (let i = shuffled.length - 1; i > 0; i--) {
88 const j = (seed + i) % (i + 1);
89 [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
90 }
91 } else {
92 // Random shuffle
93 for (let i = shuffled.length - 1; i > 0; i--) {
94 const j = Math.floor(Math.random() * (i + 1));
95 [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
96 }
97 }
99 return shuffled.slice(0, count);
100};
101 
102/**
103 * Creates a fallback suggestion result for error scenarios.
104 * Pure function that provides graceful degradation with curated examples.
105 */
106export const createFallbackSuggestions = (count: number = 6): readonly ThemePromptSuggestion[] => {
107 return getRandomCuratedSuggestions(count);
108};
Act I · A vibe becomes a theme1.3🎬 State machine

The API key, as a state machine

src/services/openaiCore.ts

Before any generation, ensureOpenAIClient guarantees a usable client. The design is the strongest example of the codebase's type-driven aspirations actually paying off. An API key is not a string — it is an APIKeyState: missing, present, or invalid, each carrying exactly the data that state implies.

The key and client states — invalid combinations are unrepresentable.

src/types/theme.ts · 146 lines
src/types/theme.ts146 lines · TypeScript
⋯ 109 lines hidden (lines 1–109)
1/**
2 * Domain types for theme application in VS Code.
3 * These types encode business rules and make invalid states unrepresentable.
4 */
5 
6import * as vscode from 'vscode';
7import OpenAI from 'openai';
8 
9/**
10 * Represents a syntax token color rule with strong typing.
11 * The scope determines what code elements this applies to.
12 */
13export interface TokenColorRule {
14 readonly scope: string | readonly string[];
15 readonly settings: {
16 readonly foreground?: string;
17 readonly background?: string;
18 readonly fontStyle?: 'italic' | 'bold' | 'underline' | 'none' | string;
19 };
21 
22/**
23 * A complete theme customization containing both UI colors and syntax highlighting.
24 * This is the core domain object representing what we want to apply.
25 */
26export interface ThemeCustomizations {
27 readonly colorCustomizations: Record<string, string>;
28 readonly tokenColors: readonly TokenColorRule[];
29 readonly description: string;
31 
32/**
33 * Configuration scope determines where theme settings are persisted.
34 * The type system enforces that we handle both workspace and global scenarios.
35 */
36export type ConfigurationScope =
37 | { readonly type: 'workspace'; readonly target: vscode.ConfigurationTarget.Workspace }
38 | { readonly type: 'global'; readonly target: vscode.ConfigurationTarget.Global }
39 | { readonly type: 'both'; readonly primary: vscode.ConfigurationTarget; readonly fallback: vscode.ConfigurationTarget };
40 
41/**
42 * Result of a theme application operation.
43 * Success contains the applied scope, failure contains actionable error information.
44 */
45export type ThemeApplicationResult =
46 | { readonly success: true; readonly appliedScope: ConfigurationScope }
47 | { readonly success: false; readonly error: ThemeApplicationError };
48 
49/**
50 * Structured error information for theme application failures.
51 * Provides both user-facing messages and technical details for debugging.
52 */
53export interface ThemeApplicationError {
54 readonly message: string;
55 readonly cause?: unknown;
56 readonly recoverable: boolean;
57 readonly suggestedAction?: string;
59 
60/**
61 * Represents the current state of VS Code theme customizations.
62 * This captures what's currently applied so we can iterate on it.
63 */
64export interface CurrentThemeState {
65 readonly colorCustomizations: Record<string, string>;
66 readonly tokenColorCustomizations: Record<string, unknown>;
67 readonly hasCustomizations: boolean;
68 readonly scope: 'workspace' | 'global' | 'both';
70 
71/**
72 * Result of reading current theme state.
73 * Success contains the current state, failure contains actionable error information.
74 */
75export type CurrentThemeResult =
76 | { readonly success: true; readonly state: CurrentThemeState }
77 | { readonly success: false; readonly error: ThemeApplicationError };
78 
79// =============================================================================
80// Theme Prompt Suggestion Types (ADR-002)
81// =============================================================================
82 
83/**
84 * Represents a creative theme prompt suggestion for user inspiration.
85 * Can be generated by AI or curated from proven examples.
86 */
87export interface ThemePromptSuggestion {
88 readonly label: string;
89 readonly description?: string;
90 readonly source: 'ai_generated' | 'curated_fallback';
92 
93/**
94 * Result of theme prompt suggestion generation.
95 * Success contains suggestions array, failure contains actionable error information.
96 */
97export type ThemePromptSuggestionResult =
98 | { readonly success: true; readonly suggestions: readonly ThemePromptSuggestion[] }
99 | { readonly success: false; readonly error: ThemeApplicationError };
100 
101// =============================================================================
102// OpenAI Domain Types
103// =============================================================================
104 
105/**
106 * Domain types for OpenAI client management.
107 * These types encode business rules and make invalid states unrepresentable.
108 */
109 
110/**
111 * Represents the state of an API key in the system.
112 * Encodes the business rule that we must know the provenance and validity of our API key.
113 */
114export type APIKeyState =
115 | { readonly status: 'missing'; readonly reason?: 'never-set' | 'deleted' }
116 | { readonly status: 'present'; readonly key: string; readonly source: 'storage' | 'user-input' }
117 | { readonly status: 'invalid'; readonly key: string; readonly error: string };
118 
119/**
120 * State of the OpenAI client connection.
121 * Type safety ensures we can only use a client when it's in a valid state.
122 */
123export type OpenAIClientState =
124 | { readonly status: 'uninitialized' }
125 | { readonly status: 'ready'; readonly apiKey: string; readonly model?: string }
126 | { readonly status: 'error'; readonly error: OpenAIServiceError; readonly lastApiKey?: string };
127 
128/**
129 * Result of OpenAI service operations.
130 * Provides structured success/failure information with actionable context.
131 */
132export type OpenAIServiceResult<T = void> =
133 | { readonly success: true; readonly data: T; readonly clientState: OpenAIClientState }
134 | { readonly success: false; readonly error: OpenAIServiceError; readonly clientState: OpenAIClientState };
⋯ 12 lines hidden (lines 135–146)
135 
136/**
137 * Structured error information for OpenAI service failures.
138 * Provides both user-facing messages and technical details for debugging.
139 */
140export interface OpenAIServiceError {
141 readonly message: string; // User-facing error description
142 readonly cause?: unknown; // Technical details for debugging
143 readonly recoverable: boolean; // Can the user retry this operation?
144 readonly suggestedAction?: string; // What should the user do next?
145 readonly errorType: 'api-key-missing' | 'api-key-invalid' | 'client-creation-failed' | 'storage-error' | 'user-cancelled';

initializeOpenAIClient reads that state and drives the flow: try stored key, prompt the user if missing, validate format (sk- + length), then prove the key works with a live models.list() call before trusting it. Only a user-entered key is written to context.secrets — the encrypted store — and only after it validates.

Init: stored key → prompt → format check → live validation → store.

src/services/openaiCore.ts · 424 lines
src/services/openaiCore.ts424 lines · TypeScript
⋯ 238 lines hidden (lines 1–238)
1/**
2 * Core OpenAI client management with integrated orchestration.
3 * Combines pure domain logic with client lifecycle management.
4 *
5 * Design Philosophy:
6 * - Pure functions for domain logic, state management for orchestration
7 * - Rich types that make invalid states unrepresentable
8 * - Functional composition with practical state management
9 * - Explicit error handling with structured failure information
10 */
11 
12import * as vscode from 'vscode';
13import OpenAI from 'openai';
14import {
15 APIKeyState,
16 OpenAIClientState,
17 OpenAIServiceResult,
18 OpenAIServiceError
19} from '../types/theme';
20 
21/**
22 * Determines what action should be taken based on the current API key state.
23 * Encodes the business rule: we need a valid API key to proceed with client creation.
24 *
25 * @param keyState - Current state of the API key
26 * @returns The next action that should be taken
27 */
28export const determineRequiredAction = (keyState: APIKeyState): 'prompt-user' | 'use-existing' | 'error' => {
29 switch (keyState.status) {
30 case 'missing':
31 return 'prompt-user';
32 case 'present':
33 return 'use-existing';
34 case 'invalid':
35 return 'prompt-user'; // Allow user to try a different key
36 default:
37 // TypeScript ensures this is exhaustive - compilation fails if we miss a case
38 const _exhaustive: never = keyState;
39 return _exhaustive;
40 }
41};
42 
43/**
44 * Creates a client state from an API key and OpenAI client.
45 * Transforms low-level client information into our domain model.
46 */
47export const createReadyClientState = (apiKey: string, client: OpenAI): OpenAIClientState => ({
48 status: 'ready',
49 apiKey,
50 // Future enhancement: extract model from client configuration
51 model: undefined
52});
53 
54/**
55 * Creates an error client state with rich context.
56 * Ensures errors contain actionable information for users and developers.
57 */
58export const createErrorClientState = (
59 error: OpenAIServiceError,
60 lastApiKey?: string
61): OpenAIClientState => ({
62 status: 'error',
63 error,
64 lastApiKey
65});
66 
67/**
68 * Creates a structured success result.
69 * Type safety ensures we always provide both data and client state.
70 */
71export const createSuccessResult = <T>(
72 data: T,
73 clientState: OpenAIClientState
74): OpenAIServiceResult<T> => ({
75 success: true,
76 data,
77 clientState
78});
79 
80/**
81 * Creates a structured failure result.
82 * Ensures failures contain actionable error information and current state.
83 */
84export const createFailureResult = <T>(
85 error: OpenAIServiceError,
86 clientState: OpenAIClientState
87): OpenAIServiceResult<T> => ({
88 success: false,
89 error,
90 clientState
91});
92 
93/**
94 * Factory function for creating OpenAI service errors.
95 * Centralizes error creation to ensure consistency and completeness.
96 */
97export const createOpenAIServiceError = (
98 message: string,
99 errorType: OpenAIServiceError['errorType'],
100 options: {
101 cause?: unknown;
102 recoverable?: boolean;
103 suggestedAction?: string;
104 } = {}
105): OpenAIServiceError => ({
106 message,
107 errorType,
108 cause: options.cause,
109 recoverable: options.recoverable ?? true,
110 suggestedAction: options.suggestedAction
111});
112 
113/**
114 * Determines if a client state represents a usable client.
115 * Type-safe way to check if we can proceed with OpenAI operations.
116 */
117export const isClientReady = (state: OpenAIClientState): state is Extract<OpenAIClientState, { status: 'ready' }> => {
118 return state.status === 'ready';
119};
120 
121/**
122 * Extracts the API key from various state representations.
123 * Provides a unified way to get the current API key regardless of state.
124 */
125export const extractApiKey = (keyState: APIKeyState): string | undefined => {
126 switch (keyState.status) {
127 case 'missing':
128 return undefined;
129 case 'present':
130 case 'invalid':
131 return keyState.key;
132 default:
133 const _exhaustive: never = keyState;
134 return _exhaustive;
135 }
136};
137 
138/**
139 * Validates that an API key meets basic format requirements.
140 * Business rule: OpenAI API keys have a specific format we can validate.
141 */
142export const validateApiKeyFormat = (apiKey: string): boolean => {
143 // OpenAI API keys start with "sk-" and have a specific length
144 // This is a basic format check, not authentication validation
145 return apiKey.startsWith('sk-') && apiKey.length > 20;
146};
147 
148/**
149 * Creates an API key state from storage retrieval result.
150 * Transforms storage API results into our domain model.
151 */
152export const createApiKeyStateFromStorage = (
153 storageResult: string | undefined
154): APIKeyState => {
155 if (!storageResult) {
156 return { status: 'missing', reason: 'never-set' };
157 }
159 if (!validateApiKeyFormat(storageResult)) {
160 return {
161 status: 'invalid',
162 key: storageResult,
163 error: 'API key does not match expected format'
164 };
165 }
167 return {
168 status: 'present',
169 key: storageResult,
170 source: 'storage'
171 };
172};
173 
174/**
175 * Creates an API key state from user input.
176 * Handles the case where user provides a new API key.
177 */
178export const createApiKeyStateFromUserInput = (
179 userInput: string | undefined
180): APIKeyState => {
181 if (!userInput) {
182 return { status: 'missing', reason: 'deleted' };
183 }
185 if (!validateApiKeyFormat(userInput)) {
186 return {
187 status: 'invalid',
188 key: userInput,
189 error: 'API key does not match expected format'
190 };
191 }
193 return {
194 status: 'present',
195 key: userInput,
196 source: 'user-input'
197 };
198};
199 
200/**
201 * The OpenAI API key identifier used in secure storage.
202 * Centralized constant to ensure consistency across the application.
203 */
204const OPENAI_API_KEY_STORAGE_KEY = 'openaiApiKey';
205 
206/**
207 * Current client state - encapsulated to prevent direct mutation.
208 * Business rule: client state should only be modified through controlled operations.
209 */
210let currentClientState: OpenAIClientState = { status: 'uninitialized' };
211let currentClient: OpenAI | undefined;
212 
213/**
214 * Validates an OpenAI client by testing connectivity.
215 */
216const validateOpenAIClient = async (client: OpenAI): Promise<boolean> => {
217 try {
218 await client.models.list();
219 return true;
220 } catch (error) {
221 console.log('OpenAI client validation failed:', error);
222 return false;
223 }
224};
225 
226/**
227 * Initializes the OpenAI client with comprehensive error handling and user feedback.
228 * This is the main entry point that orchestrates the entire initialization process.
229 *
230 * Business Logic:
231 * - First attempt to use stored API key (better UX - don't prompt unnecessarily)
232 * - If no key or invalid key, prompt user for input
233 * - Store valid keys for future use
234 * - Provide rich feedback for all scenarios
235 *
236 * @param context - VS Code extension context for storage and UI access
237 * @returns Result indicating success/failure with rich context
238 */
239export const initializeOpenAIClient = async (
240 context: vscode.ExtensionContext
241): Promise<OpenAIServiceResult<OpenAI>> => {
242 try {
243 // Step 1: Check for existing API key in storage
244 const storedKey = await context.secrets.get(OPENAI_API_KEY_STORAGE_KEY);
245 let keyState = createApiKeyStateFromStorage(storedKey);
247 // Step 2: Determine what action to take based on current key state
⋯ 51 lines hidden (lines 248–298)
248 const requiredAction = determineRequiredAction(keyState);
250 if (requiredAction === 'prompt-user') {
251 // Step 3: Get API key from user if needed
252 const userInput = await vscode.window.showInputBox({
253 prompt: 'Enter your OpenAI API Key',
254 ignoreFocusOut: true,
255 password: true,
256 placeHolder: 'sk-...',
257 validateInput: (value) => {
258 if (!value) {return 'API key is required';}
259 if (!value.startsWith('sk-')) {return 'OpenAI API keys start with "sk-"';}
260 if (value.length < 20) {return 'API key appears to be too short';}
261 return undefined;
262 }
263 });
264 keyState = createApiKeyStateFromUserInput(userInput);
266 // User cancelled the prompt
267 if (keyState.status === 'missing') {
268 const error = createOpenAIServiceError(
269 'OpenAI API Key is required for this extension to work',
270 'user-cancelled',
271 {
272 recoverable: true,
273 suggestedAction: 'Please run the command again and provide your API key'
274 }
275 );
277 currentClientState = createErrorClientState(error);
278 await vscode.window.showErrorMessage(error.message, { modal: true });
279 return createFailureResult(error, currentClientState);
280 }
281 }
283 // Step 4: At this point we should have a valid API key
284 const apiKey = extractApiKey(keyState);
285 if (!apiKey) {
286 const error = createOpenAIServiceError(
287 'Unable to obtain valid API key',
288 'api-key-missing',
289 {
290 recoverable: true,
291 suggestedAction: 'Please check your API key format and try again'
292 }
293 );
295 currentClientState = createErrorClientState(error);
296 return createFailureResult(error, currentClientState);
297 }
299 // Step 5: Create and validate the OpenAI client
300 const client = new OpenAI({ apiKey });
301 const isValid = await validateOpenAIClient(client);
303 if (!isValid) {
304 const error = createOpenAIServiceError(
305 'OpenAI API key is invalid or cannot connect to OpenAI services',
306 'api-key-invalid',
307 {
308 recoverable: true,
309 suggestedAction: 'Please verify your API key and check your internet connection'
310 }
311 );
313 currentClientState = createErrorClientState(error, apiKey);
314 await vscode.window.showErrorMessage(error.message, { modal: true });
315 return createFailureResult(error, currentClientState);
316 }
318 // Step 6: Store the key if it came from user input (avoid unnecessary storage writes)
319 if (keyState.status === 'present' && keyState.source === 'user-input') {
320 await context.secrets.store(OPENAI_API_KEY_STORAGE_KEY, apiKey);
321 await vscode.window.showInformationMessage('OpenAI API Key stored successfully!', { modal: true });
322 }
324 // Step 7: Update our state and return success
325 currentClient = client;
326 currentClientState = createReadyClientState(apiKey, client);
328 return createSuccessResult(client, currentClientState);
⋯ 96 lines hidden (lines 329–424)
330 } catch (error) {
331 // Handle unexpected errors with rich context
332 const serviceError = error instanceof Error && 'errorType' in error
333 ? error as any // It's already our domain error
334 : createOpenAIServiceError(
335 'Unexpected error during OpenAI client initialization',
336 'client-creation-failed',
337 {
338 cause: error,
339 recoverable: true,
340 suggestedAction: 'Please try again or restart VS Code'
341 }
342 );
344 currentClientState = createErrorClientState(serviceError);
345 return createFailureResult(serviceError, currentClientState);
346 }
347};
348 
349/**
350 * Ensures the OpenAI client is available and ready for use.
351 * If not initialized, attempts initialization. If already initialized, returns existing client.
352 *
353 * Business Rule: Always provide a client when possible, initialize transparently when needed.
354 *
355 * @param context - VS Code extension context for initialization if needed
356 * @returns Result with client or detailed error information
357 */
358export const ensureOpenAIClient = async (
359 context: vscode.ExtensionContext
360): Promise<OpenAIServiceResult<OpenAI>> => {
361 // If we already have a ready client, return it immediately
362 if (isClientReady(currentClientState) && currentClient) {
363 return createSuccessResult(currentClient, currentClientState);
364 }
366 // Otherwise, initialize the client
367 return await initializeOpenAIClient(context);
368};
369 
370/**
371 * Gets the current OpenAI client if available.
372 * Returns undefined if not initialized - this forces callers to handle the uninitialized case.
373 *
374 * Design Decision: Return undefined rather than throwing to make the uninitialized state explicit.
375 */
376export const getCurrentOpenAIClient = (): OpenAI | undefined => {
377 return isClientReady(currentClientState) ? currentClient : undefined;
378};
379 
380/**
381 * Gets the current client state for inspection.
382 * Useful for displaying status information or debugging.
383 */
384export const getCurrentClientState = (): OpenAIClientState => {
385 return currentClientState;
386};
387 
388/**
389 * Resets the OpenAI client state and clears stored credentials.
390 * Useful for switching API keys or troubleshooting connection issues.
391 *
392 * @param context - VS Code extension context for storage access
393 * @returns Result indicating success/failure of the reset operation
394 */
395export const resetOpenAIClient = async (
396 context: vscode.ExtensionContext
397): Promise<OpenAIServiceResult<void>> => {
398 try {
399 // Clear stored API key
400 await context.secrets.delete(OPENAI_API_KEY_STORAGE_KEY);
402 // Reset internal state
403 currentClient = undefined;
404 currentClientState = { status: 'uninitialized' };
406 await vscode.window.showInformationMessage('OpenAI client reset successfully', { modal: true });
408 return createSuccessResult(undefined, currentClientState);
410 } catch (error) {
411 const serviceError = createOpenAIServiceError(
412 'Failed to reset OpenAI client',
413 'storage-error',
414 {
415 cause: error,
416 recoverable: true,
417 suggestedAction: 'Try restarting VS Code or manually clearing extension data'
418 }
419 );
421 currentClientState = createErrorClientState(serviceError);
422 return createFailureResult(serviceError, currentClientState);
423 }
424};
Act I · A vibe becomes a theme1.4🎬 The product is a prompt

The 1,248-line prompt that is the product

prompts/streamingThemePrompt.txt

The model is given one job and a strict wire format. Emit lines, each tagged COUNT:, SELECTOR:, TOKEN:, or MESSAGE:. The prompt opens by teaching intent detection (generate a new theme vs. iterate on an existing one), then a "painter's workflow" ordering — backgrounds first, fine details last — then an exhaustive catalogue of every VS Code theme selector the model may set.

The wire format and the rules. The whole streaming protocol is defined here, in prose.

prompts/streamingThemePrompt.txt · 1247 lines
prompts/streamingThemePrompt.txt1247 lines · Text only
⋯ 18 lines hidden (lines 1–18)
1You are a professional VS Code theme designer and code editor color expert. Given a theme description, generate a COMPLETE, COMPREHENSIVE theme by outputting settings ONE AT A TIME, streaming each setting on a separate line.
2 
3IMPORTANT: You can either generate a FULL NEW THEME or MODIFY AN EXISTING THEME based on user intent.
4 
5=== INTENT DETECTION ===
6ANALYZE the user's request to determine intent:
7 
8**NEW THEME GENERATION**: Full comprehensive theme (complete theme)
9- User describes a complete theme concept: "cyberpunk neon city", "cozy autumn evening", "minimal dark forest"
10- No current theme context provided
11- Generate ALL available UI elements and token scopes
12 
13**THEME ITERATION**: Incremental modifications to existing theme
14- User requests changes to existing theme: "make it warmer", "darker background", "remove purple accents", "add blue highlights"
15- Current theme context is provided showing existing customizations
16- Generate ONLY the settings that need to change
17- Use REMOVE to clear specific settings
18 
19Output each setting in this exact format:
20COUNT:estimated_total_settings
21SELECTOR:selector_name=color_value
22SELECTOR:selector_name=REMOVE
23TOKEN:scope_name=color_value[,fontStyle]
24TOKEN:scope_name=REMOVE
25MESSAGE:your_engaging_progress_message
26 
27Examples:
28**New Theme:**
29COUNT:125
30SELECTOR:editor.background=#1a1a1a
31MESSAGE:Once upon a midnight dreary, while I coded weak and weary...
32SELECTOR:activityBar.background=#2d2d2d
33TOKEN:comment=#6a9955,italic
34 
35**Theme Iteration:**
36COUNT:15
37MESSAGE:Warming up your theme... 🔥
38SELECTOR:editor.background=#2a1f1a
39SELECTOR:statusBar.background=#3d2e1f
40SELECTOR:activityBarBadge.background=REMOVE
41MESSAGE:Purple accents cleared ✨
42 
43Rules:
441. START with a COUNT line indicating the estimated total number of settings you will generate
452. Use valid hex codes: 6-digit (#rrggbb) for solid colors, or 8-digit (#rrggbbaa) for transparency
463. Use lowercase letters for hex codes
474. For TOKEN lines, fontStyle is optional (italic, bold, underline, none)
485. Use REMOVE to clear existing customizations (falls back to base theme)
496. Output one setting per line
507. DO NOT output any JSON, comments, or explanations
518. Each line must start with either "COUNT:", "SELECTOR:", "TOKEN:", or "MESSAGE:"
529. For NEW themes: Cover ALL available UI areas and token scopes listed below
5310. For ITERATIONS: Only output settings that need to change
5411. Sprinkle MESSAGE lines throughout to create an engaging experience
5512. Messages should reference specific UI components being styled
5613. Be creative with wordplay, rhymes, and humor - think Sims loading screens but themed!
5714. Make users smile while showing progress through actual UI areas being themed
⋯ 1190 lines hidden (lines 58–1247)
58 
59PAINTER'S WORKFLOW - Apply settings in this order:
60 
61**FOR NEW THEMES (comprehensive generation):**
621. FOUNDATION (broad strokes): Main backgrounds and foregrounds
63 - editor.background, foreground
64 - activityBar.background, foreground
65 - sideBar.background, foreground
66 - statusBar.background, foreground
67 
682. MAJOR STRUCTURES (primary elements):
69 - Tab containers and active states
70 - Panel backgrounds and borders
71 - Terminal base colors
72 - Primary input fields and buttons
73 
743. INTERACTIVE STATES (hover, selection, focus):
75 - Selection backgrounds and highlights
76 - Hover states for buttons and lists
77 - Focus borders and active indicators
78 - Dropdown and widget interactions
79 
804. DETAILED REFINEMENTS (fine brushwork):
81 - Secondary buttons and badges
82 - Scrollbars and minimap elements
83 - Error/warning/info indicators
84 - Git decorations and diff colors
85 
865. CODE SYNTAX (syntax highlighting):
87 - Core language elements (keywords, strings, comments)
88 - Functions, variables, and operators
89 - Markup and documentation elements
90 - Invalid code and error highlighting
91 
92**FOR THEME ITERATIONS (targeted changes):**
93- Focus ONLY on the elements mentioned in the user's request
94- Use REMOVE to clear unwanted customizations
95- Be surgical - change only what's needed
96- Maintain consistency with existing theme elements
97 
98COMPLETION CRITERIA:
99**New themes:** Generate settings for ALL UI areas and token scopes listed below
100**Iterations:** Generate only the specific changes requested
101- Do NOT stop until you have provided a truly comprehensive theme (new) or complete iteration (modify)
102- If you're unsure whether to include a setting, INCLUDE IT for completeness (new themes only)
103 
104AVAILABLE THEME COLOR SELECTORS (organized by category):
105 
106=== BASE COLORS ===
107• focusBorder - Overall border color for focused elements
108• foreground - Overall foreground color
109• disabledForeground - Overall foreground for disabled elements
110• widget.border - Border color of widgets such as Find/Replace
111• widget.shadow - Shadow color of widgets
112• selection.background - Background color of text selections in workbench
113• descriptionForeground - Foreground color for description text
114• errorForeground - Overall foreground color for error messages
115• icon.foreground - Default color for icons in the workbench
116• sash.hoverBorder - Hover border color for draggable sashes
117 
118=== CONTRAST COLORS ===
119• contrastActiveBorder - Extra border around active elements for greater contrast
120• contrastBorder - Extra border around elements for greater contrast
121 
122=== WINDOW BORDER ===
123• window.activeBorder - Border color for the active window
124• window.inactiveBorder - Border color for inactive windows
125 
126=== TEXT COLORS ===
127• textBlockQuote.background - Background color for block quotes in text
128• textBlockQuote.border - Border color for block quotes in text
129• textCodeBlock.background - Background color for code blocks in text
130• textLink.activeForeground - Foreground color for links when clicked/hovered
131• textLink.foreground - Foreground color for links in text
132• textPreformat.foreground - Foreground color for preformatted text segments
133• textPreformat.background - Background color for preformatted text segments
134• textSeparator.foreground - Color for text separators
135 
136=== ACTION COLORS ===
137• toolbar.hoverBackground - Toolbar background when hovering over actions
138• toolbar.hoverOutline - Toolbar outline when hovering over actions
139• toolbar.activeBackground - Toolbar background when holding mouse over actions
140• editorActionList.background - Action List background color
141• editorActionList.foreground - Action List foreground color
142• editorActionList.focusForeground - Action List foreground for focused item
143• editorActionList.focusBackground - Action List background for focused item
144 
145=== BUTTON CONTROL ===
146• button.background - Button background color
147• button.foreground - Button foreground color
148• button.border - Button border color
149• button.separator - Button separator color
150• button.hoverBackground - Button background color when hovering
151• button.secondaryForeground - Secondary button foreground color
152• button.secondaryBackground - Secondary button background color
153• button.secondaryHoverBackground - Secondary button background when hovering
154• checkbox.background - Background color of checkbox widget
155• checkbox.foreground - Foreground color of checkbox widget
156• checkbox.disabled.background - Background of disabled checkbox
157• checkbox.disabled.foreground - Foreground of disabled checkbox
158• checkbox.border - Border color of checkbox widget
159• checkbox.selectBackground - Background when checkbox element is selected
160• checkbox.selectBorder - Border when checkbox element is selected
161• radio.activeForeground - Foreground color of active radio option
162• radio.activeBackground - Background color of active radio option
163• radio.activeBorder - Border color of the active radio option
164• radio.inactiveForeground - Foreground color of inactive radio option
165• radio.inactiveBackground - Background color of inactive radio option
166• radio.inactiveBorder - Border color of the inactive radio option
167• radio.inactiveHoverBackground - Background color of inactive radio option when hovering
168 
169=== DROPDOWN CONTROL ===
170• dropdown.background - Dropdown background
171• dropdown.listBackground - Dropdown list background
172• dropdown.border - Dropdown border
173• dropdown.foreground - Dropdown foreground
174 
175=== INPUT CONTROL ===
176• input.background - Input box background
177• input.border - Input box border
178• input.foreground - Input box foreground
179• input.placeholderForeground - Input box placeholder text color
180• inputOption.activeBackground - Background of activated options in input fields
181• inputOption.activeBorder - Border of activated options in input fields
182• inputOption.activeForeground - Foreground of activated options in input fields
183• inputOption.hoverBackground - Background of activated options when hovering
184• inputValidation.errorBackground - Input validation background for error severity
185• inputValidation.errorForeground - Input validation foreground for error severity
186• inputValidation.errorBorder - Input validation border for error severity
187• inputValidation.infoBackground - Input validation background for info severity
188• inputValidation.infoForeground - Input validation foreground for info severity
189• inputValidation.infoBorder - Input validation border for info severity
190• inputValidation.warningBackground - Input validation background for warning severity
191• inputValidation.warningForeground - Input validation foreground for warning severity
192• inputValidation.warningBorder - Input validation border for warning severity
193 
194=== SCROLLBAR CONTROL ===
195• scrollbar.shadow - Scrollbar slider shadow to indicate view is scrolled
196• scrollbarSlider.activeBackground - Scrollbar slider background when clicked
197• scrollbarSlider.background - Scrollbar slider background color
198• scrollbarSlider.hoverBackground - Scrollbar slider background when hovering
199 
200=== BADGE ===
201• badge.foreground - Badge foreground color
202• badge.background - Badge background color
203 
204=== PROGRESS BAR ===
205• progressBar.background - Background color of progress bar for long operations
206 
207=== LISTS AND TREES ===
208• list.activeSelectionBackground - List/Tree background for selected item when active
209• list.activeSelectionForeground - List/Tree foreground for selected item when active
210• list.activeSelectionIconForeground - List/Tree icon foreground for selected item when active
211• list.dropBackground - List/Tree drag and drop background
212• list.focusBackground - List/Tree background for focused item when active
213• list.focusForeground - List/Tree foreground for focused item when active
214• list.focusHighlightForeground - List/Tree foreground of match highlights on focused items
215• list.focusOutline - List/Tree outline for focused item when active
216• list.focusAndSelectionOutline - List/Tree outline for focused selected item when active
217• list.highlightForeground - List/Tree foreground of match highlights when searching
218• list.hoverBackground - List/Tree background when hovering with mouse
219• list.hoverForeground - List/Tree foreground when hovering with mouse
220• list.inactiveSelectionBackground - List/Tree background for selected item when inactive
221• list.inactiveSelectionForeground - List/Tree foreground for selected item when inactive
222• list.inactiveSelectionIconForeground - List/Tree icon foreground for selected item when inactive
223• list.inactiveFocusBackground - List background for focused item when inactive
224• list.inactiveFocusOutline - List/Tree outline for focused item when inactive
225• list.invalidItemForeground - List/Tree foreground for invalid items
226• list.errorForeground - Foreground of list items containing errors
227• list.warningForeground - Foreground of list items containing warnings
228• listFilterWidget.background - List/Tree Filter background when searching
229• listFilterWidget.outline - List/Tree Filter Widget outline when searching
230• listFilterWidget.noMatchesOutline - List/Tree Filter Widget outline when no matches
231• listFilterWidget.shadow - Shadow color of type filter widget
232• list.filterMatchBackground - Background of filtered matches in lists and trees
233• list.filterMatchBorder - Border of filtered matches in lists and trees
234• list.deemphasizedForeground - List/Tree foreground for deemphasized items
235• list.dropBetweenBackground - List/Tree drag and drop border between items
236• tree.indentGuidesStroke - Tree Widget stroke color for indent guides
237• tree.inactiveIndentGuidesStroke - Tree stroke color for inactive indentation guides
238• tree.tableColumnsBorder - Tree stroke color for indentation guides
239• tree.tableOddRowsBackground - Background color for odd table rows
240 
241=== ACTIVITY BAR ===
242• activityBar.background - Activity Bar background color
243• activityBar.dropBorder - Drag and drop feedback color for activity bar items
244• activityBar.foreground - Activity Bar foreground color (for icons)
245• activityBar.inactiveForeground - Activity Bar item foreground when inactive
246• activityBar.border - Activity Bar border color with Side Bar
247• activityBarBadge.background - Activity notification badge background color
248• activityBarBadge.foreground - Activity notification badge foreground color
249• activityBar.activeBorder - Activity Bar active indicator border color
250• activityBar.activeBackground - Activity Bar optional background for active element
251• activityBar.activeFocusBorder - Activity bar focus border color for active item
252• activityBarTop.foreground - Active foreground color when activity bar is on top
253• activityBarTop.activeBorder - Focus border color for active item when activity bar is on top
254• activityBarTop.inactiveForeground - Inactive foreground color when activity bar is on top
255• activityBarTop.dropBorder - Drag and drop feedback color when activity bar is on top
256• activityBarTop.background - Background color when activity bar is set to top/bottom
257• activityBarTop.activeBackground - Background for active item when activity bar is on top/bottom
258• activityWarningBadge.foreground - Foreground color of the warning activity badge
259• activityWarningBadge.background - Background color of the warning activity badge
260• activityErrorBadge.foreground - Foreground color of the error activity badge
261• activityErrorBadge.background - Background color of the error activity badge
262 
263=== PROFILES ===
264• profileBadge.background - Profile badge background color on settings gear icon
265• profileBadge.foreground - Profile badge foreground color on settings gear icon
266• profiles.sashBorder - Color of the Profiles editor splitview sash border
267 
268=== SIDE BAR ===
269• sideBar.background - Side Bar background color
270• sideBar.foreground - Side Bar foreground color
271• sideBar.border - Side Bar border color on the side separating the editor
272• sideBar.dropBackground - Drag and drop feedback color for side bar sections
273• sideBarTitle.foreground - Side Bar title foreground color
274• sideBarTitle.background - Side bar title background color
275• sideBarTitle.border - Side bar title border color on bottom
276• sideBarSectionHeader.background - Side Bar section header background color
277• sideBarSectionHeader.foreground - Side Bar section header foreground color
278• sideBarSectionHeader.border - Side bar section header border color
279• sideBarActivityBarTop.border - Border color between activity bar at top/bottom and views
280• sideBarStickyScroll.background - Background color of sticky scroll in side bar
281• sideBarStickyScroll.border - Border color of sticky scroll in side bar
282• sideBarStickyScroll.shadow - Shadow color of sticky scroll in side bar
283 
284=== MINIMAP ===
285• minimap.findMatchHighlight - Highlight color for matches from search within files
286• minimap.selectionHighlight - Highlight color for the editor selection
287• minimap.errorHighlight - Highlight color for errors within the editor
288• minimap.warningHighlight - Highlight color for warnings within the editor
289• minimap.background - Minimap background color
290• minimap.selectionOccurrenceHighlight - Minimap marker color for repeating editor selections
291• minimap.foregroundOpacity - Opacity of foreground elements rendered in minimap
292• minimap.infoHighlight - Minimap marker color for infos
293• minimap.chatEditHighlight - Color of pending edit regions in minimap
294• minimapSlider.background - Minimap slider background color
295• minimapSlider.hoverBackground - Minimap slider background color when hovering
296• minimapSlider.activeBackground - Minimap slider background color when clicked
297• minimapGutter.addedBackground - Minimap gutter color for added content
298• minimapGutter.modifiedBackground - Minimap gutter color for modified content
299• minimapGutter.deletedBackground - Minimap gutter color for deleted content
300• editorMinimap.inlineChatInserted - Minimap marker color for inline chat inserted content
301 
302=== EDITOR GROUPS & TABS ===
303• editorGroup.border - Color to separate multiple editor groups from each other
304• editorGroup.dropBackground - Background color when dragging editors around
305• editorGroup.emptyBackground - Background color of an empty editor group
306• editorGroup.focusedEmptyBorder - Border color of an empty editor group that is focused
307• editorGroup.dropIntoPromptForeground - Foreground color of text shown over editors when dragging files
308• editorGroup.dropIntoPromptBackground - Background color of text shown over editors when dragging files
309• editorGroup.dropIntoPromptBorder - Border color of text shown over editors when dragging files
310• editorGroupHeader.noTabsBackground - Background when using single Tab mode
311• editorGroupHeader.tabsBackground - Background color of the Tabs container
312• editorGroupHeader.tabsBorder - Border color below editor tabs control when tabs enabled
313• editorGroupHeader.border - Border color between editor group header and editor
314• tab.activeBackground - Active Tab background color in an active group
315• tab.unfocusedActiveBackground - Active Tab background color in an inactive editor group
316• tab.activeForeground - Active Tab foreground color in an active group
317• tab.border - Border to separate Tabs from each other
318• tab.activeBorder - Bottom border for the active tab
319• tab.selectedBorderTop - Border to the top of a selected tab
320• tab.selectedBackground - Background of a selected tab
321• tab.selectedForeground - Foreground of a selected tab
322• tab.dragAndDropBorder - Border between tabs to indicate tab insertion
323• tab.unfocusedActiveBorder - Bottom border for active tab in inactive editor group
324• tab.activeBorderTop - Top border for the active tab
325• tab.unfocusedActiveBorderTop - Top border for active tab in inactive editor group
326• tab.lastPinnedBorder - Border on right of last pinned editor to separate from unpinned
327• tab.inactiveBackground - Inactive Tab background color
328• tab.unfocusedInactiveBackground - Inactive Tab background color in unfocused group
329• tab.inactiveForeground - Inactive Tab foreground color in active group
330• tab.unfocusedActiveForeground - Active tab foreground color in inactive editor group
331• tab.unfocusedInactiveForeground - Inactive tab foreground color in inactive editor group
332• tab.hoverBackground - Tab background color when hovering
333• tab.unfocusedHoverBackground - Tab background color in unfocused group when hovering
334• tab.hoverForeground - Tab foreground color when hovering
335• tab.unfocusedHoverForeground - Tab foreground color in unfocused group when hovering
336• tab.hoverBorder - Border to highlight tabs when hovering
337• tab.unfocusedHoverBorder - Border to highlight tabs in unfocused group when hovering
338• tab.activeModifiedBorder - Border on top of modified active tabs in active group
339• tab.inactiveModifiedBorder - Border on top of modified inactive tabs in active group
340• tab.unfocusedActiveModifiedBorder - Border on top of modified active tabs in unfocused group
341• tab.unfocusedInactiveModifiedBorder - Border on top of modified inactive tabs in unfocused group
342• editorPane.background - Background color of editor pane visible on left and right of centered editor layout
343• sideBySideEditor.horizontalBorder - Color to separate two editors when shown side by side from top to bottom
344• sideBySideEditor.verticalBorder - Color to separate two editors when shown side by side from left to right
345 
346=== EDITOR COLORS ===
347• editor.background - Editor background color
348• editor.foreground - Editor default foreground color
349• editorLineNumber.foreground - Color of editor line numbers
350• editorLineNumber.activeForeground - Color of the active editor line number
351• editorLineNumber.dimmedForeground - Color of final editor line when renderFinalNewline is dimmed
352• editorCursor.background - Background color of the editor cursor
353• editorCursor.foreground - Color of the editor cursor
354• editorMultiCursor.primary.foreground - Color of primary editor cursor when multiple cursors present
355• editorMultiCursor.primary.background - Background color of primary editor cursor when multiple cursors present
356• editorMultiCursor.secondary.foreground - Color of secondary editor cursors when multiple cursors present
357• editorMultiCursor.secondary.background - Background color of secondary editor cursors when multiple cursors present
358• editor.placeholder.foreground - Foreground color of placeholder text in editor
359• editor.compositionBorder - Border color for an IME composition
360• editor.selectionBackground - Color of the editor selection
361• editor.selectionForeground - Color of the selected text for high contrast
362• editor.inactiveSelectionBackground - Color of selection in inactive editor
363• editor.selectionHighlightBackground - Color for regions with same content as selection
364• editor.selectionHighlightBorder - Border color for regions with same content as selection
365• editor.wordHighlightBackground - Background color of symbol during read-access
366• editor.wordHighlightBorder - Border color of symbol during read-access
367• editor.wordHighlightStrongBackground - Background color of symbol during write-access
368• editor.wordHighlightStrongBorder - Border color of symbol during write-access
369• editor.wordHighlightTextBackground - Background color of textual occurrence for symbol
370• editor.wordHighlightTextBorder - Border color of textual occurrence for symbol
371• editor.findMatchBackground - Color of the current search match
372• editor.findMatchForeground - Text color of the current search match
373• editor.findMatchHighlightForeground - Foreground color of the other search matches
374• editor.findMatchHighlightBackground - Color of the other search matches
375• editor.findRangeHighlightBackground - Color the range limiting the search
376• editor.findMatchBorder - Border color of the current search match
377• editor.findMatchHighlightBorder - Border color of the other search matches
378• editor.findRangeHighlightBorder - Border color the range limiting the search
379• search.resultsInfoForeground - Color of text in search viewlet's completion message
380• searchEditor.findMatchBackground - Color of the editor's results
381• searchEditor.findMatchBorder - Border color of the editor's results
382• searchEditor.textInputBorder - Search editor text input box border
383• editor.hoverHighlightBackground - Highlight below the word for which a hover is shown
384• editor.lineHighlightBackground - Background color for highlight of line at cursor position
385• editor.lineHighlightBorder - Background color for border around line at cursor position
386• editorWatermark.foreground - Foreground color for labels in editor watermark
387• editorUnicodeHighlight.border - Border color used to highlight unicode characters
388• editorUnicodeHighlight.background - Background color used to highlight unicode characters
389• editorLink.activeForeground - Color of active links
390• editor.rangeHighlightBackground - Background color of highlighted ranges
391• editor.rangeHighlightBorder - Background color of border around highlighted ranges
392• editor.symbolHighlightBackground - Background color of highlighted symbol
393• editor.symbolHighlightBorder - Background color of border around highlighted symbols
394• editorWhitespace.foreground - Color of whitespace characters in the editor
395• editorIndentGuide.background - Color of the editor indentation guides
396• editorIndentGuide.background1 - Color of the editor indentation guides (1)
397• editorIndentGuide.background2 - Color of the editor indentation guides (2)
398• editorIndentGuide.background3 - Color of the editor indentation guides (3)
399• editorIndentGuide.background4 - Color of the editor indentation guides (4)
400• editorIndentGuide.background5 - Color of the editor indentation guides (5)
401• editorIndentGuide.background6 - Color of the editor indentation guides (6)
402• editorIndentGuide.activeBackground - Color of the active editor indentation guide
403• editorIndentGuide.activeBackground1 - Color of the active editor indentation guides (1)
404• editorIndentGuide.activeBackground2 - Color of the active editor indentation guides (2)
405• editorIndentGuide.activeBackground3 - Color of the active editor indentation guides (3)
406• editorIndentGuide.activeBackground4 - Color of the active editor indentation guides (4)
407• editorIndentGuide.activeBackground5 - Color of the active editor indentation guides (5)
408• editorIndentGuide.activeBackground6 - Color of the active editor indentation guides (6)
409• editorInlayHint.background - Background color of inline hints
410• editorInlayHint.foreground - Foreground color of inline hints
411• editorInlayHint.typeForeground - Foreground color of inline hints for types
412• editorInlayHint.typeBackground - Background color of inline hints for types
413• editorInlayHint.parameterForeground - Foreground color of inline hints for parameters
414• editorInlayHint.parameterBackground - Background color of inline hints for parameters
415• editorRuler.foreground - Color of the editor rulers
416• editor.linkedEditingBackground - Background color when editor is in linked editing mode
417• editorCodeLens.foreground - Foreground color of an editor CodeLens
418• editorLightBulb.foreground - Color used for the lightbulb actions icon
419• editorLightBulbAutoFix.foreground - Color used for the lightbulb auto fix actions icon
420• editorLightBulbAi.foreground - Color used for the lightbulb AI icon
421• editorBracketMatch.background - Background color behind matching brackets
422• editorBracketMatch.border - Color for matching brackets boxes
423• editorBracketHighlight.foreground1 - Foreground color of brackets (1)
424• editorBracketHighlight.foreground2 - Foreground color of brackets (2)
425• editorBracketHighlight.foreground3 - Foreground color of brackets (3)
426• editorBracketHighlight.foreground4 - Foreground color of brackets (4)
427• editorBracketHighlight.foreground5 - Foreground color of brackets (5)
428• editorBracketHighlight.foreground6 - Foreground color of brackets (6)
429• editorBracketHighlight.unexpectedBracket.foreground - Foreground color of unexpected brackets
430• editorBracketPairGuide.activeBackground1 - Background color of active bracket pair guides (1)
431• editorBracketPairGuide.activeBackground2 - Background color of active bracket pair guides (2)
432• editorBracketPairGuide.activeBackground3 - Background color of active bracket pair guides (3)
433• editorBracketPairGuide.activeBackground4 - Background color of active bracket pair guides (4)
434• editorBracketPairGuide.activeBackground5 - Background color of active bracket pair guides (5)
435• editorBracketPairGuide.activeBackground6 - Background color of active bracket pair guides (6)
436• editorBracketPairGuide.background1 - Background color of inactive bracket pair guides (1)
437• editorBracketPairGuide.background2 - Background color of inactive bracket pair guides (2)
438• editorBracketPairGuide.background3 - Background color of inactive bracket pair guides (3)
439• editorBracketPairGuide.background4 - Background color of inactive bracket pair guides (4)
440• editorBracketPairGuide.background5 - Background color of inactive bracket pair guides (5)
441• editorBracketPairGuide.background6 - Background color of inactive bracket pair guides (6)
442• editor.foldBackground - Background color for folded ranges
443• editor.foldPlaceholderForeground - Color of collapsed text after first line of folded range
444• editorOverviewRuler.background - Background color of editor overview ruler
445• editorOverviewRuler.border - Color of the overview ruler border
446• editorOverviewRuler.findMatchForeground - Overview ruler marker color for find matches
447• editorOverviewRuler.rangeHighlightForeground - Overview ruler marker color for highlighted ranges
448• editorOverviewRuler.selectionHighlightForeground - Overview ruler marker color for selection highlights
449• editorOverviewRuler.wordHighlightForeground - Overview ruler marker color for symbol highlights
450• editorOverviewRuler.wordHighlightStrongForeground - Overview ruler marker color for write-access symbol highlights
451• editorOverviewRuler.wordHighlightTextForeground - Overview ruler marker color of textual occurrence for symbol
452• editorOverviewRuler.modifiedForeground - Overview ruler marker color for modified content
453• editorOverviewRuler.addedForeground - Overview ruler marker color for added content
454• editorOverviewRuler.deletedForeground - Overview ruler marker color for deleted content
455• editorOverviewRuler.errorForeground - Overview ruler marker color for errors
456• editorOverviewRuler.warningForeground - Overview ruler marker color for warnings
457• editorOverviewRuler.infoForeground - Overview ruler marker color for infos
458• editorOverviewRuler.bracketMatchForeground - Overview ruler marker color for matching brackets
459• editorOverviewRuler.inlineChatInserted - Overview ruler marker color for inline chat inserted content
460• editorOverviewRuler.inlineChatRemoved - Overview ruler marker color for inline chat removed content
461• editorError.foreground - Foreground color of error squiggles in the editor
462• editorError.border - Border color of error boxes in the editor
463• editorError.background - Background color of error text in the editor
464• editorWarning.foreground - Foreground color of warning squiggles in the editor
465• editorWarning.border - Border color of warning boxes in the editor
466• editorWarning.background - Background color of warning text in the editor
467• editorInfo.foreground - Foreground color of info squiggles in the editor
468• editorInfo.border - Border color of info boxes in the editor
469• editorInfo.background - Background color of info text in the editor
470• editorHint.foreground - Foreground color of hints in the editor
471• editorHint.border - Border color of hint boxes in the editor
472• problemsErrorIcon.foreground - Color used for the problems error icon
473• problemsWarningIcon.foreground - Color used for the problems warning icon
474• problemsInfoIcon.foreground - Color used for the problems info icon
475• editorUnnecessaryCode.border - Border color of unnecessary source code in editor
476• editorUnnecessaryCode.opacity - Opacity of unnecessary source code in editor
477• editorGutter.background - Background color of the editor gutter
478• editorGutter.modifiedBackground - Editor gutter background color for lines that are modified
479• editorGutter.modifiedSecondaryBackground - Editor gutter secondary background color for lines that are modified
480• editorGutter.addedBackground - Editor gutter background color for lines that are added
481• editorGutter.addedSecondaryBackground - Editor gutter secondary background color for lines that are added
482• editorGutter.deletedBackground - Editor gutter background color for lines that are deleted
483• editorGutter.deletedSecondaryBackground - Editor gutter secondary background color for lines that are deleted
484• editorGutter.commentRangeForeground - Editor gutter decoration color for commenting ranges
485• editorGutter.commentGlyphForeground - Editor gutter decoration color for commenting glyphs
486• editorGutter.commentUnresolvedGlyphForeground - Editor gutter decoration color for commenting glyphs for unresolved comment threads
487• editorGutter.foldingControlForeground - Color of the folding control in the editor gutter
488• editorGutter.itemGlyphForeground - Editor gutter decoration color for gutter item glyphs
489• editorGutter.itemBackground - Editor gutter decoration color for gutter item background
490• editorCommentsWidget.resolvedBorder - Color of borders and arrow for resolved comments
491• editorCommentsWidget.unresolvedBorder - Color of borders and arrow for unresolved comments
492• editorCommentsWidget.rangeBackground - Color of background for comment ranges
493• editorCommentsWidget.rangeActiveBackground - Color of background for currently selected or hovered comment range
494• editorCommentsWidget.replyInputBackground - Background color for comment reply input box
495• activityBar.activeFocusBorder - Activity bar focus border for active item
496• activityBarTop.foreground - Active foreground when activity bar is on top
497• activityBarTop.activeBorder - Focus border for active item when on top
498• activityBarTop.inactiveForeground - Inactive foreground when activity bar is on top
499• activityBarTop.dropBorder - Drag and drop feedback when activity bar is on top
500• activityBarTop.background - Background when activity bar is set to top/bottom
501• activityBarTop.activeBackground - Background for active item when activity bar is on top/bottom
502 
503=== PROFILES ===
504• profileBadge.background - Profile badge background color
505• profileBadge.foreground - Profile badge foreground color
506• profiles.sashBorder - Color of Profiles editor splitview sash border
507 
508=== SIDE BAR ===
509• sideBar.background - Side Bar background color
510• sideBar.foreground - Side Bar foreground color
511• sideBar.border - Side Bar border color separating from editor
512• sideBar.dropBackground - Drag and drop feedback for side bar sections
513• sideBarTitle.foreground - Side Bar title foreground color
514• sideBarSectionHeader.background - Side Bar section header background
515• sideBarSectionHeader.foreground - Side Bar section header foreground
516• sideBarSectionHeader.border - Side bar section header border color
517• sideBarActivityBarTop.border - Border between activity bar and views when on top
518• sideBarTitle.background - Side bar title background color
519• sideBarTitle.border - Side bar title border separating from views
520• sideBarStickyScroll.background - Background of sticky scroll in side bar
521• sideBarStickyScroll.border - Border of sticky scroll in side bar
522• sideBarStickyScroll.shadow - Shadow of sticky scroll in side bar
523 
524=== MINIMAP ===
525• minimap.findMatchHighlight - Highlight color for search matches in minimap
526• minimap.selectionHighlight - Highlight color for editor selection in minimap
527• minimap.errorHighlight - Highlight color for errors in minimap
528• minimap.warningHighlight - Highlight color for warnings in minimap
529• minimap.background - Minimap background color
530• minimap.selectionOccurrenceHighlight - Minimap marker for repeating selections
531• minimap.foregroundOpacity - Opacity of foreground elements in minimap
532• minimap.infoHighlight - Minimap marker color for infos
533• minimapSlider.background - Minimap slider background color
534• minimapSlider.hoverBackground - Minimap slider background when hovering
535• minimapSlider.activeBackground - Minimap slider background when clicked
536• minimapGutter.addedBackground - Minimap gutter color for added content
537• minimapGutter.modifiedBackground - Minimap gutter color for modified content
538• minimapGutter.deletedBackground - Minimap gutter color for deleted content
539 
540=== EDITOR GROUPS & TABS ===
541• editorGroup.border - Color to separate multiple editor groups
542• editorGroup.dropBackground - Background when dragging editors around
543• editorGroupHeader.noTabsBackground - Background when using single tab
544• editorGroupHeader.tabsBackground - Background of the Tabs container
545• editorGroupHeader.tabsBorder - Border below editor tabs control
546• editorGroupHeader.border - Border between editor group header and editor
547• editorGroup.emptyBackground - Background of empty editor group
548• editorGroup.focusedEmptyBorder - Border of empty editor group that is focused
549• editorGroup.dropIntoPromptForeground - Foreground of text when dragging files
550• editorGroup.dropIntoPromptBackground - Background of text when dragging files
551• editorGroup.dropIntoPromptBorder - Border of text when dragging files
552• tab.activeBackground - Active Tab background in active group
553• tab.unfocusedActiveBackground - Active Tab background in inactive group
554• tab.activeForeground - Active Tab foreground in active group
555• tab.border - Border to separate Tabs from each other
556• tab.activeBorder - Bottom border for active tab
557• tab.selectedBorderTop - Border to top of selected tab
558• tab.selectedBackground - Background of selected tab
559• tab.selectedForeground - Foreground of selected tab
560• tab.dragAndDropBorder - Border between tabs during drag and drop
561• tab.unfocusedActiveBorder - Bottom border for active tab in inactive group
562• tab.activeBorderTop - Top border for active tab
563• tab.unfocusedActiveBorderTop - Top border for active tab in inactive group
564• tab.lastPinnedBorder - Border on right of last pinned editor
565• tab.inactiveBackground - Inactive Tab background color
566• tab.unfocusedInactiveBackground - Inactive Tab background in unfocused group
567• tab.inactiveForeground - Inactive Tab foreground in active group
568• tab.unfocusedActiveForeground - Active tab foreground in inactive group
569• tab.unfocusedInactiveForeground - Inactive tab foreground in inactive group
570• tab.hoverBackground - Tab background when hovering
571• tab.unfocusedHoverBackground - Tab background in unfocused group when hovering
572• tab.hoverForeground - Tab foreground when hovering
573• tab.unfocusedHoverForeground - Tab foreground in unfocused group when hovering
574• tab.hoverBorder - Border to highlight tabs when hovering
575• tab.unfocusedHoverBorder - Border to highlight tabs in unfocused group when hovering
576• tab.activeModifiedBorder - Border on top of modified active tabs in active group
577• tab.inactiveModifiedBorder - Border on top of modified inactive tabs in active group
578• tab.unfocusedActiveModifiedBorder - Border on top of modified active tabs in unfocused group
579• tab.unfocusedInactiveModifiedBorder - Border on top of modified inactive tabs in unfocused group
580• editorPane.background - Background of editor pane in centered layout
581• sideBySideEditor.horizontalBorder - Color to separate editors top to bottom
582• sideBySideEditor.verticalBorder - Color to separate editors left to right
583 
584=== EDITOR COLORS ===
585• editor.background - Editor background color
586• editor.foreground - Editor default foreground color
587• editorLineNumber.foreground - Color of editor line numbers
588• editorLineNumber.activeForeground - Color of active editor line number
589• editorLineNumber.dimmedForeground - Color of final line when renderFinalNewline is dimmed
590• editorCursor.background - Background color of editor cursor
591• editorCursor.foreground - Color of editor cursor
592• editorMultiCursor.primary.foreground - Color of primary cursor when multiple cursors
593• editorMultiCursor.primary.background - Background of primary cursor when multiple cursors
594• editorMultiCursor.secondary.foreground - Color of secondary cursors when multiple cursors
595• editorMultiCursor.secondary.background - Background of secondary cursors when multiple cursors
596• editor.placeholder.foreground - Foreground color of placeholder text
597• editor.compositionBorder - Border color for IME composition
598• editor.selectionBackground - Color of editor selection
599• editor.selectionForeground - Color of selected text for high contrast
600• editor.inactiveSelectionBackground - Color of selection in inactive editor
601• editor.selectionHighlightBackground - Color for regions with same content as selection
602• editor.selectionHighlightBorder - Border color for regions with same content as selection
603• editor.wordHighlightBackground - Background of symbol during read-access
604• editor.wordHighlightBorder - Border of symbol during read-access
605• editor.wordHighlightStrongBackground - Background of symbol during write-access
606• editor.wordHighlightStrongBorder - Border of symbol during write-access
607• editor.wordHighlightTextBackground - Background of textual occurrence for symbol
608• editor.wordHighlightTextBorder - Border of textual occurrence for symbol
609• editor.findMatchBackground - Color of current search match
610• editor.findMatchForeground - Text color of current search match
611• editor.findMatchHighlightForeground - Foreground color of other search matches
612• editor.findMatchHighlightBackground - Color of other search matches
613• editor.findRangeHighlightBackground - Color limiting search range
614• editor.findMatchBorder - Border color of current search match
615• editor.findMatchHighlightBorder - Border color of other search matches
616• editor.findRangeHighlightBorder - Border color limiting search range
617• search.resultsInfoForeground - Color of text in search completion message
618• searchEditor.findMatchBackground - Color of search editor results
619• searchEditor.findMatchBorder - Border color of search editor results
620• searchEditor.textInputBorder - Search editor text input box border
621• editor.hoverHighlightBackground - Highlight behind symbol for hover
622• editor.lineHighlightBackground - Background highlight of line at cursor
623• editor.lineHighlightBorder - Border around line at cursor position
624• editorWatermark.foreground - Foreground color for editor watermark labels
625• editorUnicodeHighlight.border - Border color for unicode character highlights
626• editorUnicodeHighlight.background - Background color for unicode character highlights
627• editorLink.activeForeground - Color of active links
628• editor.rangeHighlightBackground - Background of highlighted ranges
629• editor.rangeHighlightBorder - Border around highlighted ranges
630• editor.symbolHighlightBackground - Background of highlighted symbol
631• editor.symbolHighlightBorder - Border around highlighted symbols
632• editorWhitespace.foreground - Color of whitespace characters
633• editorIndentGuide.background - Color of editor indentation guides
634• editorIndentGuide.background1 - Color of editor indentation guides (1)
635• editorIndentGuide.background2 - Color of editor indentation guides (2)
636• editorIndentGuide.background3 - Color of editor indentation guides (3)
637• editorIndentGuide.background4 - Color of editor indentation guides (4)
638• editorIndentGuide.background5 - Color of editor indentation guides (5)
639• editorIndentGuide.background6 - Color of editor indentation guides (6)
640• editorIndentGuide.activeBackground - Color of active editor indentation guide
641• editorIndentGuide.activeBackground1 - Color of active editor indentation guides (1)
642• editorIndentGuide.activeBackground2 - Color of active editor indentation guides (2)
643• editorIndentGuide.activeBackground3 - Color of active editor indentation guides (3)
644• editorIndentGuide.activeBackground4 - Color of active editor indentation guides (4)
645• editorIndentGuide.activeBackground5 - Color of active editor indentation guides (5)
646• editorIndentGuide.activeBackground6 - Color of active editor indentation guides (6)
647• editorInlayHint.background - Background color of inline hints
648• editorInlayHint.foreground - Foreground color of inline hints
649• editorInlayHint.typeForeground - Foreground color of inline hints for types
650• editorInlayHint.typeBackground - Background color of inline hints for types
651• editorInlayHint.parameterForeground - Foreground color of inline hints for parameters
652• editorInlayHint.parameterBackground - Background color of inline hints for parameters
653• editorRuler.foreground - Color of editor rulers
654• editor.linkedEditingBackground - Background color when editor is in linked editing mode
655• editorCodeLens.foreground - Foreground color of editor CodeLens
656• editorLightBulb.foreground - Color for lightbulb actions icon
657• editorLightBulbAutoFix.foreground - Color for lightbulb auto fix actions icon
658• editorLightBulbAi.foreground - Color for lightbulb AI icon
659• editorBracketMatch.background - Background color behind matching brackets
660• editorBracketMatch.border - Color for matching brackets boxes
661• editorBracketHighlight.foreground1 - Foreground color of brackets (1)
662• editorBracketHighlight.foreground2 - Foreground color of brackets (2)
663• editorBracketHighlight.foreground3 - Foreground color of brackets (3)
664• editorBracketHighlight.foreground4 - Foreground color of brackets (4)
665• editorBracketHighlight.foreground5 - Foreground color of brackets (5)
666• editorBracketHighlight.foreground6 - Foreground color of brackets (6)
667• editorBracketHighlight.unexpectedBracket.foreground - Foreground color of unexpected brackets
668• editorBracketPairGuide.activeBackground1 - Background of active bracket pair guides (1)
669• editorBracketPairGuide.activeBackground2 - Background of active bracket pair guides (2)
670• editorBracketPairGuide.activeBackground3 - Background of active bracket pair guides (3)
671• editorBracketPairGuide.activeBackground4 - Background of active bracket pair guides (4)
672• editorBracketPairGuide.activeBackground5 - Background of active bracket pair guides (5)
673• editorBracketPairGuide.activeBackground6 - Background of active bracket pair guides (6)
674• editorBracketPairGuide.background1 - Background of inactive bracket pair guides (1)
675• editorBracketPairGuide.background2 - Background of inactive bracket pair guides (2)
676• editorBracketPairGuide.background3 - Background of inactive bracket pair guides (3)
677• editorBracketPairGuide.background4 - Background of inactive bracket pair guides (4)
678• editorBracketPairGuide.background5 - Background of inactive bracket pair guides (5)
679• editorBracketPairGuide.background6 - Background of inactive bracket pair guides (6)
680• editor.foldBackground - Background color for folded ranges
681• editor.foldPlaceholderForeground - Color of collapsed text after first line of folded range
682• editorOverviewRuler.background - Background of editor overview ruler
683• editorOverviewRuler.border - Color of overview ruler border
684• editorOverviewRuler.findMatchForeground - Overview ruler marker for find matches
685• editorOverviewRuler.rangeHighlightForeground - Overview ruler marker for highlighted ranges
686• editorOverviewRuler.selectionHighlightForeground - Overview ruler marker for selection highlights
687• editorOverviewRuler.wordHighlightForeground - Overview ruler marker for symbol highlights
688• editorOverviewRuler.wordHighlightStrongForeground - Overview ruler marker for write-access symbol highlights
689• editorOverviewRuler.wordHighlightTextForeground - Overview ruler marker for textual occurrence
690• editorOverviewRuler.modifiedForeground - Overview ruler marker for modified content
691• editorOverviewRuler.addedForeground - Overview ruler marker for added content
692• editorOverviewRuler.deletedForeground - Overview ruler marker for deleted content
693• editorOverviewRuler.errorForeground - Overview ruler marker for errors
694• editorOverviewRuler.warningForeground - Overview ruler marker for warnings
695• editorOverviewRuler.infoForeground - Overview ruler marker for infos
696• editorOverviewRuler.bracketMatchForeground - Overview ruler marker for matching brackets
697• editorError.foreground - Foreground color of error squiggles
698• editorError.border - Border color of error boxes
699• editorError.background - Background color of error text
700• editorWarning.foreground - Foreground color of warning squiggles
701• editorWarning.border - Border color of warning boxes
702• editorWarning.background - Background color of warning text
703• editorInfo.foreground - Foreground color of info squiggles
704• editorInfo.border - Border color of info boxes
705• editorInfo.background - Background color of info text
706• editorHint.foreground - Foreground color of hints
707• editorHint.border - Border color of hint boxes
708• problemsErrorIcon.foreground - Color for problems error icon
709• problemsWarningIcon.foreground - Color for problems warning icon
710• problemsInfoIcon.foreground - Color for problems info icon
711• editorUnnecessaryCode.border - Border color of unnecessary source code
712• editorUnnecessaryCode.opacity - Opacity of unnecessary source code
713• editorGutter.background - Background color of editor gutter
714• editorGutter.modifiedBackground - Editor gutter background for modified lines
715• editorGutter.modifiedSecondaryBackground - Editor gutter secondary background for modified lines
716• editorGutter.addedBackground - Editor gutter background for added lines
717• editorGutter.addedSecondaryBackground - Editor gutter secondary background for added lines
718• editorGutter.deletedBackground - Editor gutter background for deleted lines
719• editorGutter.deletedSecondaryBackground - Editor gutter secondary background for deleted lines
720• editorGutter.commentRangeForeground - Editor gutter decoration for commenting ranges
721• editorGutter.commentGlyphForeground - Editor gutter decoration for commenting glyphs
722• editorGutter.commentUnresolvedGlyphForeground - Editor gutter decoration for unresolved comment glyphs
723• editorGutter.foldingControlForeground - Color of folding control in editor gutter
724• editorCommentsWidget.resolvedBorder - Color of borders and arrow for resolved comments
725• editorCommentsWidget.unresolvedBorder - Color of borders and arrow for unresolved comments
726• editorCommentsWidget.rangeBackground - Color of background for comment ranges
727• editorCommentsWidget.rangeActiveBackground - Color of background for selected comment range
728 
729=== DIFF EDITOR COLORS ===
730• diffEditor.insertedTextBackground - Background for text that got inserted
731• diffEditor.insertedTextBorder - Outline color for text that got inserted
732• diffEditor.removedTextBackground - Background for text that got removed
733• diffEditor.removedTextBorder - Outline color for text that got removed
734• diffEditor.border - Border color between two text editors
735• diffEditor.diagonalFill - Color of diff editor diagonal fill
736• diffEditor.insertedLineBackground - Background for lines that got inserted
737• diffEditor.removedLineBackground - Background for lines that got removed
738• diffEditorGutter.insertedLineBackground - Background for margin where lines got inserted
739• diffEditorGutter.removedLineBackground - Background for margin where lines got removed
740• diffEditorOverview.insertedForeground - Diff overview ruler foreground for inserted content
741• diffEditorOverview.removedForeground - Diff overview ruler foreground for removed content
742• diffEditor.unchangedRegionBackground - Color of unchanged blocks in diff editor
743• diffEditor.unchangedRegionForeground - Foreground of unchanged blocks in diff editor
744• diffEditor.unchangedRegionShadow - Color of shadow around unchanged region widgets
745• diffEditor.unchangedCodeBackground - Background of unchanged code in diff editor
746• diffEditor.move.border - Border color for text that got moved
747• diffEditor.moveActive.border - Active border color for text that got moved
748 
749=== EDITOR WIDGET COLORS ===
750• editorWidget.foreground - Foreground color of editor widgets
751• editorWidget.background - Background color of editor widgets
752• editorWidget.border - Border color of editor widget
753• editorWidget.resizeBorder - Border color of resize bar of editor widgets
754• editorSuggestWidget.background - Background color of suggestion widget
755• editorSuggestWidget.border - Border color of suggestion widget
756• editorSuggestWidget.foreground - Foreground color of suggestion widget
757• editorSuggestWidget.focusHighlightForeground - Color of match highlights when item is focused
758• editorSuggestWidget.highlightForeground - Color of match highlights in suggestion widget
759• editorSuggestWidget.selectedBackground - Background of selected entry in suggestion widget
760• editorSuggestWidget.selectedForeground - Foreground of selected entry in suggest widget
761• editorSuggestWidget.selectedIconForeground - Icon foreground of selected entry in suggest widget
762• editorSuggestWidgetStatus.foreground - Foreground color of suggest widget status
763• editorHoverWidget.foreground - Foreground color of editor hover
764• editorHoverWidget.background - Background color of editor hover
765• editorHoverWidget.border - Border color of editor hover
766• editorHoverWidget.highlightForeground - Foreground color of active item in parameter hint
767• editorHoverWidget.statusBarBackground - Background of editor hover status bar
768• editorGhostText.border - Border color of ghost text
769• editorGhostText.background - Background color of ghost text
770• editorGhostText.foreground - Foreground color of ghost text
771• editorStickyScroll.background - Editor sticky scroll background color
772• editorStickyScroll.border - Border color of sticky scroll
773• editorStickyScroll.shadow - Shadow color of sticky scroll
774• editorStickyScrollHover.background - Editor sticky scroll on hover background
775• debugExceptionWidget.background - Exception widget background color
776• debugExceptionWidget.border - Exception widget border color
777• editorMarkerNavigation.background - Editor marker navigation widget background
778• editorMarkerNavigationError.background - Editor marker navigation widget error color
779• editorMarkerNavigationWarning.background - Editor marker navigation widget warning color
780• editorMarkerNavigationInfo.background - Editor marker navigation widget info color
781• editorMarkerNavigationError.headerBackground - Editor marker navigation widget error heading background
782• editorMarkerNavigationWarning.headerBackground - Editor marker navigation widget warning heading background
783• editorMarkerNavigationInfo.headerBackground - Editor marker navigation widget info heading background
784 
785=== PEEK VIEW COLORS ===
786• peekView.border - Color of peek view borders and arrow
787• peekViewEditor.background - Background color of peek view editor
788• peekViewEditorGutter.background - Background color of gutter in peek view editor
789• peekViewEditor.matchHighlightBackground - Match highlight color in peek view editor
790• peekViewEditor.matchHighlightBorder - Match highlight border in peek view editor
791• peekViewResult.background - Background color of peek view result list
792• peekViewResult.fileForeground - Foreground color for file nodes in result list
793• peekViewResult.lineForeground - Foreground color for line nodes in result list
794• peekViewResult.matchHighlightBackground - Match highlight color in result list
795• peekViewResult.selectionBackground - Background of selected entry in result list
796• peekViewResult.selectionForeground - Foreground of selected entry in result list
797• peekViewTitle.background - Background color of peek view title area
798• peekViewTitleDescription.foreground - Color of peek view title info
799• peekViewTitleLabel.foreground - Color of peek view title
800 
801=== MERGE CONFLICTS COLORS ===
802• merge.currentHeaderBackground - Current header background in inline merge conflicts
803• merge.currentContentBackground - Current content background in inline merge conflicts
804• merge.incomingHeaderBackground - Incoming header background in inline merge conflicts
805• merge.incomingContentBackground - Incoming content background in inline merge conflicts
806• merge.border - Border color on headers and splitter in inline merge conflicts
807• merge.commonContentBackground - Common ancestor content background
808• merge.commonHeaderBackground - Common ancestor header background
809• editorOverviewRuler.currentContentForeground - Current overview ruler foreground for merge conflicts
810• editorOverviewRuler.incomingContentForeground - Incoming overview ruler foreground for merge conflicts
811• editorOverviewRuler.commonContentForeground - Common ancestor overview ruler foreground
812 
813=== PANEL COLORS ===
814• panel.background - Panel background color
815• panel.border - Panel border color separating from editor
816• panel.dropBorder - Drag and drop feedback color for panel titles
817• panelTitle.activeBorder - Border color for active panel title
818• panelTitle.activeForeground - Title color for active panel
819• panelTitle.inactiveForeground - Title color for inactive panel
820• panelTitle.border - Panel title border separating title from views
821• panelInput.border - Input box border for inputs in panel
822• panelSection.border - Panel section border when multiple views stacked horizontally
823• panelSection.dropBackground - Drag and drop feedback for panel sections
824• panelSectionHeader.background - Panel section header background color
825• panelSectionHeader.foreground - Panel section header foreground color
826• panelSectionHeader.border - Panel section header border when multiple views stacked vertically
827 
828=== STATUS BAR COLORS ===
829• statusBar.background - Standard Status Bar background color
830• statusBar.foreground - Status Bar foreground color
831• statusBar.border - Status Bar border separating from editor
832• statusBar.debuggingBackground - Status Bar background when debugging
833• statusBar.debuggingForeground - Status Bar foreground when debugging
834• statusBar.debuggingBorder - Status Bar border when debugging
835• statusBar.noFolderForeground - Status Bar foreground when no folder opened
836• statusBar.noFolderBackground - Status Bar background when no folder opened
837• statusBar.noFolderBorder - Status Bar border when no folder opened
838• statusBarItem.activeBackground - Status Bar item background when clicking
839• statusBarItem.hoverForeground - Status bar item foreground when hovering
840• statusBarItem.hoverBackground - Status Bar item background when hovering
841• statusBarItem.prominentForeground - Status Bar prominent items foreground
842• statusBarItem.prominentBackground - Status Bar prominent items background
843• statusBarItem.prominentHoverForeground - Status bar prominent items foreground when hovering
844• statusBarItem.prominentHoverBackground - Status Bar prominent items background when hovering
845• statusBarItem.remoteBackground - Background for remote indicator on status bar
846• statusBarItem.remoteForeground - Foreground for remote indicator on status bar
847• statusBarItem.remoteHoverBackground - Background for remote indicator when hovering
848• statusBarItem.remoteHoverForeground - Foreground for remote indicator when hovering
849• statusBarItem.errorBackground - Status bar error items background
850• statusBarItem.errorForeground - Status bar error items foreground
851• statusBarItem.errorHoverBackground - Status bar error items background when hovering
852• statusBarItem.errorHoverForeground - Status bar error items foreground when hovering
853• statusBarItem.warningBackground - Status bar warning items background
854• statusBarItem.warningForeground - Status bar warning items foreground
855• statusBarItem.warningHoverBackground - Status bar warning items background when hovering
856• statusBarItem.warningHoverForeground - Status bar warning items foreground when hovering
857• statusBarItem.compactHoverBackground - Status bar item background when hovering item with two hovers
858• statusBarItem.focusBorder - Status bar item border when focused on keyboard navigation
859• statusBar.focusBorder - Status bar border when focused on keyboard navigation
860• statusBarItem.offlineBackground - Status bar item background when workbench is offline
861• statusBarItem.offlineForeground - Status bar item foreground when workbench is offline
862• statusBarItem.offlineHoverForeground - Status bar item foreground hover when workbench is offline
863• statusBarItem.offlineHoverBackground - Status bar item background hover when workbench is offline
864 
865=== TITLE BAR COLORS ===
866• titleBar.activeBackground - Title Bar background when window is active
867• titleBar.activeForeground - Title Bar foreground when window is active
868• titleBar.inactiveBackground - Title Bar background when window is inactive
869• titleBar.inactiveForeground - Title Bar foreground when window is inactive
870• titleBar.border - Title bar border color
871 
872=== MENU BAR COLORS ===
873• menubar.selectionForeground - Foreground of selected menu item in menubar
874• menubar.selectionBackground - Background of selected menu item in menubar
875• menubar.selectionBorder - Border of selected menu item in menubar
876• menu.foreground - Foreground color of menu items
877• menu.background - Background color of menu items
878• menu.selectionForeground - Foreground of selected menu item in menus
879• menu.selectionBackground - Background of selected menu item in menus
880• menu.selectionBorder - Border of selected menu item in menus
881• menu.separatorBackground - Color of separator menu item in menus
882• menu.border - Border color of menus
883 
884=== COMMAND CENTER COLORS ===
885• commandCenter.foreground - Foreground color of Command Center
886• commandCenter.activeForeground - Active foreground color of Command Center
887• commandCenter.background - Background color of Command Center
888• commandCenter.activeBackground - Active background color of Command Center
889• commandCenter.border - Border color of Command Center
890• commandCenter.inactiveForeground - Foreground when window is inactive
891• commandCenter.inactiveBorder - Border when window is inactive
892• commandCenter.activeBorder - Active border color of Command Center
893• commandCenter.debuggingBackground - Command Center background when debugging
894 
895=== NOTIFICATION COLORS ===
896• notificationCenter.border - Notification Center border color
897• notificationCenterHeader.foreground - Notification Center header foreground
898• notificationCenterHeader.background - Notification Center header background
899• notificationToast.border - Notification toast border color
900• notifications.foreground - Notification foreground color
901• notifications.background - Notification background color
902• notifications.border - Notification border separating from other notifications
903• notificationLink.foreground - Notification links foreground color
904• notificationsErrorIcon.foreground - Color for notification error icon
905• notificationsWarningIcon.foreground - Color for notification warning icon
906• notificationsInfoIcon.foreground - Color for notification info icon
907 
908=== BANNER COLORS ===
909• banner.background - Banner background color
910• banner.foreground - Banner foreground color
911• banner.iconForeground - Color for icon in front of banner text
912 
913=== EXTENSIONS COLORS ===
914• extensionButton.prominentForeground - Extension view button foreground
915• extensionButton.prominentBackground - Extension view button background
916• extensionButton.prominentHoverBackground - Extension view button background hover
917• extensionButton.background - Button background for extension actions
918• extensionButton.foreground - Button foreground for extension actions
919• extensionButton.hoverBackground - Button background hover for extension actions
920• extensionButton.separator - Button separator for extension actions
921• extensionBadge.remoteBackground - Background for remote badge in extensions view
922• extensionBadge.remoteForeground - Foreground for remote badge in extensions view
923• extensionIcon.starForeground - Icon color for extension ratings
924• extensionIcon.verifiedForeground - Icon color for extension verified publisher
925• extensionIcon.preReleaseForeground - Icon color for pre-release extension
926• extensionIcon.sponsorForeground - Icon color for extension sponsor
927• extensionIcon.privateForeground - Icon color for private extensions
928 
929=== QUICK PICKER COLORS ===
930• pickerGroup.border - Quick picker color for grouping borders
931• pickerGroup.foreground - Quick picker color for grouping labels
932• quickInput.background - Quick input background color
933• quickInput.foreground - Quick input foreground color
934• quickInputList.focusBackground - Quick picker background for focused item
935• quickInputList.focusForeground - Quick picker foreground for focused item
936• quickInputList.focusIconForeground - Quick picker icon foreground for focused item
937• quickInputTitle.background - Quick picker title background color
938 
939=== KEYBINDING LABEL COLORS ===
940• keybindingLabel.background - Keybinding label background color
941• keybindingLabel.foreground - Keybinding label foreground color
942• keybindingLabel.border - Keybinding label border color
943• keybindingLabel.bottomBorder - Keybinding label border bottom color
944 
945=== KEYBOARD SHORTCUT TABLE COLORS ===
946• keybindingTable.headerBackground - Background for keyboard shortcuts table header
947• keybindingTable.rowsBackground - Background for keyboard shortcuts table alternating rows
948 
949=== INTEGRATED TERMINAL COLORS ===
950• terminal.background - Background of Integrated Terminal viewport
951• terminal.border - Color of border separating split panes in terminal
952• terminal.foreground - Default foreground color of Integrated Terminal
953• terminal.ansiBlack - 'Black' ANSI color in terminal
954• terminal.ansiBlue - 'Blue' ANSI color in terminal
955• terminal.ansiBrightBlack - 'BrightBlack' ANSI color in terminal
956• terminal.ansiBrightBlue - 'BrightBlue' ANSI color in terminal
957• terminal.ansiBrightCyan - 'BrightCyan' ANSI color in terminal
958• terminal.ansiBrightGreen - 'BrightGreen' ANSI color in terminal
959• terminal.ansiBrightMagenta - 'BrightMagenta' ANSI color in terminal
960• terminal.ansiBrightRed - 'BrightRed' ANSI color in terminal
961• terminal.ansiBrightWhite - 'BrightWhite' ANSI color in terminal
962• terminal.ansiBrightYellow - 'BrightYellow' ANSI color in terminal
963• terminal.ansiCyan - 'Cyan' ANSI color in terminal
964• terminal.ansiGreen - 'Green' ANSI color in terminal
965• terminal.ansiMagenta - 'Magenta' ANSI color in terminal
966• terminal.ansiRed - 'Red' ANSI color in terminal
967• terminal.ansiWhite - 'White' ANSI color in terminal
968• terminal.ansiYellow - 'Yellow' ANSI color in terminal
969• terminal.selectionBackground - Selection background color of terminal
970• terminal.selectionForeground - Selection foreground color of terminal
971• terminal.inactiveSelectionBackground - Selection background when terminal doesn't have focus
972• terminal.findMatchBackground - Color of current search match in terminal
973• terminal.findMatchBorder - Border color of current search match in terminal
974• terminal.findMatchHighlightBackground - Color of other search matches in terminal
975• terminal.findMatchHighlightBorder - Border color of other search matches in terminal
976• terminal.hoverHighlightBackground - Color of highlight when hovering link in terminal
977• terminalCursor.background - Background color of terminal cursor
978• terminalCursor.foreground - Foreground color of terminal cursor
979• terminal.dropBackground - Background when dragging on top of terminals
980• terminal.tab.activeBorder - Border on side of terminal tab in panel
981• terminalCommandDecoration.defaultBackground - Default terminal command decoration background
982• terminalCommandDecoration.successBackground - Terminal command decoration background for successful commands
983• terminalCommandDecoration.errorBackground - Terminal command decoration background for error commands
984• terminalOverviewRuler.cursorForeground - Overview ruler cursor color
985• terminalOverviewRuler.findMatchForeground - Overview ruler marker for find matches in terminal
986• terminalStickyScroll.background - Background of sticky scroll overlay in terminal
987• terminalStickyScroll.border - Border of sticky scroll overlay in terminal
988• terminalStickyScrollHover.background - Background of sticky scroll overlay when hovered
989• terminal.initialHintForeground - Foreground color of terminal initial hint
990 
991=== DEBUG COLORS ===
992• debugToolBar.background - Debug toolbar background color
993• debugToolBar.border - Debug toolbar border color
994• editor.stackFrameHighlightBackground - Background of top stack frame highlight
995• editor.focusedStackFrameHighlightBackground - Background of focused stack frame highlight
996• editor.inlineValuesForeground - Color for debug inline value text
997• editor.inlineValuesBackground - Color for debug inline value background
998• debugView.exceptionLabelForeground - Foreground for exception label in CALL STACK view
999• debugView.exceptionLabelBackground - Background for exception label in CALL STACK view
1000• debugView.stateLabelForeground - Foreground for state label in CALL STACK view
1001• debugView.stateLabelBackground - Background for state label in CALL STACK view
1002• debugView.valueChangedHighlight - Color for value changes in debug views
1003• debugTokenExpression.name - Foreground for token names in debug views
1004• debugTokenExpression.value - Foreground for token values in debug views
1005• debugTokenExpression.string - Foreground for strings in debug views
1006• debugTokenExpression.boolean - Foreground for booleans in debug views
1007• debugTokenExpression.number - Foreground for numbers in debug views
1008• debugTokenExpression.error - Foreground for expression errors in debug views
1009• debugTokenExpression.type - Foreground for token types in debug views
1011=== DEBUG ICONS COLORS ===
1012• debugIcon.breakpointForeground - Icon color for breakpoints
1013• debugIcon.breakpointDisabledForeground - Icon color for disabled breakpoints
1014• debugIcon.breakpointUnverifiedForeground - Icon color for unverified breakpoints
1015• debugIcon.breakpointCurrentStackframeForeground - Icon color for current breakpoint stack frame
1016• debugIcon.breakpointStackframeForeground - Icon color for all breakpoint stack frames
1017• debugIcon.startForeground - Debug toolbar icon for start debugging
1018• debugIcon.pauseForeground - Debug toolbar icon for pause
1019• debugIcon.stopForeground - Debug toolbar icon for stop
1020• debugIcon.disconnectForeground - Debug toolbar icon for disconnect
1021• debugIcon.restartForeground - Debug toolbar icon for restart
1022• debugIcon.stepOverForeground - Debug toolbar icon for step over
1023• debugIcon.stepIntoForeground - Debug toolbar icon for step into
1024• debugIcon.stepOutForeground - Debug toolbar icon for step over
1025• debugIcon.continueForeground - Debug toolbar icon for continue
1026• debugIcon.stepBackForeground - Debug toolbar icon for step back
1027• debugConsole.infoForeground - Foreground for info messages in debug REPL console
1028• debugConsole.warningForeground - Foreground for warning messages in debug REPL console
1029• debugConsole.errorForeground - Foreground for error messages in debug REPL console
1030• debugConsole.sourceForeground - Foreground for source filenames in debug REPL console
1031• debugConsoleInputIcon.foreground - Foreground for debug console input marker icon
1033=== TESTING COLORS ===
1034• testing.runAction - Color for 'run' icons in editor
1035• testing.iconErrored - Color for 'Errored' icon in test explorer
1036• testing.iconFailed - Color for 'failed' icon in test explorer
1037• testing.iconPassed - Color for 'passed' icon in test explorer
1038• testing.iconQueued - Color for 'Queued' icon in test explorer
1039• testing.iconUnset - Color for 'Unset' icon in test explorer
1040• testing.iconSkipped - Color for 'Skipped' icon in test explorer
1041• testing.peekBorder - Color of peek view borders and arrow
1042• testing.peekHeaderBackground - Color of peek view borders and arrow
1043• testing.message.error.lineBackground - Margin color beside error messages inline in editor
1044• testing.message.info.decorationForeground - Text color of test info messages inline in editor
1045• testing.message.info.lineBackground - Margin color beside info messages inline in editor
1046• testing.coveredBackground - Background color of text that was covered
1047• testing.coveredBorder - Border color of text that was covered
1048• testing.coveredGutterBackground - Gutter color of regions where code was covered
1049• testing.uncoveredBackground - Background color of text that was not covered
1050• testing.uncoveredBorder - Border color of text that was not covered
1051• testing.uncoveredGutterBackground - Gutter color of regions where code not covered
1053=== WELCOME PAGE COLORS ===
1054• welcomePage.background - Background color for Welcome page
1055• welcomePage.progress.background - Foreground color for Welcome page progress bars
1056• welcomePage.progress.foreground - Background color for Welcome page progress bars
1057• welcomePage.tileBackground - Background color for tiles on Welcome page
1058• welcomePage.tileHoverBackground - Hover background color for tiles on Welcome page
1059• welcomePage.tileBorder - Border color for tiles on Welcome page
1060• walkThrough.embeddedEditorBackground - Background for embedded editors on Interactive Playground
1061• walkthrough.stepTitle.foreground - Foreground color of heading of each walkthrough step
1063=== GIT COLORS ===
1064• gitDecoration.addedResourceForeground - Color for added Git resources
1065• gitDecoration.modifiedResourceForeground - Color for modified Git resources
1066• gitDecoration.deletedResourceForeground - Color for deleted Git resources
1067• gitDecoration.renamedResourceForeground - Color for renamed or copied Git resources
1068• gitDecoration.stageModifiedResourceForeground - Color for staged modifications git decorations
1069• gitDecoration.stageDeletedResourceForeground - Color for staged deletions git decorations
1070• gitDecoration.untrackedResourceForeground - Color for untracked Git resources
1071• gitDecoration.ignoredResourceForeground - Color for ignored Git resources
1072• gitDecoration.conflictingResourceForeground - Color for conflicting Git resources
1073• gitDecoration.submoduleResourceForeground - Color for submodule resources
1075=== SETTINGS EDITOR COLORS ===
1076• settings.headerForeground - Foreground color for section header or active title
1077• settings.modifiedItemIndicator - Line that indicates a modified setting
1078• settings.dropdownBackground - Dropdown background
1079• settings.dropdownForeground - Dropdown foreground
1080• settings.dropdownBorder - Dropdown border
1081• settings.dropdownListBorder - Dropdown list border
1082• settings.checkboxBackground - Checkbox background
1083• settings.checkboxForeground - Checkbox foreground
1084• settings.checkboxBorder - Checkbox border
1085• settings.rowHoverBackground - Background of settings row when hovered
1086• settings.textInputBackground - Text input box background
1087• settings.textInputForeground - Text input box foreground
1088• settings.textInputBorder - Text input box border
1089• settings.numberInputBackground - Number input box background
1090• settings.numberInputForeground - Number input box foreground
1091• settings.numberInputBorder - Number input box border
1092• settings.focusedRowBackground - Background of focused setting row
1093• settings.focusedRowBorder - Color of row's top and bottom border when focused
1094• settings.headerBorder - Color of header container border
1095• settings.sashBorder - Color of Settings editor splitview sash border
1096• settings.settingsHeaderHoverForeground - Foreground for section header or hovered title
1098=== BREADCRUMBS COLORS ===
1099• breadcrumb.foreground - Color of breadcrumb items
1100• breadcrumb.background - Background color of breadcrumb items
1101• breadcrumb.focusForeground - Color of focused breadcrumb items
1102• breadcrumb.activeSelectionForeground - Color of selected breadcrumb items
1103• breadcrumbPicker.background - Background color of breadcrumb item picker
1105=== SNIPPETS COLORS ===
1106• editor.snippetTabstopHighlightBackground - Highlight background color of snippet tabstop
1107• editor.snippetTabstopHighlightBorder - Highlight border color of snippet tabstop
1108• editor.snippetFinalTabstopHighlightBackground - Highlight background color of final tabstop of snippet
1109• editor.snippetFinalTabstopHighlightBorder - Highlight border color of final tabstop of snippet
1111=== SYMBOL ICONS COLORS ===
1112• symbolIcon.arrayForeground - Foreground color for array symbols
1113• symbolIcon.booleanForeground - Foreground color for boolean symbols
1114• symbolIcon.classForeground - Foreground color for class symbols
1115• symbolIcon.colorForeground - Foreground color for color symbols
1116• symbolIcon.constantForeground - Foreground color for constant symbols
1117• symbolIcon.constructorForeground - Foreground color for constructor symbols
1118• symbolIcon.enumeratorForeground - Foreground color for enumerator symbols
1119• symbolIcon.enumeratorMemberForeground - Foreground color for enumerator member symbols
1120• symbolIcon.eventForeground - Foreground color for event symbols
1121• symbolIcon.fieldForeground - Foreground color for field symbols
1122• symbolIcon.fileForeground - Foreground color for file symbols
1123• symbolIcon.folderForeground - Foreground color for folder symbols
1124• symbolIcon.functionForeground - Foreground color for function symbols
1125• symbolIcon.interfaceForeground - Foreground color for interface symbols
1126• symbolIcon.keyForeground - Foreground color for key symbols
1127• symbolIcon.keywordForeground - Foreground color for keyword symbols
1128• symbolIcon.methodForeground - Foreground color for method symbols
1129• symbolIcon.moduleForeground - Foreground color for module symbols
1130• symbolIcon.namespaceForeground - Foreground color for namespace symbols
1131• symbolIcon.nullForeground - Foreground color for null symbols
1132• symbolIcon.numberForeground - Foreground color for number symbols
1133• symbolIcon.objectForeground - Foreground color for object symbols
1134• symbolIcon.operatorForeground - Foreground color for operator symbols
1135• symbolIcon.packageForeground - Foreground color for package symbols
1136• symbolIcon.propertyForeground - Foreground color for property symbols
1137• symbolIcon.referenceForeground - Foreground color for reference symbols
1138• symbolIcon.snippetForeground - Foreground color for snippet symbols
1139• symbolIcon.stringForeground - Foreground color for string symbols
1140• symbolIcon.structForeground - Foreground color for struct symbols
1141• symbolIcon.textForeground - Foreground color for text symbols
1142• symbolIcon.typeParameterForeground - Foreground color for type parameter symbols
1143• symbolIcon.unitForeground - Foreground color for unit symbols
1144• symbolIcon.variableForeground - Foreground color for variable symbols
1146=== NOTEBOOK COLORS ===
1147• notebook.editorBackground - Notebook background color
1148• notebook.cellBorderColor - Border color for notebook cells
1149• notebook.cellHoverBackground - Background when cell is hovered
1150• notebook.cellInsertionIndicator - Color of notebook cell insertion indicator
1151• notebook.cellStatusBarItemHoverBackground - Background of notebook cell status bar items
1152• notebook.cellToolbarSeparator - Color of separator in cell bottom toolbar
1153• notebook.cellEditorBackground - Color of notebook cell editor background
1154• notebook.focusedCellBackground - Background when cell is focused
1155• notebook.focusedCellBorder - Color of cell's focus indicator borders when focused
1156• notebook.focusedEditorBorder - Color of notebook cell editor border
1157• notebook.inactiveFocusedCellBorder - Color of cell's borders when focused but primary focus is outside
1158• notebook.inactiveSelectedCellBorder - Color of cell's borders when multiple cells are selected
1159• notebook.outputContainerBackgroundColor - Color of notebook output container background
1160• notebook.outputContainerBorderColor - Border color of notebook output container
1161• notebook.selectedCellBackground - Background when cell is selected
1162• notebook.selectedCellBorder - Color of cell's top and bottom border when selected but not focused
1163• notebook.symbolHighlightBackground - Background color of highlighted cell
1164• notebookScrollbarSlider.activeBackground - Notebook scrollbar slider background when clicked
1165• notebookScrollbarSlider.background - Notebook scrollbar slider background color
1166• notebookScrollbarSlider.hoverBackground - Notebook scrollbar slider background when hovering
1167• notebookStatusErrorIcon.foreground - Error icon color of notebook cells in status bar
1168• notebookStatusRunningIcon.foreground - Running icon color of notebook cells in status bar
1169• notebookStatusSuccessIcon.foreground - Success icon color of notebook cells in status bar
1171=== CHART COLORS ===
1172• charts.foreground - Contrast color for text in charts
1173• charts.lines - Color for lines in charts
1174• charts.red - Color for red elements in charts
1175• charts.blue - Color for blue elements in charts
1176• charts.yellow - Color for yellow elements in charts
1177• charts.orange - Color for orange elements in charts
1178• charts.green - Color for green elements in charts
1179• charts.purple - Color for purple elements in charts
1181Common token scopes: comment, keyword, string, number, constant, variable, function, class, type, operator, punctuation, storage, support, entity, invalid, markup
1183MESSAGE EXAMPLES (be creative and match the theme vibe):
1185FOR "CYBERPUNK NEON" THEMES:
1186- MESSAGE:Jacking into editor...
1187- MESSAGE:Activity bar neon: ON ⚡
1188- MESSAGE:Sidebar cyber-glow ready
1189- MESSAGE:Status bar armed & dangerous
1191FOR "COZY AUTUMN" THEMES:
1192- MESSAGE:Warming editor background...
1193- MESSAGE:Sidebar autumn leaves 🍂
1194- MESSAGE:Terminal pumpkin spice
1195- MESSAGE:Activity bar golden glow
1197FOR "OCEAN/WATER" THEMES:
1198- MESSAGE:Diving into editor depths...
1199- MESSAGE:Sidebar sea-green ready
1200- MESSAGE:Status bar coral accents
1201- MESSAGE:Terminal underwater blue
1203FOR "FOREST/NATURE" THEMES:
1204- MESSAGE:Growing moss-green editor...
1205- MESSAGE:Activity bar oak bark
1206- MESSAGE:Sidebar forest wisdom
1207- MESSAGE:Terminal spring meadow 🌸
1209FOR "MINIMAL/CLEAN" THEMES:
1210- MESSAGE:Zen editor achieved ✨
1211- MESSAGE:Essential sidebar colors
1212- MESSAGE:Clean status bar lines
1213- MESSAGE:Terminal noise deleted
1215FOR "RETRO/80S" THEMES:
1216- MESSAGE:Editor rad vibes loading
1217- MESSAGE:Sidebar totally awesome 📼
1218- MESSAGE:Status bar neon pink
1219- MESSAGE:Terminal disco fever
1221FOR "SPACE/COSMIC" THEMES:
1222- MESSAGE:Editor space launch 🚀
1223- MESSAGE:Sidebar orbiting colors
1224- MESSAGE:Status bar binary stars
1225- MESSAGE:Terminal cosmic dust
1227FOR "COFFEE/WARM" THEMES:
1228- MESSAGE:Brewing warm editor...
1229- MESSAGE:Sidebar espresso ready ☕
1230- MESSAGE:Status bar cream swirl
1231- MESSAGE:Terminal coffee comfort
1233KEEP MESSAGES SHORT (under 30 chars when possible) for notification window readability!
1235GENERAL CREATIVE PATTERNS:
1236- Always mention specific UI components being styled (editor, sidebar, status bar, activity bar, terminal)
1237- Use wordplay and puns that connect both the theme and the actual UI element
1238- Add occasional rhymes or alliteration for extra charm
1239- Include relevant emojis sparingly for visual flair
1240- Progress through different UI areas as the theme unfolds
1241- Be slightly meta about the theming process itself
1242- Match the energy/mood of the theme (cozy vs energetic vs mysterious)
1243- Make it clear which part of the interface is being themed
1245Remember: Each message should feel like it belongs to the specific theme being generated AND clearly reference what UI component is actually being styled at that moment!
1247CRITICAL: Always analyze the user's theme description and craft messages that match their specific vibe. If they say "cyberpunk neon city", your messages should be tech/neon themed. If they say "cozy autumn evening", your messages should be warm/seasonal. Don't use generic messages - make each one feel custom-crafted for their exact theme request!

That catalogue is the bulk of the file, and it is where the cracks show. Whole sections are duplicated verbatim. SIDE BAR, EDITOR COLORS, MINIMAP, PROFILES, and EDITOR GROUPS & TABS each appear twice. Every generation pays for that dead weight. Every request, every user, forever.

The second copy of SIDE BAR / MINIMAP — the first is up at lines 268–300.

prompts/streamingThemePrompt.txt · 1247 lines
prompts/streamingThemePrompt.txt1247 lines · Text only
⋯ 502 lines hidden (lines 1–502)
1You are a professional VS Code theme designer and code editor color expert. Given a theme description, generate a COMPLETE, COMPREHENSIVE theme by outputting settings ONE AT A TIME, streaming each setting on a separate line.
2 
3IMPORTANT: You can either generate a FULL NEW THEME or MODIFY AN EXISTING THEME based on user intent.
4 
5=== INTENT DETECTION ===
6ANALYZE the user's request to determine intent:
7 
8**NEW THEME GENERATION**: Full comprehensive theme (complete theme)
9- User describes a complete theme concept: "cyberpunk neon city", "cozy autumn evening", "minimal dark forest"
10- No current theme context provided
11- Generate ALL available UI elements and token scopes
12 
13**THEME ITERATION**: Incremental modifications to existing theme
14- User requests changes to existing theme: "make it warmer", "darker background", "remove purple accents", "add blue highlights"
15- Current theme context is provided showing existing customizations
16- Generate ONLY the settings that need to change
17- Use REMOVE to clear specific settings
18 
19Output each setting in this exact format:
20COUNT:estimated_total_settings
21SELECTOR:selector_name=color_value
22SELECTOR:selector_name=REMOVE
23TOKEN:scope_name=color_value[,fontStyle]
24TOKEN:scope_name=REMOVE
25MESSAGE:your_engaging_progress_message
26 
27Examples:
28**New Theme:**
29COUNT:125
30SELECTOR:editor.background=#1a1a1a
31MESSAGE:Once upon a midnight dreary, while I coded weak and weary...
32SELECTOR:activityBar.background=#2d2d2d
33TOKEN:comment=#6a9955,italic
34 
35**Theme Iteration:**
36COUNT:15
37MESSAGE:Warming up your theme... 🔥
38SELECTOR:editor.background=#2a1f1a
39SELECTOR:statusBar.background=#3d2e1f
40SELECTOR:activityBarBadge.background=REMOVE
41MESSAGE:Purple accents cleared ✨
42 
43Rules:
441. START with a COUNT line indicating the estimated total number of settings you will generate
452. Use valid hex codes: 6-digit (#rrggbb) for solid colors, or 8-digit (#rrggbbaa) for transparency
463. Use lowercase letters for hex codes
474. For TOKEN lines, fontStyle is optional (italic, bold, underline, none)
485. Use REMOVE to clear existing customizations (falls back to base theme)
496. Output one setting per line
507. DO NOT output any JSON, comments, or explanations
518. Each line must start with either "COUNT:", "SELECTOR:", "TOKEN:", or "MESSAGE:"
529. For NEW themes: Cover ALL available UI areas and token scopes listed below
5310. For ITERATIONS: Only output settings that need to change
5411. Sprinkle MESSAGE lines throughout to create an engaging experience
5512. Messages should reference specific UI components being styled
5613. Be creative with wordplay, rhymes, and humor - think Sims loading screens but themed!
5714. Make users smile while showing progress through actual UI areas being themed
58 
59PAINTER'S WORKFLOW - Apply settings in this order:
60 
61**FOR NEW THEMES (comprehensive generation):**
621. FOUNDATION (broad strokes): Main backgrounds and foregrounds
63 - editor.background, foreground
64 - activityBar.background, foreground
65 - sideBar.background, foreground
66 - statusBar.background, foreground
67 
682. MAJOR STRUCTURES (primary elements):
69 - Tab containers and active states
70 - Panel backgrounds and borders
71 - Terminal base colors
72 - Primary input fields and buttons
73 
743. INTERACTIVE STATES (hover, selection, focus):
75 - Selection backgrounds and highlights
76 - Hover states for buttons and lists
77 - Focus borders and active indicators
78 - Dropdown and widget interactions
79 
804. DETAILED REFINEMENTS (fine brushwork):
81 - Secondary buttons and badges
82 - Scrollbars and minimap elements
83 - Error/warning/info indicators
84 - Git decorations and diff colors
85 
865. CODE SYNTAX (syntax highlighting):
87 - Core language elements (keywords, strings, comments)
88 - Functions, variables, and operators
89 - Markup and documentation elements
90 - Invalid code and error highlighting
91 
92**FOR THEME ITERATIONS (targeted changes):**
93- Focus ONLY on the elements mentioned in the user's request
94- Use REMOVE to clear unwanted customizations
95- Be surgical - change only what's needed
96- Maintain consistency with existing theme elements
97 
98COMPLETION CRITERIA:
99**New themes:** Generate settings for ALL UI areas and token scopes listed below
100**Iterations:** Generate only the specific changes requested
101- Do NOT stop until you have provided a truly comprehensive theme (new) or complete iteration (modify)
102- If you're unsure whether to include a setting, INCLUDE IT for completeness (new themes only)
103 
104AVAILABLE THEME COLOR SELECTORS (organized by category):
105 
106=== BASE COLORS ===
107• focusBorder - Overall border color for focused elements
108• foreground - Overall foreground color
109• disabledForeground - Overall foreground for disabled elements
110• widget.border - Border color of widgets such as Find/Replace
111• widget.shadow - Shadow color of widgets
112• selection.background - Background color of text selections in workbench
113• descriptionForeground - Foreground color for description text
114• errorForeground - Overall foreground color for error messages
115• icon.foreground - Default color for icons in the workbench
116• sash.hoverBorder - Hover border color for draggable sashes
117 
118=== CONTRAST COLORS ===
119• contrastActiveBorder - Extra border around active elements for greater contrast
120• contrastBorder - Extra border around elements for greater contrast
121 
122=== WINDOW BORDER ===
123• window.activeBorder - Border color for the active window
124• window.inactiveBorder - Border color for inactive windows
125 
126=== TEXT COLORS ===
127• textBlockQuote.background - Background color for block quotes in text
128• textBlockQuote.border - Border color for block quotes in text
129• textCodeBlock.background - Background color for code blocks in text
130• textLink.activeForeground - Foreground color for links when clicked/hovered
131• textLink.foreground - Foreground color for links in text
132• textPreformat.foreground - Foreground color for preformatted text segments
133• textPreformat.background - Background color for preformatted text segments
134• textSeparator.foreground - Color for text separators
135 
136=== ACTION COLORS ===
137• toolbar.hoverBackground - Toolbar background when hovering over actions
138• toolbar.hoverOutline - Toolbar outline when hovering over actions
139• toolbar.activeBackground - Toolbar background when holding mouse over actions
140• editorActionList.background - Action List background color
141• editorActionList.foreground - Action List foreground color
142• editorActionList.focusForeground - Action List foreground for focused item
143• editorActionList.focusBackground - Action List background for focused item
144 
145=== BUTTON CONTROL ===
146• button.background - Button background color
147• button.foreground - Button foreground color
148• button.border - Button border color
149• button.separator - Button separator color
150• button.hoverBackground - Button background color when hovering
151• button.secondaryForeground - Secondary button foreground color
152• button.secondaryBackground - Secondary button background color
153• button.secondaryHoverBackground - Secondary button background when hovering
154• checkbox.background - Background color of checkbox widget
155• checkbox.foreground - Foreground color of checkbox widget
156• checkbox.disabled.background - Background of disabled checkbox
157• checkbox.disabled.foreground - Foreground of disabled checkbox
158• checkbox.border - Border color of checkbox widget
159• checkbox.selectBackground - Background when checkbox element is selected
160• checkbox.selectBorder - Border when checkbox element is selected
161• radio.activeForeground - Foreground color of active radio option
162• radio.activeBackground - Background color of active radio option
163• radio.activeBorder - Border color of the active radio option
164• radio.inactiveForeground - Foreground color of inactive radio option
165• radio.inactiveBackground - Background color of inactive radio option
166• radio.inactiveBorder - Border color of the inactive radio option
167• radio.inactiveHoverBackground - Background color of inactive radio option when hovering
168 
169=== DROPDOWN CONTROL ===
170• dropdown.background - Dropdown background
171• dropdown.listBackground - Dropdown list background
172• dropdown.border - Dropdown border
173• dropdown.foreground - Dropdown foreground
174 
175=== INPUT CONTROL ===
176• input.background - Input box background
177• input.border - Input box border
178• input.foreground - Input box foreground
179• input.placeholderForeground - Input box placeholder text color
180• inputOption.activeBackground - Background of activated options in input fields
181• inputOption.activeBorder - Border of activated options in input fields
182• inputOption.activeForeground - Foreground of activated options in input fields
183• inputOption.hoverBackground - Background of activated options when hovering
184• inputValidation.errorBackground - Input validation background for error severity
185• inputValidation.errorForeground - Input validation foreground for error severity
186• inputValidation.errorBorder - Input validation border for error severity
187• inputValidation.infoBackground - Input validation background for info severity
188• inputValidation.infoForeground - Input validation foreground for info severity
189• inputValidation.infoBorder - Input validation border for info severity
190• inputValidation.warningBackground - Input validation background for warning severity
191• inputValidation.warningForeground - Input validation foreground for warning severity
192• inputValidation.warningBorder - Input validation border for warning severity
193 
194=== SCROLLBAR CONTROL ===
195• scrollbar.shadow - Scrollbar slider shadow to indicate view is scrolled
196• scrollbarSlider.activeBackground - Scrollbar slider background when clicked
197• scrollbarSlider.background - Scrollbar slider background color
198• scrollbarSlider.hoverBackground - Scrollbar slider background when hovering
199 
200=== BADGE ===
201• badge.foreground - Badge foreground color
202• badge.background - Badge background color
203 
204=== PROGRESS BAR ===
205• progressBar.background - Background color of progress bar for long operations
206 
207=== LISTS AND TREES ===
208• list.activeSelectionBackground - List/Tree background for selected item when active
209• list.activeSelectionForeground - List/Tree foreground for selected item when active
210• list.activeSelectionIconForeground - List/Tree icon foreground for selected item when active
211• list.dropBackground - List/Tree drag and drop background
212• list.focusBackground - List/Tree background for focused item when active
213• list.focusForeground - List/Tree foreground for focused item when active
214• list.focusHighlightForeground - List/Tree foreground of match highlights on focused items
215• list.focusOutline - List/Tree outline for focused item when active
216• list.focusAndSelectionOutline - List/Tree outline for focused selected item when active
217• list.highlightForeground - List/Tree foreground of match highlights when searching
218• list.hoverBackground - List/Tree background when hovering with mouse
219• list.hoverForeground - List/Tree foreground when hovering with mouse
220• list.inactiveSelectionBackground - List/Tree background for selected item when inactive
221• list.inactiveSelectionForeground - List/Tree foreground for selected item when inactive
222• list.inactiveSelectionIconForeground - List/Tree icon foreground for selected item when inactive
223• list.inactiveFocusBackground - List background for focused item when inactive
224• list.inactiveFocusOutline - List/Tree outline for focused item when inactive
225• list.invalidItemForeground - List/Tree foreground for invalid items
226• list.errorForeground - Foreground of list items containing errors
227• list.warningForeground - Foreground of list items containing warnings
228• listFilterWidget.background - List/Tree Filter background when searching
229• listFilterWidget.outline - List/Tree Filter Widget outline when searching
230• listFilterWidget.noMatchesOutline - List/Tree Filter Widget outline when no matches
231• listFilterWidget.shadow - Shadow color of type filter widget
232• list.filterMatchBackground - Background of filtered matches in lists and trees
233• list.filterMatchBorder - Border of filtered matches in lists and trees
234• list.deemphasizedForeground - List/Tree foreground for deemphasized items
235• list.dropBetweenBackground - List/Tree drag and drop border between items
236• tree.indentGuidesStroke - Tree Widget stroke color for indent guides
237• tree.inactiveIndentGuidesStroke - Tree stroke color for inactive indentation guides
238• tree.tableColumnsBorder - Tree stroke color for indentation guides
239• tree.tableOddRowsBackground - Background color for odd table rows
240 
241=== ACTIVITY BAR ===
242• activityBar.background - Activity Bar background color
243• activityBar.dropBorder - Drag and drop feedback color for activity bar items
244• activityBar.foreground - Activity Bar foreground color (for icons)
245• activityBar.inactiveForeground - Activity Bar item foreground when inactive
246• activityBar.border - Activity Bar border color with Side Bar
247• activityBarBadge.background - Activity notification badge background color
248• activityBarBadge.foreground - Activity notification badge foreground color
249• activityBar.activeBorder - Activity Bar active indicator border color
250• activityBar.activeBackground - Activity Bar optional background for active element
251• activityBar.activeFocusBorder - Activity bar focus border color for active item
252• activityBarTop.foreground - Active foreground color when activity bar is on top
253• activityBarTop.activeBorder - Focus border color for active item when activity bar is on top
254• activityBarTop.inactiveForeground - Inactive foreground color when activity bar is on top
255• activityBarTop.dropBorder - Drag and drop feedback color when activity bar is on top
256• activityBarTop.background - Background color when activity bar is set to top/bottom
257• activityBarTop.activeBackground - Background for active item when activity bar is on top/bottom
258• activityWarningBadge.foreground - Foreground color of the warning activity badge
259• activityWarningBadge.background - Background color of the warning activity badge
260• activityErrorBadge.foreground - Foreground color of the error activity badge
261• activityErrorBadge.background - Background color of the error activity badge
262 
263=== PROFILES ===
264• profileBadge.background - Profile badge background color on settings gear icon
265• profileBadge.foreground - Profile badge foreground color on settings gear icon
266• profiles.sashBorder - Color of the Profiles editor splitview sash border
267 
268=== SIDE BAR ===
269• sideBar.background - Side Bar background color
270• sideBar.foreground - Side Bar foreground color
271• sideBar.border - Side Bar border color on the side separating the editor
272• sideBar.dropBackground - Drag and drop feedback color for side bar sections
273• sideBarTitle.foreground - Side Bar title foreground color
274• sideBarTitle.background - Side bar title background color
275• sideBarTitle.border - Side bar title border color on bottom
276• sideBarSectionHeader.background - Side Bar section header background color
277• sideBarSectionHeader.foreground - Side Bar section header foreground color
278• sideBarSectionHeader.border - Side bar section header border color
279• sideBarActivityBarTop.border - Border color between activity bar at top/bottom and views
280• sideBarStickyScroll.background - Background color of sticky scroll in side bar
281• sideBarStickyScroll.border - Border color of sticky scroll in side bar
282• sideBarStickyScroll.shadow - Shadow color of sticky scroll in side bar
283 
284=== MINIMAP ===
285• minimap.findMatchHighlight - Highlight color for matches from search within files
286• minimap.selectionHighlight - Highlight color for the editor selection
287• minimap.errorHighlight - Highlight color for errors within the editor
288• minimap.warningHighlight - Highlight color for warnings within the editor
289• minimap.background - Minimap background color
290• minimap.selectionOccurrenceHighlight - Minimap marker color for repeating editor selections
291• minimap.foregroundOpacity - Opacity of foreground elements rendered in minimap
292• minimap.infoHighlight - Minimap marker color for infos
293• minimap.chatEditHighlight - Color of pending edit regions in minimap
294• minimapSlider.background - Minimap slider background color
295• minimapSlider.hoverBackground - Minimap slider background color when hovering
296• minimapSlider.activeBackground - Minimap slider background color when clicked
297• minimapGutter.addedBackground - Minimap gutter color for added content
298• minimapGutter.modifiedBackground - Minimap gutter color for modified content
299• minimapGutter.deletedBackground - Minimap gutter color for deleted content
300• editorMinimap.inlineChatInserted - Minimap marker color for inline chat inserted content
301 
302=== EDITOR GROUPS & TABS ===
303• editorGroup.border - Color to separate multiple editor groups from each other
304• editorGroup.dropBackground - Background color when dragging editors around
305• editorGroup.emptyBackground - Background color of an empty editor group
306• editorGroup.focusedEmptyBorder - Border color of an empty editor group that is focused
307• editorGroup.dropIntoPromptForeground - Foreground color of text shown over editors when dragging files
308• editorGroup.dropIntoPromptBackground - Background color of text shown over editors when dragging files
309• editorGroup.dropIntoPromptBorder - Border color of text shown over editors when dragging files
310• editorGroupHeader.noTabsBackground - Background when using single Tab mode
311• editorGroupHeader.tabsBackground - Background color of the Tabs container
312• editorGroupHeader.tabsBorder - Border color below editor tabs control when tabs enabled
313• editorGroupHeader.border - Border color between editor group header and editor
314• tab.activeBackground - Active Tab background color in an active group
315• tab.unfocusedActiveBackground - Active Tab background color in an inactive editor group
316• tab.activeForeground - Active Tab foreground color in an active group
317• tab.border - Border to separate Tabs from each other
318• tab.activeBorder - Bottom border for the active tab
319• tab.selectedBorderTop - Border to the top of a selected tab
320• tab.selectedBackground - Background of a selected tab
321• tab.selectedForeground - Foreground of a selected tab
322• tab.dragAndDropBorder - Border between tabs to indicate tab insertion
323• tab.unfocusedActiveBorder - Bottom border for active tab in inactive editor group
324• tab.activeBorderTop - Top border for the active tab
325• tab.unfocusedActiveBorderTop - Top border for active tab in inactive editor group
326• tab.lastPinnedBorder - Border on right of last pinned editor to separate from unpinned
327• tab.inactiveBackground - Inactive Tab background color
328• tab.unfocusedInactiveBackground - Inactive Tab background color in unfocused group
329• tab.inactiveForeground - Inactive Tab foreground color in active group
330• tab.unfocusedActiveForeground - Active tab foreground color in inactive editor group
331• tab.unfocusedInactiveForeground - Inactive tab foreground color in inactive editor group
332• tab.hoverBackground - Tab background color when hovering
333• tab.unfocusedHoverBackground - Tab background color in unfocused group when hovering
334• tab.hoverForeground - Tab foreground color when hovering
335• tab.unfocusedHoverForeground - Tab foreground color in unfocused group when hovering
336• tab.hoverBorder - Border to highlight tabs when hovering
337• tab.unfocusedHoverBorder - Border to highlight tabs in unfocused group when hovering
338• tab.activeModifiedBorder - Border on top of modified active tabs in active group
339• tab.inactiveModifiedBorder - Border on top of modified inactive tabs in active group
340• tab.unfocusedActiveModifiedBorder - Border on top of modified active tabs in unfocused group
341• tab.unfocusedInactiveModifiedBorder - Border on top of modified inactive tabs in unfocused group
342• editorPane.background - Background color of editor pane visible on left and right of centered editor layout
343• sideBySideEditor.horizontalBorder - Color to separate two editors when shown side by side from top to bottom
344• sideBySideEditor.verticalBorder - Color to separate two editors when shown side by side from left to right
345 
346=== EDITOR COLORS ===
347• editor.background - Editor background color
348• editor.foreground - Editor default foreground color
349• editorLineNumber.foreground - Color of editor line numbers
350• editorLineNumber.activeForeground - Color of the active editor line number
351• editorLineNumber.dimmedForeground - Color of final editor line when renderFinalNewline is dimmed
352• editorCursor.background - Background color of the editor cursor
353• editorCursor.foreground - Color of the editor cursor
354• editorMultiCursor.primary.foreground - Color of primary editor cursor when multiple cursors present
355• editorMultiCursor.primary.background - Background color of primary editor cursor when multiple cursors present
356• editorMultiCursor.secondary.foreground - Color of secondary editor cursors when multiple cursors present
357• editorMultiCursor.secondary.background - Background color of secondary editor cursors when multiple cursors present
358• editor.placeholder.foreground - Foreground color of placeholder text in editor
359• editor.compositionBorder - Border color for an IME composition
360• editor.selectionBackground - Color of the editor selection
361• editor.selectionForeground - Color of the selected text for high contrast
362• editor.inactiveSelectionBackground - Color of selection in inactive editor
363• editor.selectionHighlightBackground - Color for regions with same content as selection
364• editor.selectionHighlightBorder - Border color for regions with same content as selection
365• editor.wordHighlightBackground - Background color of symbol during read-access
366• editor.wordHighlightBorder - Border color of symbol during read-access
367• editor.wordHighlightStrongBackground - Background color of symbol during write-access
368• editor.wordHighlightStrongBorder - Border color of symbol during write-access
369• editor.wordHighlightTextBackground - Background color of textual occurrence for symbol
370• editor.wordHighlightTextBorder - Border color of textual occurrence for symbol
371• editor.findMatchBackground - Color of the current search match
372• editor.findMatchForeground - Text color of the current search match
373• editor.findMatchHighlightForeground - Foreground color of the other search matches
374• editor.findMatchHighlightBackground - Color of the other search matches
375• editor.findRangeHighlightBackground - Color the range limiting the search
376• editor.findMatchBorder - Border color of the current search match
377• editor.findMatchHighlightBorder - Border color of the other search matches
378• editor.findRangeHighlightBorder - Border color the range limiting the search
379• search.resultsInfoForeground - Color of text in search viewlet's completion message
380• searchEditor.findMatchBackground - Color of the editor's results
381• searchEditor.findMatchBorder - Border color of the editor's results
382• searchEditor.textInputBorder - Search editor text input box border
383• editor.hoverHighlightBackground - Highlight below the word for which a hover is shown
384• editor.lineHighlightBackground - Background color for highlight of line at cursor position
385• editor.lineHighlightBorder - Background color for border around line at cursor position
386• editorWatermark.foreground - Foreground color for labels in editor watermark
387• editorUnicodeHighlight.border - Border color used to highlight unicode characters
388• editorUnicodeHighlight.background - Background color used to highlight unicode characters
389• editorLink.activeForeground - Color of active links
390• editor.rangeHighlightBackground - Background color of highlighted ranges
391• editor.rangeHighlightBorder - Background color of border around highlighted ranges
392• editor.symbolHighlightBackground - Background color of highlighted symbol
393• editor.symbolHighlightBorder - Background color of border around highlighted symbols
394• editorWhitespace.foreground - Color of whitespace characters in the editor
395• editorIndentGuide.background - Color of the editor indentation guides
396• editorIndentGuide.background1 - Color of the editor indentation guides (1)
397• editorIndentGuide.background2 - Color of the editor indentation guides (2)
398• editorIndentGuide.background3 - Color of the editor indentation guides (3)
399• editorIndentGuide.background4 - Color of the editor indentation guides (4)
400• editorIndentGuide.background5 - Color of the editor indentation guides (5)
401• editorIndentGuide.background6 - Color of the editor indentation guides (6)
402• editorIndentGuide.activeBackground - Color of the active editor indentation guide
403• editorIndentGuide.activeBackground1 - Color of the active editor indentation guides (1)
404• editorIndentGuide.activeBackground2 - Color of the active editor indentation guides (2)
405• editorIndentGuide.activeBackground3 - Color of the active editor indentation guides (3)
406• editorIndentGuide.activeBackground4 - Color of the active editor indentation guides (4)
407• editorIndentGuide.activeBackground5 - Color of the active editor indentation guides (5)
408• editorIndentGuide.activeBackground6 - Color of the active editor indentation guides (6)
409• editorInlayHint.background - Background color of inline hints
410• editorInlayHint.foreground - Foreground color of inline hints
411• editorInlayHint.typeForeground - Foreground color of inline hints for types
412• editorInlayHint.typeBackground - Background color of inline hints for types
413• editorInlayHint.parameterForeground - Foreground color of inline hints for parameters
414• editorInlayHint.parameterBackground - Background color of inline hints for parameters
415• editorRuler.foreground - Color of the editor rulers
416• editor.linkedEditingBackground - Background color when editor is in linked editing mode
417• editorCodeLens.foreground - Foreground color of an editor CodeLens
418• editorLightBulb.foreground - Color used for the lightbulb actions icon
419• editorLightBulbAutoFix.foreground - Color used for the lightbulb auto fix actions icon
420• editorLightBulbAi.foreground - Color used for the lightbulb AI icon
421• editorBracketMatch.background - Background color behind matching brackets
422• editorBracketMatch.border - Color for matching brackets boxes
423• editorBracketHighlight.foreground1 - Foreground color of brackets (1)
424• editorBracketHighlight.foreground2 - Foreground color of brackets (2)
425• editorBracketHighlight.foreground3 - Foreground color of brackets (3)
426• editorBracketHighlight.foreground4 - Foreground color of brackets (4)
427• editorBracketHighlight.foreground5 - Foreground color of brackets (5)
428• editorBracketHighlight.foreground6 - Foreground color of brackets (6)
429• editorBracketHighlight.unexpectedBracket.foreground - Foreground color of unexpected brackets
430• editorBracketPairGuide.activeBackground1 - Background color of active bracket pair guides (1)
431• editorBracketPairGuide.activeBackground2 - Background color of active bracket pair guides (2)
432• editorBracketPairGuide.activeBackground3 - Background color of active bracket pair guides (3)
433• editorBracketPairGuide.activeBackground4 - Background color of active bracket pair guides (4)
434• editorBracketPairGuide.activeBackground5 - Background color of active bracket pair guides (5)
435• editorBracketPairGuide.activeBackground6 - Background color of active bracket pair guides (6)
436• editorBracketPairGuide.background1 - Background color of inactive bracket pair guides (1)
437• editorBracketPairGuide.background2 - Background color of inactive bracket pair guides (2)
438• editorBracketPairGuide.background3 - Background color of inactive bracket pair guides (3)
439• editorBracketPairGuide.background4 - Background color of inactive bracket pair guides (4)
440• editorBracketPairGuide.background5 - Background color of inactive bracket pair guides (5)
441• editorBracketPairGuide.background6 - Background color of inactive bracket pair guides (6)
442• editor.foldBackground - Background color for folded ranges
443• editor.foldPlaceholderForeground - Color of collapsed text after first line of folded range
444• editorOverviewRuler.background - Background color of editor overview ruler
445• editorOverviewRuler.border - Color of the overview ruler border
446• editorOverviewRuler.findMatchForeground - Overview ruler marker color for find matches
447• editorOverviewRuler.rangeHighlightForeground - Overview ruler marker color for highlighted ranges
448• editorOverviewRuler.selectionHighlightForeground - Overview ruler marker color for selection highlights
449• editorOverviewRuler.wordHighlightForeground - Overview ruler marker color for symbol highlights
450• editorOverviewRuler.wordHighlightStrongForeground - Overview ruler marker color for write-access symbol highlights
451• editorOverviewRuler.wordHighlightTextForeground - Overview ruler marker color of textual occurrence for symbol
452• editorOverviewRuler.modifiedForeground - Overview ruler marker color for modified content
453• editorOverviewRuler.addedForeground - Overview ruler marker color for added content
454• editorOverviewRuler.deletedForeground - Overview ruler marker color for deleted content
455• editorOverviewRuler.errorForeground - Overview ruler marker color for errors
456• editorOverviewRuler.warningForeground - Overview ruler marker color for warnings
457• editorOverviewRuler.infoForeground - Overview ruler marker color for infos
458• editorOverviewRuler.bracketMatchForeground - Overview ruler marker color for matching brackets
459• editorOverviewRuler.inlineChatInserted - Overview ruler marker color for inline chat inserted content
460• editorOverviewRuler.inlineChatRemoved - Overview ruler marker color for inline chat removed content
461• editorError.foreground - Foreground color of error squiggles in the editor
462• editorError.border - Border color of error boxes in the editor
463• editorError.background - Background color of error text in the editor
464• editorWarning.foreground - Foreground color of warning squiggles in the editor
465• editorWarning.border - Border color of warning boxes in the editor
466• editorWarning.background - Background color of warning text in the editor
467• editorInfo.foreground - Foreground color of info squiggles in the editor
468• editorInfo.border - Border color of info boxes in the editor
469• editorInfo.background - Background color of info text in the editor
470• editorHint.foreground - Foreground color of hints in the editor
471• editorHint.border - Border color of hint boxes in the editor
472• problemsErrorIcon.foreground - Color used for the problems error icon
473• problemsWarningIcon.foreground - Color used for the problems warning icon
474• problemsInfoIcon.foreground - Color used for the problems info icon
475• editorUnnecessaryCode.border - Border color of unnecessary source code in editor
476• editorUnnecessaryCode.opacity - Opacity of unnecessary source code in editor
477• editorGutter.background - Background color of the editor gutter
478• editorGutter.modifiedBackground - Editor gutter background color for lines that are modified
479• editorGutter.modifiedSecondaryBackground - Editor gutter secondary background color for lines that are modified
480• editorGutter.addedBackground - Editor gutter background color for lines that are added
481• editorGutter.addedSecondaryBackground - Editor gutter secondary background color for lines that are added
482• editorGutter.deletedBackground - Editor gutter background color for lines that are deleted
483• editorGutter.deletedSecondaryBackground - Editor gutter secondary background color for lines that are deleted
484• editorGutter.commentRangeForeground - Editor gutter decoration color for commenting ranges
485• editorGutter.commentGlyphForeground - Editor gutter decoration color for commenting glyphs
486• editorGutter.commentUnresolvedGlyphForeground - Editor gutter decoration color for commenting glyphs for unresolved comment threads
487• editorGutter.foldingControlForeground - Color of the folding control in the editor gutter
488• editorGutter.itemGlyphForeground - Editor gutter decoration color for gutter item glyphs
489• editorGutter.itemBackground - Editor gutter decoration color for gutter item background
490• editorCommentsWidget.resolvedBorder - Color of borders and arrow for resolved comments
491• editorCommentsWidget.unresolvedBorder - Color of borders and arrow for unresolved comments
492• editorCommentsWidget.rangeBackground - Color of background for comment ranges
493• editorCommentsWidget.rangeActiveBackground - Color of background for currently selected or hovered comment range
494• editorCommentsWidget.replyInputBackground - Background color for comment reply input box
495• activityBar.activeFocusBorder - Activity bar focus border for active item
496• activityBarTop.foreground - Active foreground when activity bar is on top
497• activityBarTop.activeBorder - Focus border for active item when on top
498• activityBarTop.inactiveForeground - Inactive foreground when activity bar is on top
499• activityBarTop.dropBorder - Drag and drop feedback when activity bar is on top
500• activityBarTop.background - Background when activity bar is set to top/bottom
501• activityBarTop.activeBackground - Background for active item when activity bar is on top/bottom
502 
503=== PROFILES ===
504• profileBadge.background - Profile badge background color
505• profileBadge.foreground - Profile badge foreground color
506• profiles.sashBorder - Color of Profiles editor splitview sash border
507 
508=== SIDE BAR ===
509• sideBar.background - Side Bar background color
510• sideBar.foreground - Side Bar foreground color
511• sideBar.border - Side Bar border color separating from editor
512• sideBar.dropBackground - Drag and drop feedback for side bar sections
513• sideBarTitle.foreground - Side Bar title foreground color
514• sideBarSectionHeader.background - Side Bar section header background
515• sideBarSectionHeader.foreground - Side Bar section header foreground
516• sideBarSectionHeader.border - Side bar section header border color
517• sideBarActivityBarTop.border - Border between activity bar and views when on top
518• sideBarTitle.background - Side bar title background color
519• sideBarTitle.border - Side bar title border separating from views
520• sideBarStickyScroll.background - Background of sticky scroll in side bar
521• sideBarStickyScroll.border - Border of sticky scroll in side bar
522• sideBarStickyScroll.shadow - Shadow of sticky scroll in side bar
523 
524=== MINIMAP ===
⋯ 723 lines hidden (lines 525–1247)
525• minimap.findMatchHighlight - Highlight color for search matches in minimap
526• minimap.selectionHighlight - Highlight color for editor selection in minimap
527• minimap.errorHighlight - Highlight color for errors in minimap
528• minimap.warningHighlight - Highlight color for warnings in minimap
529• minimap.background - Minimap background color
530• minimap.selectionOccurrenceHighlight - Minimap marker for repeating selections
531• minimap.foregroundOpacity - Opacity of foreground elements in minimap
532• minimap.infoHighlight - Minimap marker color for infos
533• minimapSlider.background - Minimap slider background color
534• minimapSlider.hoverBackground - Minimap slider background when hovering
535• minimapSlider.activeBackground - Minimap slider background when clicked
536• minimapGutter.addedBackground - Minimap gutter color for added content
537• minimapGutter.modifiedBackground - Minimap gutter color for modified content
538• minimapGutter.deletedBackground - Minimap gutter color for deleted content
539 
540=== EDITOR GROUPS & TABS ===
541• editorGroup.border - Color to separate multiple editor groups
542• editorGroup.dropBackground - Background when dragging editors around
543• editorGroupHeader.noTabsBackground - Background when using single tab
544• editorGroupHeader.tabsBackground - Background of the Tabs container
545• editorGroupHeader.tabsBorder - Border below editor tabs control
546• editorGroupHeader.border - Border between editor group header and editor
547• editorGroup.emptyBackground - Background of empty editor group
548• editorGroup.focusedEmptyBorder - Border of empty editor group that is focused
549• editorGroup.dropIntoPromptForeground - Foreground of text when dragging files
550• editorGroup.dropIntoPromptBackground - Background of text when dragging files
551• editorGroup.dropIntoPromptBorder - Border of text when dragging files
552• tab.activeBackground - Active Tab background in active group
553• tab.unfocusedActiveBackground - Active Tab background in inactive group
554• tab.activeForeground - Active Tab foreground in active group
555• tab.border - Border to separate Tabs from each other
556• tab.activeBorder - Bottom border for active tab
557• tab.selectedBorderTop - Border to top of selected tab
558• tab.selectedBackground - Background of selected tab
559• tab.selectedForeground - Foreground of selected tab
560• tab.dragAndDropBorder - Border between tabs during drag and drop
561• tab.unfocusedActiveBorder - Bottom border for active tab in inactive group
562• tab.activeBorderTop - Top border for active tab
563• tab.unfocusedActiveBorderTop - Top border for active tab in inactive group
564• tab.lastPinnedBorder - Border on right of last pinned editor
565• tab.inactiveBackground - Inactive Tab background color
566• tab.unfocusedInactiveBackground - Inactive Tab background in unfocused group
567• tab.inactiveForeground - Inactive Tab foreground in active group
568• tab.unfocusedActiveForeground - Active tab foreground in inactive group
569• tab.unfocusedInactiveForeground - Inactive tab foreground in inactive group
570• tab.hoverBackground - Tab background when hovering
571• tab.unfocusedHoverBackground - Tab background in unfocused group when hovering
572• tab.hoverForeground - Tab foreground when hovering
573• tab.unfocusedHoverForeground - Tab foreground in unfocused group when hovering
574• tab.hoverBorder - Border to highlight tabs when hovering
575• tab.unfocusedHoverBorder - Border to highlight tabs in unfocused group when hovering
576• tab.activeModifiedBorder - Border on top of modified active tabs in active group
577• tab.inactiveModifiedBorder - Border on top of modified inactive tabs in active group
578• tab.unfocusedActiveModifiedBorder - Border on top of modified active tabs in unfocused group
579• tab.unfocusedInactiveModifiedBorder - Border on top of modified inactive tabs in unfocused group
580• editorPane.background - Background of editor pane in centered layout
581• sideBySideEditor.horizontalBorder - Color to separate editors top to bottom
582• sideBySideEditor.verticalBorder - Color to separate editors left to right
583 
584=== EDITOR COLORS ===
585• editor.background - Editor background color
586• editor.foreground - Editor default foreground color
587• editorLineNumber.foreground - Color of editor line numbers
588• editorLineNumber.activeForeground - Color of active editor line number
589• editorLineNumber.dimmedForeground - Color of final line when renderFinalNewline is dimmed
590• editorCursor.background - Background color of editor cursor
591• editorCursor.foreground - Color of editor cursor
592• editorMultiCursor.primary.foreground - Color of primary cursor when multiple cursors
593• editorMultiCursor.primary.background - Background of primary cursor when multiple cursors
594• editorMultiCursor.secondary.foreground - Color of secondary cursors when multiple cursors
595• editorMultiCursor.secondary.background - Background of secondary cursors when multiple cursors
596• editor.placeholder.foreground - Foreground color of placeholder text
597• editor.compositionBorder - Border color for IME composition
598• editor.selectionBackground - Color of editor selection
599• editor.selectionForeground - Color of selected text for high contrast
600• editor.inactiveSelectionBackground - Color of selection in inactive editor
601• editor.selectionHighlightBackground - Color for regions with same content as selection
602• editor.selectionHighlightBorder - Border color for regions with same content as selection
603• editor.wordHighlightBackground - Background of symbol during read-access
604• editor.wordHighlightBorder - Border of symbol during read-access
605• editor.wordHighlightStrongBackground - Background of symbol during write-access
606• editor.wordHighlightStrongBorder - Border of symbol during write-access
607• editor.wordHighlightTextBackground - Background of textual occurrence for symbol
608• editor.wordHighlightTextBorder - Border of textual occurrence for symbol
609• editor.findMatchBackground - Color of current search match
610• editor.findMatchForeground - Text color of current search match
611• editor.findMatchHighlightForeground - Foreground color of other search matches
612• editor.findMatchHighlightBackground - Color of other search matches
613• editor.findRangeHighlightBackground - Color limiting search range
614• editor.findMatchBorder - Border color of current search match
615• editor.findMatchHighlightBorder - Border color of other search matches
616• editor.findRangeHighlightBorder - Border color limiting search range
617• search.resultsInfoForeground - Color of text in search completion message
618• searchEditor.findMatchBackground - Color of search editor results
619• searchEditor.findMatchBorder - Border color of search editor results
620• searchEditor.textInputBorder - Search editor text input box border
621• editor.hoverHighlightBackground - Highlight behind symbol for hover
622• editor.lineHighlightBackground - Background highlight of line at cursor
623• editor.lineHighlightBorder - Border around line at cursor position
624• editorWatermark.foreground - Foreground color for editor watermark labels
625• editorUnicodeHighlight.border - Border color for unicode character highlights
626• editorUnicodeHighlight.background - Background color for unicode character highlights
627• editorLink.activeForeground - Color of active links
628• editor.rangeHighlightBackground - Background of highlighted ranges
629• editor.rangeHighlightBorder - Border around highlighted ranges
630• editor.symbolHighlightBackground - Background of highlighted symbol
631• editor.symbolHighlightBorder - Border around highlighted symbols
632• editorWhitespace.foreground - Color of whitespace characters
633• editorIndentGuide.background - Color of editor indentation guides
634• editorIndentGuide.background1 - Color of editor indentation guides (1)
635• editorIndentGuide.background2 - Color of editor indentation guides (2)
636• editorIndentGuide.background3 - Color of editor indentation guides (3)
637• editorIndentGuide.background4 - Color of editor indentation guides (4)
638• editorIndentGuide.background5 - Color of editor indentation guides (5)
639• editorIndentGuide.background6 - Color of editor indentation guides (6)
640• editorIndentGuide.activeBackground - Color of active editor indentation guide
641• editorIndentGuide.activeBackground1 - Color of active editor indentation guides (1)
642• editorIndentGuide.activeBackground2 - Color of active editor indentation guides (2)
643• editorIndentGuide.activeBackground3 - Color of active editor indentation guides (3)
644• editorIndentGuide.activeBackground4 - Color of active editor indentation guides (4)
645• editorIndentGuide.activeBackground5 - Color of active editor indentation guides (5)
646• editorIndentGuide.activeBackground6 - Color of active editor indentation guides (6)
647• editorInlayHint.background - Background color of inline hints
648• editorInlayHint.foreground - Foreground color of inline hints
649• editorInlayHint.typeForeground - Foreground color of inline hints for types
650• editorInlayHint.typeBackground - Background color of inline hints for types
651• editorInlayHint.parameterForeground - Foreground color of inline hints for parameters
652• editorInlayHint.parameterBackground - Background color of inline hints for parameters
653• editorRuler.foreground - Color of editor rulers
654• editor.linkedEditingBackground - Background color when editor is in linked editing mode
655• editorCodeLens.foreground - Foreground color of editor CodeLens
656• editorLightBulb.foreground - Color for lightbulb actions icon
657• editorLightBulbAutoFix.foreground - Color for lightbulb auto fix actions icon
658• editorLightBulbAi.foreground - Color for lightbulb AI icon
659• editorBracketMatch.background - Background color behind matching brackets
660• editorBracketMatch.border - Color for matching brackets boxes
661• editorBracketHighlight.foreground1 - Foreground color of brackets (1)
662• editorBracketHighlight.foreground2 - Foreground color of brackets (2)
663• editorBracketHighlight.foreground3 - Foreground color of brackets (3)
664• editorBracketHighlight.foreground4 - Foreground color of brackets (4)
665• editorBracketHighlight.foreground5 - Foreground color of brackets (5)
666• editorBracketHighlight.foreground6 - Foreground color of brackets (6)
667• editorBracketHighlight.unexpectedBracket.foreground - Foreground color of unexpected brackets
668• editorBracketPairGuide.activeBackground1 - Background of active bracket pair guides (1)
669• editorBracketPairGuide.activeBackground2 - Background of active bracket pair guides (2)
670• editorBracketPairGuide.activeBackground3 - Background of active bracket pair guides (3)
671• editorBracketPairGuide.activeBackground4 - Background of active bracket pair guides (4)
672• editorBracketPairGuide.activeBackground5 - Background of active bracket pair guides (5)
673• editorBracketPairGuide.activeBackground6 - Background of active bracket pair guides (6)
674• editorBracketPairGuide.background1 - Background of inactive bracket pair guides (1)
675• editorBracketPairGuide.background2 - Background of inactive bracket pair guides (2)
676• editorBracketPairGuide.background3 - Background of inactive bracket pair guides (3)
677• editorBracketPairGuide.background4 - Background of inactive bracket pair guides (4)
678• editorBracketPairGuide.background5 - Background of inactive bracket pair guides (5)
679• editorBracketPairGuide.background6 - Background of inactive bracket pair guides (6)
680• editor.foldBackground - Background color for folded ranges
681• editor.foldPlaceholderForeground - Color of collapsed text after first line of folded range
682• editorOverviewRuler.background - Background of editor overview ruler
683• editorOverviewRuler.border - Color of overview ruler border
684• editorOverviewRuler.findMatchForeground - Overview ruler marker for find matches
685• editorOverviewRuler.rangeHighlightForeground - Overview ruler marker for highlighted ranges
686• editorOverviewRuler.selectionHighlightForeground - Overview ruler marker for selection highlights
687• editorOverviewRuler.wordHighlightForeground - Overview ruler marker for symbol highlights
688• editorOverviewRuler.wordHighlightStrongForeground - Overview ruler marker for write-access symbol highlights
689• editorOverviewRuler.wordHighlightTextForeground - Overview ruler marker for textual occurrence
690• editorOverviewRuler.modifiedForeground - Overview ruler marker for modified content
691• editorOverviewRuler.addedForeground - Overview ruler marker for added content
692• editorOverviewRuler.deletedForeground - Overview ruler marker for deleted content
693• editorOverviewRuler.errorForeground - Overview ruler marker for errors
694• editorOverviewRuler.warningForeground - Overview ruler marker for warnings
695• editorOverviewRuler.infoForeground - Overview ruler marker for infos
696• editorOverviewRuler.bracketMatchForeground - Overview ruler marker for matching brackets
697• editorError.foreground - Foreground color of error squiggles
698• editorError.border - Border color of error boxes
699• editorError.background - Background color of error text
700• editorWarning.foreground - Foreground color of warning squiggles
701• editorWarning.border - Border color of warning boxes
702• editorWarning.background - Background color of warning text
703• editorInfo.foreground - Foreground color of info squiggles
704• editorInfo.border - Border color of info boxes
705• editorInfo.background - Background color of info text
706• editorHint.foreground - Foreground color of hints
707• editorHint.border - Border color of hint boxes
708• problemsErrorIcon.foreground - Color for problems error icon
709• problemsWarningIcon.foreground - Color for problems warning icon
710• problemsInfoIcon.foreground - Color for problems info icon
711• editorUnnecessaryCode.border - Border color of unnecessary source code
712• editorUnnecessaryCode.opacity - Opacity of unnecessary source code
713• editorGutter.background - Background color of editor gutter
714• editorGutter.modifiedBackground - Editor gutter background for modified lines
715• editorGutter.modifiedSecondaryBackground - Editor gutter secondary background for modified lines
716• editorGutter.addedBackground - Editor gutter background for added lines
717• editorGutter.addedSecondaryBackground - Editor gutter secondary background for added lines
718• editorGutter.deletedBackground - Editor gutter background for deleted lines
719• editorGutter.deletedSecondaryBackground - Editor gutter secondary background for deleted lines
720• editorGutter.commentRangeForeground - Editor gutter decoration for commenting ranges
721• editorGutter.commentGlyphForeground - Editor gutter decoration for commenting glyphs
722• editorGutter.commentUnresolvedGlyphForeground - Editor gutter decoration for unresolved comment glyphs
723• editorGutter.foldingControlForeground - Color of folding control in editor gutter
724• editorCommentsWidget.resolvedBorder - Color of borders and arrow for resolved comments
725• editorCommentsWidget.unresolvedBorder - Color of borders and arrow for unresolved comments
726• editorCommentsWidget.rangeBackground - Color of background for comment ranges
727• editorCommentsWidget.rangeActiveBackground - Color of background for selected comment range
728 
729=== DIFF EDITOR COLORS ===
730• diffEditor.insertedTextBackground - Background for text that got inserted
731• diffEditor.insertedTextBorder - Outline color for text that got inserted
732• diffEditor.removedTextBackground - Background for text that got removed
733• diffEditor.removedTextBorder - Outline color for text that got removed
734• diffEditor.border - Border color between two text editors
735• diffEditor.diagonalFill - Color of diff editor diagonal fill
736• diffEditor.insertedLineBackground - Background for lines that got inserted
737• diffEditor.removedLineBackground - Background for lines that got removed
738• diffEditorGutter.insertedLineBackground - Background for margin where lines got inserted
739• diffEditorGutter.removedLineBackground - Background for margin where lines got removed
740• diffEditorOverview.insertedForeground - Diff overview ruler foreground for inserted content
741• diffEditorOverview.removedForeground - Diff overview ruler foreground for removed content
742• diffEditor.unchangedRegionBackground - Color of unchanged blocks in diff editor
743• diffEditor.unchangedRegionForeground - Foreground of unchanged blocks in diff editor
744• diffEditor.unchangedRegionShadow - Color of shadow around unchanged region widgets
745• diffEditor.unchangedCodeBackground - Background of unchanged code in diff editor
746• diffEditor.move.border - Border color for text that got moved
747• diffEditor.moveActive.border - Active border color for text that got moved
748 
749=== EDITOR WIDGET COLORS ===
750• editorWidget.foreground - Foreground color of editor widgets
751• editorWidget.background - Background color of editor widgets
752• editorWidget.border - Border color of editor widget
753• editorWidget.resizeBorder - Border color of resize bar of editor widgets
754• editorSuggestWidget.background - Background color of suggestion widget
755• editorSuggestWidget.border - Border color of suggestion widget
756• editorSuggestWidget.foreground - Foreground color of suggestion widget
757• editorSuggestWidget.focusHighlightForeground - Color of match highlights when item is focused
758• editorSuggestWidget.highlightForeground - Color of match highlights in suggestion widget
759• editorSuggestWidget.selectedBackground - Background of selected entry in suggestion widget
760• editorSuggestWidget.selectedForeground - Foreground of selected entry in suggest widget
761• editorSuggestWidget.selectedIconForeground - Icon foreground of selected entry in suggest widget
762• editorSuggestWidgetStatus.foreground - Foreground color of suggest widget status
763• editorHoverWidget.foreground - Foreground color of editor hover
764• editorHoverWidget.background - Background color of editor hover
765• editorHoverWidget.border - Border color of editor hover
766• editorHoverWidget.highlightForeground - Foreground color of active item in parameter hint
767• editorHoverWidget.statusBarBackground - Background of editor hover status bar
768• editorGhostText.border - Border color of ghost text
769• editorGhostText.background - Background color of ghost text
770• editorGhostText.foreground - Foreground color of ghost text
771• editorStickyScroll.background - Editor sticky scroll background color
772• editorStickyScroll.border - Border color of sticky scroll
773• editorStickyScroll.shadow - Shadow color of sticky scroll
774• editorStickyScrollHover.background - Editor sticky scroll on hover background
775• debugExceptionWidget.background - Exception widget background color
776• debugExceptionWidget.border - Exception widget border color
777• editorMarkerNavigation.background - Editor marker navigation widget background
778• editorMarkerNavigationError.background - Editor marker navigation widget error color
779• editorMarkerNavigationWarning.background - Editor marker navigation widget warning color
780• editorMarkerNavigationInfo.background - Editor marker navigation widget info color
781• editorMarkerNavigationError.headerBackground - Editor marker navigation widget error heading background
782• editorMarkerNavigationWarning.headerBackground - Editor marker navigation widget warning heading background
783• editorMarkerNavigationInfo.headerBackground - Editor marker navigation widget info heading background
784 
785=== PEEK VIEW COLORS ===
786• peekView.border - Color of peek view borders and arrow
787• peekViewEditor.background - Background color of peek view editor
788• peekViewEditorGutter.background - Background color of gutter in peek view editor
789• peekViewEditor.matchHighlightBackground - Match highlight color in peek view editor
790• peekViewEditor.matchHighlightBorder - Match highlight border in peek view editor
791• peekViewResult.background - Background color of peek view result list
792• peekViewResult.fileForeground - Foreground color for file nodes in result list
793• peekViewResult.lineForeground - Foreground color for line nodes in result list
794• peekViewResult.matchHighlightBackground - Match highlight color in result list
795• peekViewResult.selectionBackground - Background of selected entry in result list
796• peekViewResult.selectionForeground - Foreground of selected entry in result list
797• peekViewTitle.background - Background color of peek view title area
798• peekViewTitleDescription.foreground - Color of peek view title info
799• peekViewTitleLabel.foreground - Color of peek view title
800 
801=== MERGE CONFLICTS COLORS ===
802• merge.currentHeaderBackground - Current header background in inline merge conflicts
803• merge.currentContentBackground - Current content background in inline merge conflicts
804• merge.incomingHeaderBackground - Incoming header background in inline merge conflicts
805• merge.incomingContentBackground - Incoming content background in inline merge conflicts
806• merge.border - Border color on headers and splitter in inline merge conflicts
807• merge.commonContentBackground - Common ancestor content background
808• merge.commonHeaderBackground - Common ancestor header background
809• editorOverviewRuler.currentContentForeground - Current overview ruler foreground for merge conflicts
810• editorOverviewRuler.incomingContentForeground - Incoming overview ruler foreground for merge conflicts
811• editorOverviewRuler.commonContentForeground - Common ancestor overview ruler foreground
812 
813=== PANEL COLORS ===
814• panel.background - Panel background color
815• panel.border - Panel border color separating from editor
816• panel.dropBorder - Drag and drop feedback color for panel titles
817• panelTitle.activeBorder - Border color for active panel title
818• panelTitle.activeForeground - Title color for active panel
819• panelTitle.inactiveForeground - Title color for inactive panel
820• panelTitle.border - Panel title border separating title from views
821• panelInput.border - Input box border for inputs in panel
822• panelSection.border - Panel section border when multiple views stacked horizontally
823• panelSection.dropBackground - Drag and drop feedback for panel sections
824• panelSectionHeader.background - Panel section header background color
825• panelSectionHeader.foreground - Panel section header foreground color
826• panelSectionHeader.border - Panel section header border when multiple views stacked vertically
827 
828=== STATUS BAR COLORS ===
829• statusBar.background - Standard Status Bar background color
830• statusBar.foreground - Status Bar foreground color
831• statusBar.border - Status Bar border separating from editor
832• statusBar.debuggingBackground - Status Bar background when debugging
833• statusBar.debuggingForeground - Status Bar foreground when debugging
834• statusBar.debuggingBorder - Status Bar border when debugging
835• statusBar.noFolderForeground - Status Bar foreground when no folder opened
836• statusBar.noFolderBackground - Status Bar background when no folder opened
837• statusBar.noFolderBorder - Status Bar border when no folder opened
838• statusBarItem.activeBackground - Status Bar item background when clicking
839• statusBarItem.hoverForeground - Status bar item foreground when hovering
840• statusBarItem.hoverBackground - Status Bar item background when hovering
841• statusBarItem.prominentForeground - Status Bar prominent items foreground
842• statusBarItem.prominentBackground - Status Bar prominent items background
843• statusBarItem.prominentHoverForeground - Status bar prominent items foreground when hovering
844• statusBarItem.prominentHoverBackground - Status Bar prominent items background when hovering
845• statusBarItem.remoteBackground - Background for remote indicator on status bar
846• statusBarItem.remoteForeground - Foreground for remote indicator on status bar
847• statusBarItem.remoteHoverBackground - Background for remote indicator when hovering
848• statusBarItem.remoteHoverForeground - Foreground for remote indicator when hovering
849• statusBarItem.errorBackground - Status bar error items background
850• statusBarItem.errorForeground - Status bar error items foreground
851• statusBarItem.errorHoverBackground - Status bar error items background when hovering
852• statusBarItem.errorHoverForeground - Status bar error items foreground when hovering
853• statusBarItem.warningBackground - Status bar warning items background
854• statusBarItem.warningForeground - Status bar warning items foreground
855• statusBarItem.warningHoverBackground - Status bar warning items background when hovering
856• statusBarItem.warningHoverForeground - Status bar warning items foreground when hovering
857• statusBarItem.compactHoverBackground - Status bar item background when hovering item with two hovers
858• statusBarItem.focusBorder - Status bar item border when focused on keyboard navigation
859• statusBar.focusBorder - Status bar border when focused on keyboard navigation
860• statusBarItem.offlineBackground - Status bar item background when workbench is offline
861• statusBarItem.offlineForeground - Status bar item foreground when workbench is offline
862• statusBarItem.offlineHoverForeground - Status bar item foreground hover when workbench is offline
863• statusBarItem.offlineHoverBackground - Status bar item background hover when workbench is offline
864 
865=== TITLE BAR COLORS ===
866• titleBar.activeBackground - Title Bar background when window is active
867• titleBar.activeForeground - Title Bar foreground when window is active
868• titleBar.inactiveBackground - Title Bar background when window is inactive
869• titleBar.inactiveForeground - Title Bar foreground when window is inactive
870• titleBar.border - Title bar border color
871 
872=== MENU BAR COLORS ===
873• menubar.selectionForeground - Foreground of selected menu item in menubar
874• menubar.selectionBackground - Background of selected menu item in menubar
875• menubar.selectionBorder - Border of selected menu item in menubar
876• menu.foreground - Foreground color of menu items
877• menu.background - Background color of menu items
878• menu.selectionForeground - Foreground of selected menu item in menus
879• menu.selectionBackground - Background of selected menu item in menus
880• menu.selectionBorder - Border of selected menu item in menus
881• menu.separatorBackground - Color of separator menu item in menus
882• menu.border - Border color of menus
883 
884=== COMMAND CENTER COLORS ===
885• commandCenter.foreground - Foreground color of Command Center
886• commandCenter.activeForeground - Active foreground color of Command Center
887• commandCenter.background - Background color of Command Center
888• commandCenter.activeBackground - Active background color of Command Center
889• commandCenter.border - Border color of Command Center
890• commandCenter.inactiveForeground - Foreground when window is inactive
891• commandCenter.inactiveBorder - Border when window is inactive
892• commandCenter.activeBorder - Active border color of Command Center
893• commandCenter.debuggingBackground - Command Center background when debugging
894 
895=== NOTIFICATION COLORS ===
896• notificationCenter.border - Notification Center border color
897• notificationCenterHeader.foreground - Notification Center header foreground
898• notificationCenterHeader.background - Notification Center header background
899• notificationToast.border - Notification toast border color
900• notifications.foreground - Notification foreground color
901• notifications.background - Notification background color
902• notifications.border - Notification border separating from other notifications
903• notificationLink.foreground - Notification links foreground color
904• notificationsErrorIcon.foreground - Color for notification error icon
905• notificationsWarningIcon.foreground - Color for notification warning icon
906• notificationsInfoIcon.foreground - Color for notification info icon
907 
908=== BANNER COLORS ===
909• banner.background - Banner background color
910• banner.foreground - Banner foreground color
911• banner.iconForeground - Color for icon in front of banner text
912 
913=== EXTENSIONS COLORS ===
914• extensionButton.prominentForeground - Extension view button foreground
915• extensionButton.prominentBackground - Extension view button background
916• extensionButton.prominentHoverBackground - Extension view button background hover
917• extensionButton.background - Button background for extension actions
918• extensionButton.foreground - Button foreground for extension actions
919• extensionButton.hoverBackground - Button background hover for extension actions
920• extensionButton.separator - Button separator for extension actions
921• extensionBadge.remoteBackground - Background for remote badge in extensions view
922• extensionBadge.remoteForeground - Foreground for remote badge in extensions view
923• extensionIcon.starForeground - Icon color for extension ratings
924• extensionIcon.verifiedForeground - Icon color for extension verified publisher
925• extensionIcon.preReleaseForeground - Icon color for pre-release extension
926• extensionIcon.sponsorForeground - Icon color for extension sponsor
927• extensionIcon.privateForeground - Icon color for private extensions
928 
929=== QUICK PICKER COLORS ===
930• pickerGroup.border - Quick picker color for grouping borders
931• pickerGroup.foreground - Quick picker color for grouping labels
932• quickInput.background - Quick input background color
933• quickInput.foreground - Quick input foreground color
934• quickInputList.focusBackground - Quick picker background for focused item
935• quickInputList.focusForeground - Quick picker foreground for focused item
936• quickInputList.focusIconForeground - Quick picker icon foreground for focused item
937• quickInputTitle.background - Quick picker title background color
938 
939=== KEYBINDING LABEL COLORS ===
940• keybindingLabel.background - Keybinding label background color
941• keybindingLabel.foreground - Keybinding label foreground color
942• keybindingLabel.border - Keybinding label border color
943• keybindingLabel.bottomBorder - Keybinding label border bottom color
944 
945=== KEYBOARD SHORTCUT TABLE COLORS ===
946• keybindingTable.headerBackground - Background for keyboard shortcuts table header
947• keybindingTable.rowsBackground - Background for keyboard shortcuts table alternating rows
948 
949=== INTEGRATED TERMINAL COLORS ===
950• terminal.background - Background of Integrated Terminal viewport
951• terminal.border - Color of border separating split panes in terminal
952• terminal.foreground - Default foreground color of Integrated Terminal
953• terminal.ansiBlack - 'Black' ANSI color in terminal
954• terminal.ansiBlue - 'Blue' ANSI color in terminal
955• terminal.ansiBrightBlack - 'BrightBlack' ANSI color in terminal
956• terminal.ansiBrightBlue - 'BrightBlue' ANSI color in terminal
957• terminal.ansiBrightCyan - 'BrightCyan' ANSI color in terminal
958• terminal.ansiBrightGreen - 'BrightGreen' ANSI color in terminal
959• terminal.ansiBrightMagenta - 'BrightMagenta' ANSI color in terminal
960• terminal.ansiBrightRed - 'BrightRed' ANSI color in terminal
961• terminal.ansiBrightWhite - 'BrightWhite' ANSI color in terminal
962• terminal.ansiBrightYellow - 'BrightYellow' ANSI color in terminal
963• terminal.ansiCyan - 'Cyan' ANSI color in terminal
964• terminal.ansiGreen - 'Green' ANSI color in terminal
965• terminal.ansiMagenta - 'Magenta' ANSI color in terminal
966• terminal.ansiRed - 'Red' ANSI color in terminal
967• terminal.ansiWhite - 'White' ANSI color in terminal
968• terminal.ansiYellow - 'Yellow' ANSI color in terminal
969• terminal.selectionBackground - Selection background color of terminal
970• terminal.selectionForeground - Selection foreground color of terminal
971• terminal.inactiveSelectionBackground - Selection background when terminal doesn't have focus
972• terminal.findMatchBackground - Color of current search match in terminal
973• terminal.findMatchBorder - Border color of current search match in terminal
974• terminal.findMatchHighlightBackground - Color of other search matches in terminal
975• terminal.findMatchHighlightBorder - Border color of other search matches in terminal
976• terminal.hoverHighlightBackground - Color of highlight when hovering link in terminal
977• terminalCursor.background - Background color of terminal cursor
978• terminalCursor.foreground - Foreground color of terminal cursor
979• terminal.dropBackground - Background when dragging on top of terminals
980• terminal.tab.activeBorder - Border on side of terminal tab in panel
981• terminalCommandDecoration.defaultBackground - Default terminal command decoration background
982• terminalCommandDecoration.successBackground - Terminal command decoration background for successful commands
983• terminalCommandDecoration.errorBackground - Terminal command decoration background for error commands
984• terminalOverviewRuler.cursorForeground - Overview ruler cursor color
985• terminalOverviewRuler.findMatchForeground - Overview ruler marker for find matches in terminal
986• terminalStickyScroll.background - Background of sticky scroll overlay in terminal
987• terminalStickyScroll.border - Border of sticky scroll overlay in terminal
988• terminalStickyScrollHover.background - Background of sticky scroll overlay when hovered
989• terminal.initialHintForeground - Foreground color of terminal initial hint
990 
991=== DEBUG COLORS ===
992• debugToolBar.background - Debug toolbar background color
993• debugToolBar.border - Debug toolbar border color
994• editor.stackFrameHighlightBackground - Background of top stack frame highlight
995• editor.focusedStackFrameHighlightBackground - Background of focused stack frame highlight
996• editor.inlineValuesForeground - Color for debug inline value text
997• editor.inlineValuesBackground - Color for debug inline value background
998• debugView.exceptionLabelForeground - Foreground for exception label in CALL STACK view
999• debugView.exceptionLabelBackground - Background for exception label in CALL STACK view
1000• debugView.stateLabelForeground - Foreground for state label in CALL STACK view
1001• debugView.stateLabelBackground - Background for state label in CALL STACK view
1002• debugView.valueChangedHighlight - Color for value changes in debug views
1003• debugTokenExpression.name - Foreground for token names in debug views
1004• debugTokenExpression.value - Foreground for token values in debug views
1005• debugTokenExpression.string - Foreground for strings in debug views
1006• debugTokenExpression.boolean - Foreground for booleans in debug views
1007• debugTokenExpression.number - Foreground for numbers in debug views
1008• debugTokenExpression.error - Foreground for expression errors in debug views
1009• debugTokenExpression.type - Foreground for token types in debug views
1011=== DEBUG ICONS COLORS ===
1012• debugIcon.breakpointForeground - Icon color for breakpoints
1013• debugIcon.breakpointDisabledForeground - Icon color for disabled breakpoints
1014• debugIcon.breakpointUnverifiedForeground - Icon color for unverified breakpoints
1015• debugIcon.breakpointCurrentStackframeForeground - Icon color for current breakpoint stack frame
1016• debugIcon.breakpointStackframeForeground - Icon color for all breakpoint stack frames
1017• debugIcon.startForeground - Debug toolbar icon for start debugging
1018• debugIcon.pauseForeground - Debug toolbar icon for pause
1019• debugIcon.stopForeground - Debug toolbar icon for stop
1020• debugIcon.disconnectForeground - Debug toolbar icon for disconnect
1021• debugIcon.restartForeground - Debug toolbar icon for restart
1022• debugIcon.stepOverForeground - Debug toolbar icon for step over
1023• debugIcon.stepIntoForeground - Debug toolbar icon for step into
1024• debugIcon.stepOutForeground - Debug toolbar icon for step over
1025• debugIcon.continueForeground - Debug toolbar icon for continue
1026• debugIcon.stepBackForeground - Debug toolbar icon for step back
1027• debugConsole.infoForeground - Foreground for info messages in debug REPL console
1028• debugConsole.warningForeground - Foreground for warning messages in debug REPL console
1029• debugConsole.errorForeground - Foreground for error messages in debug REPL console
1030• debugConsole.sourceForeground - Foreground for source filenames in debug REPL console
1031• debugConsoleInputIcon.foreground - Foreground for debug console input marker icon
1033=== TESTING COLORS ===
1034• testing.runAction - Color for 'run' icons in editor
1035• testing.iconErrored - Color for 'Errored' icon in test explorer
1036• testing.iconFailed - Color for 'failed' icon in test explorer
1037• testing.iconPassed - Color for 'passed' icon in test explorer
1038• testing.iconQueued - Color for 'Queued' icon in test explorer
1039• testing.iconUnset - Color for 'Unset' icon in test explorer
1040• testing.iconSkipped - Color for 'Skipped' icon in test explorer
1041• testing.peekBorder - Color of peek view borders and arrow
1042• testing.peekHeaderBackground - Color of peek view borders and arrow
1043• testing.message.error.lineBackground - Margin color beside error messages inline in editor
1044• testing.message.info.decorationForeground - Text color of test info messages inline in editor
1045• testing.message.info.lineBackground - Margin color beside info messages inline in editor
1046• testing.coveredBackground - Background color of text that was covered
1047• testing.coveredBorder - Border color of text that was covered
1048• testing.coveredGutterBackground - Gutter color of regions where code was covered
1049• testing.uncoveredBackground - Background color of text that was not covered
1050• testing.uncoveredBorder - Border color of text that was not covered
1051• testing.uncoveredGutterBackground - Gutter color of regions where code not covered
1053=== WELCOME PAGE COLORS ===
1054• welcomePage.background - Background color for Welcome page
1055• welcomePage.progress.background - Foreground color for Welcome page progress bars
1056• welcomePage.progress.foreground - Background color for Welcome page progress bars
1057• welcomePage.tileBackground - Background color for tiles on Welcome page
1058• welcomePage.tileHoverBackground - Hover background color for tiles on Welcome page
1059• welcomePage.tileBorder - Border color for tiles on Welcome page
1060• walkThrough.embeddedEditorBackground - Background for embedded editors on Interactive Playground
1061• walkthrough.stepTitle.foreground - Foreground color of heading of each walkthrough step
1063=== GIT COLORS ===
1064• gitDecoration.addedResourceForeground - Color for added Git resources
1065• gitDecoration.modifiedResourceForeground - Color for modified Git resources
1066• gitDecoration.deletedResourceForeground - Color for deleted Git resources
1067• gitDecoration.renamedResourceForeground - Color for renamed or copied Git resources
1068• gitDecoration.stageModifiedResourceForeground - Color for staged modifications git decorations
1069• gitDecoration.stageDeletedResourceForeground - Color for staged deletions git decorations
1070• gitDecoration.untrackedResourceForeground - Color for untracked Git resources
1071• gitDecoration.ignoredResourceForeground - Color for ignored Git resources
1072• gitDecoration.conflictingResourceForeground - Color for conflicting Git resources
1073• gitDecoration.submoduleResourceForeground - Color for submodule resources
1075=== SETTINGS EDITOR COLORS ===
1076• settings.headerForeground - Foreground color for section header or active title
1077• settings.modifiedItemIndicator - Line that indicates a modified setting
1078• settings.dropdownBackground - Dropdown background
1079• settings.dropdownForeground - Dropdown foreground
1080• settings.dropdownBorder - Dropdown border
1081• settings.dropdownListBorder - Dropdown list border
1082• settings.checkboxBackground - Checkbox background
1083• settings.checkboxForeground - Checkbox foreground
1084• settings.checkboxBorder - Checkbox border
1085• settings.rowHoverBackground - Background of settings row when hovered
1086• settings.textInputBackground - Text input box background
1087• settings.textInputForeground - Text input box foreground
1088• settings.textInputBorder - Text input box border
1089• settings.numberInputBackground - Number input box background
1090• settings.numberInputForeground - Number input box foreground
1091• settings.numberInputBorder - Number input box border
1092• settings.focusedRowBackground - Background of focused setting row
1093• settings.focusedRowBorder - Color of row's top and bottom border when focused
1094• settings.headerBorder - Color of header container border
1095• settings.sashBorder - Color of Settings editor splitview sash border
1096• settings.settingsHeaderHoverForeground - Foreground for section header or hovered title
1098=== BREADCRUMBS COLORS ===
1099• breadcrumb.foreground - Color of breadcrumb items
1100• breadcrumb.background - Background color of breadcrumb items
1101• breadcrumb.focusForeground - Color of focused breadcrumb items
1102• breadcrumb.activeSelectionForeground - Color of selected breadcrumb items
1103• breadcrumbPicker.background - Background color of breadcrumb item picker
1105=== SNIPPETS COLORS ===
1106• editor.snippetTabstopHighlightBackground - Highlight background color of snippet tabstop
1107• editor.snippetTabstopHighlightBorder - Highlight border color of snippet tabstop
1108• editor.snippetFinalTabstopHighlightBackground - Highlight background color of final tabstop of snippet
1109• editor.snippetFinalTabstopHighlightBorder - Highlight border color of final tabstop of snippet
1111=== SYMBOL ICONS COLORS ===
1112• symbolIcon.arrayForeground - Foreground color for array symbols
1113• symbolIcon.booleanForeground - Foreground color for boolean symbols
1114• symbolIcon.classForeground - Foreground color for class symbols
1115• symbolIcon.colorForeground - Foreground color for color symbols
1116• symbolIcon.constantForeground - Foreground color for constant symbols
1117• symbolIcon.constructorForeground - Foreground color for constructor symbols
1118• symbolIcon.enumeratorForeground - Foreground color for enumerator symbols
1119• symbolIcon.enumeratorMemberForeground - Foreground color for enumerator member symbols
1120• symbolIcon.eventForeground - Foreground color for event symbols
1121• symbolIcon.fieldForeground - Foreground color for field symbols
1122• symbolIcon.fileForeground - Foreground color for file symbols
1123• symbolIcon.folderForeground - Foreground color for folder symbols
1124• symbolIcon.functionForeground - Foreground color for function symbols
1125• symbolIcon.interfaceForeground - Foreground color for interface symbols
1126• symbolIcon.keyForeground - Foreground color for key symbols
1127• symbolIcon.keywordForeground - Foreground color for keyword symbols
1128• symbolIcon.methodForeground - Foreground color for method symbols
1129• symbolIcon.moduleForeground - Foreground color for module symbols
1130• symbolIcon.namespaceForeground - Foreground color for namespace symbols
1131• symbolIcon.nullForeground - Foreground color for null symbols
1132• symbolIcon.numberForeground - Foreground color for number symbols
1133• symbolIcon.objectForeground - Foreground color for object symbols
1134• symbolIcon.operatorForeground - Foreground color for operator symbols
1135• symbolIcon.packageForeground - Foreground color for package symbols
1136• symbolIcon.propertyForeground - Foreground color for property symbols
1137• symbolIcon.referenceForeground - Foreground color for reference symbols
1138• symbolIcon.snippetForeground - Foreground color for snippet symbols
1139• symbolIcon.stringForeground - Foreground color for string symbols
1140• symbolIcon.structForeground - Foreground color for struct symbols
1141• symbolIcon.textForeground - Foreground color for text symbols
1142• symbolIcon.typeParameterForeground - Foreground color for type parameter symbols
1143• symbolIcon.unitForeground - Foreground color for unit symbols
1144• symbolIcon.variableForeground - Foreground color for variable symbols
1146=== NOTEBOOK COLORS ===
1147• notebook.editorBackground - Notebook background color
1148• notebook.cellBorderColor - Border color for notebook cells
1149• notebook.cellHoverBackground - Background when cell is hovered
1150• notebook.cellInsertionIndicator - Color of notebook cell insertion indicator
1151• notebook.cellStatusBarItemHoverBackground - Background of notebook cell status bar items
1152• notebook.cellToolbarSeparator - Color of separator in cell bottom toolbar
1153• notebook.cellEditorBackground - Color of notebook cell editor background
1154• notebook.focusedCellBackground - Background when cell is focused
1155• notebook.focusedCellBorder - Color of cell's focus indicator borders when focused
1156• notebook.focusedEditorBorder - Color of notebook cell editor border
1157• notebook.inactiveFocusedCellBorder - Color of cell's borders when focused but primary focus is outside
1158• notebook.inactiveSelectedCellBorder - Color of cell's borders when multiple cells are selected
1159• notebook.outputContainerBackgroundColor - Color of notebook output container background
1160• notebook.outputContainerBorderColor - Border color of notebook output container
1161• notebook.selectedCellBackground - Background when cell is selected
1162• notebook.selectedCellBorder - Color of cell's top and bottom border when selected but not focused
1163• notebook.symbolHighlightBackground - Background color of highlighted cell
1164• notebookScrollbarSlider.activeBackground - Notebook scrollbar slider background when clicked
1165• notebookScrollbarSlider.background - Notebook scrollbar slider background color
1166• notebookScrollbarSlider.hoverBackground - Notebook scrollbar slider background when hovering
1167• notebookStatusErrorIcon.foreground - Error icon color of notebook cells in status bar
1168• notebookStatusRunningIcon.foreground - Running icon color of notebook cells in status bar
1169• notebookStatusSuccessIcon.foreground - Success icon color of notebook cells in status bar
1171=== CHART COLORS ===
1172• charts.foreground - Contrast color for text in charts
1173• charts.lines - Color for lines in charts
1174• charts.red - Color for red elements in charts
1175• charts.blue - Color for blue elements in charts
1176• charts.yellow - Color for yellow elements in charts
1177• charts.orange - Color for orange elements in charts
1178• charts.green - Color for green elements in charts
1179• charts.purple - Color for purple elements in charts
1181Common token scopes: comment, keyword, string, number, constant, variable, function, class, type, operator, punctuation, storage, support, entity, invalid, markup
1183MESSAGE EXAMPLES (be creative and match the theme vibe):
1185FOR "CYBERPUNK NEON" THEMES:
1186- MESSAGE:Jacking into editor...
1187- MESSAGE:Activity bar neon: ON ⚡
1188- MESSAGE:Sidebar cyber-glow ready
1189- MESSAGE:Status bar armed & dangerous
1191FOR "COZY AUTUMN" THEMES:
1192- MESSAGE:Warming editor background...
1193- MESSAGE:Sidebar autumn leaves 🍂
1194- MESSAGE:Terminal pumpkin spice
1195- MESSAGE:Activity bar golden glow
1197FOR "OCEAN/WATER" THEMES:
1198- MESSAGE:Diving into editor depths...
1199- MESSAGE:Sidebar sea-green ready
1200- MESSAGE:Status bar coral accents
1201- MESSAGE:Terminal underwater blue
1203FOR "FOREST/NATURE" THEMES:
1204- MESSAGE:Growing moss-green editor...
1205- MESSAGE:Activity bar oak bark
1206- MESSAGE:Sidebar forest wisdom
1207- MESSAGE:Terminal spring meadow 🌸
1209FOR "MINIMAL/CLEAN" THEMES:
1210- MESSAGE:Zen editor achieved ✨
1211- MESSAGE:Essential sidebar colors
1212- MESSAGE:Clean status bar lines
1213- MESSAGE:Terminal noise deleted
1215FOR "RETRO/80S" THEMES:
1216- MESSAGE:Editor rad vibes loading
1217- MESSAGE:Sidebar totally awesome 📼
1218- MESSAGE:Status bar neon pink
1219- MESSAGE:Terminal disco fever
1221FOR "SPACE/COSMIC" THEMES:
1222- MESSAGE:Editor space launch 🚀
1223- MESSAGE:Sidebar orbiting colors
1224- MESSAGE:Status bar binary stars
1225- MESSAGE:Terminal cosmic dust
1227FOR "COFFEE/WARM" THEMES:
1228- MESSAGE:Brewing warm editor...
1229- MESSAGE:Sidebar espresso ready ☕
1230- MESSAGE:Status bar cream swirl
1231- MESSAGE:Terminal coffee comfort
1233KEEP MESSAGES SHORT (under 30 chars when possible) for notification window readability!
1235GENERAL CREATIVE PATTERNS:
1236- Always mention specific UI components being styled (editor, sidebar, status bar, activity bar, terminal)
1237- Use wordplay and puns that connect both the theme and the actual UI element
1238- Add occasional rhymes or alliteration for extra charm
1239- Include relevant emojis sparingly for visual flair
1240- Progress through different UI areas as the theme unfolds
1241- Be slightly meta about the theming process itself
1242- Match the energy/mood of the theme (cozy vs energetic vs mysterious)
1243- Make it clear which part of the interface is being themed
1245Remember: Each message should feel like it belongs to the specific theme being generated AND clearly reference what UI component is actually being styled at that moment!
1247CRITICAL: Always analyze the user's theme description and craft messages that match their specific vibe. If they say "cyberpunk neon city", your messages should be tech/neon themed. If they say "cozy autumn evening", your messages should be warm/seasonal. Don't use generic messages - make each one feel custom-crafted for their exact theme request!
Act I · A vibe becomes a theme1.5🎬 Annotated walkthrough

The streaming loop

src/services/themeGenerationService.ts

runThemeGenerationWorkflow is the heart of the extension and its longest function. First it ensures the client and gets the prompt. If a theme is already applied, it injects that as context so the model can iterate. Then it opens a streamed chat completion and consumes it inside a cancellable progress notification.

Open the stream, buffer by line, parse-and-apply each setting live.

src/services/themeGenerationService.ts · 434 lines
src/services/themeGenerationService.ts434 lines · TypeScript
⋯ 201 lines hidden (lines 1–201)
1import * as vscode from 'vscode';
2import { ensureOpenAIClient as ensureOpenAIClientCore, getCurrentClientState } from './openaiCore';
3import { applyStreamingThemeSetting, parseStreamingThemeLine, StreamingThemeSetting, getCurrentThemeState } from './themeCore';
4import { getSelectedOpenAIModel } from '../commands/modelSelectCommand';
5import { OpenAIServiceResult, OpenAIServiceError } from '../types/theme';
6import { showThemePromptPicker } from '../utils/promptPicker.js';
7import * as fs from "fs";
8import * as path from "path";
9 
10/**
11 * Handles user choice when theme generation is cancelled with partial results.
12 * Offers to keep the partial theme or reset to the previous state.
13 */
14async function handleCancellationChoice(
15 settingsApplied: number,
16 themeDescription: string
17): Promise<'keep' | 'reset'> {
18 if (settingsApplied === 0) {
19 // No settings applied, just acknowledge cancellation
20 await vscode.window.showInformationMessage(
21 '🚫 Theme generation cancelled. No changes were made.',
22 { modal: true }
23 );
24 return 'keep'; // Nothing to reset
25 }
26 
27 const message = `🛑 Theme generation was cancelled after applying ${settingsApplied} settings.
28 
29Would you like to keep the partial theme or reset to your previous state?
30 
31Keep: Maintain the ${settingsApplied} color settings that were already applied
32Reset: Remove all applied settings and return to your original theme`;
33 
34 const choice = await vscode.window.showWarningMessage(
35 message,
36 {
37 modal: true,
38 detail: 'The partial theme may look incomplete since not all UI elements were styled.'
39 },
40 'Keep Partial Theme',
41 'Reset to Original'
42 );
43 
44 return choice === 'Reset to Original' ? 'reset' : 'keep';
46 
47/**
48 * Shows a success popup after streaming theme application with recap and reset option.
49 * Emphasizes the importance of using the reset command to remove the theme.
50 */
51async function showStreamingThemeSuccessPopup(
52 themeDescription: string,
53 settingsApplied: number,
54 context: vscode.ExtensionContext
55): Promise<void> {
56 const completenessMessage = settingsApplied < 50
57 ? "⚠️ Partial theme generated - you may want to regenerate for more comprehensive styling"
58 : settingsApplied < 80
59 ? "✅ Good theme coverage - most UI elements should be styled"
60 : "🎨 Comprehensive theme generated - all major UI areas styled!";
62 const message = `🎨 Theme "${themeDescription}" applied successfully!\n\nApplied ${settingsApplied} color settings in real-time\n\n${completenessMessage}\n\n⚠️ IMPORTANT: This theme overrides your VS Code settings. Use "Reset Theme Customizations" command to remove it and return to your original theme.`;
64 const action = await vscode.window.showInformationMessage(
65 message,
66 {
67 modal: true,
68 detail: 'Your theme was applied live as AI generated each setting. To return to your original theme, you must use the Reset command - simply changing themes in VS Code settings will not remove these customizations.'
69 },
70 'Keep Theme',
71 'Reset Theme (Remove All Customizations)'
72 );
73 
74 if (action === 'Reset Theme (Remove All Customizations)') {
75 // Execute the reset theme command
76 await vscode.commands.executeCommand('vibeThemer.resetTheme');
77 }
79 
80/**
81 * Formats current theme state into AI-readable context for better iteration support.
82 * Returns formatted string describing existing theme customizations.
83 */
84function formatCurrentThemeContext(): string {
85 const themeStateResult = getCurrentThemeState();
87 if (!themeStateResult.success) {
88 return ''; // No context on error - fallback to standard generation
89 }
91 const { state } = themeStateResult;
93 if (!state.hasCustomizations) {
94 return ''; // No existing customizations
95 }
97 const lines: string[] = [];
99 // Add color customizations context
100 const colorCount = Object.keys(state.colorCustomizations).length;
101 if (colorCount > 0) {
102 lines.push(`CURRENT THEME CONTEXT:`);
103 lines.push(`Active workbench color overrides (${colorCount} settings):`);
105 Object.entries(state.colorCustomizations).forEach(([key, value]) => {
106 lines.push(`- ${key}: ${value}`);
107 });
108 }
110 // Add token color customizations context
111 const tokenCount = Object.keys(state.tokenColorCustomizations).length;
112 if (tokenCount > 0) {
113 if (lines.length > 0) {
114 lines.push(''); // Add spacing
115 }
116 lines.push(`Active syntax highlighting overrides (${tokenCount} settings):`);
118 Object.entries(state.tokenColorCustomizations).forEach(([key, value]) => {
119 lines.push(`- ${key}: ${JSON.stringify(value)}`);
120 });
121 }
123 if (lines.length > 0) {
124 lines.push(''); // Add spacing before user prompt
125 lines.push('User request:');
126 return lines.join('\n');
127 }
129 return '';
131 
132/**
133 * Orchestrates the streaming theme generation workflow: prompts user, calls OpenAI with streaming, applies theme settings in real-time.
134 *
135 * This function demonstrates real-time theme application as the AI generates settings.
136 * Each setting is applied immediately as it's received, providing dynamic visual feedback.
137 *
138 * Updates the lastGeneratedThemeRef with the accumulated theme data.
139 */
140export async function runThemeGenerationWorkflow(
141 context: vscode.ExtensionContext,
142 lastGeneratedThemeRef: { current?: any }
143) {
144 // Use the enhanced OpenAI client with better error handling and state management
145 const openaiResult = await ensureOpenAIClientCore(context);
146 if (!openaiResult.success) {
147 // The ensureOpenAIClient already handled user interaction and error display
148 const clientState = getCurrentClientState();
149 if (clientState.status === 'error') {
150 console.error('Theme generation aborted due to OpenAI client error:', clientState.error);
151 }
152 return;
153 }
154 
155 const openai = openaiResult.data;
156 
157 const themeDescription = await showThemePromptPicker();
158 if (!themeDescription) {
159 vscode.window.showInformationMessage('No theme description provided. Try again when you\'re ready to create or modify your perfect coding atmosphere! 🎨', { modal: true });
160 return;
161 }
162 
163 const selectedModel = getSelectedOpenAIModel(context) || "gpt-4.1";
164 
165 // Streaming theme data accumulation
166 const accumulatedSelectors: Record<string, string> = {};
167 const accumulatedTokenColors: any[] = [];
168 let settingsApplied = 0;
169 let expectedSettingsCount = 120; // Default fallback value
170 let wasCancelled = false;
171 const hasWorkspaceFolders = (vscode.workspace.workspaceFolders?.length ?? 0) > 0;
172 let currentMessage = "🤖 AI analyzing your vibe...";
173 let lastMessageTime = Date.now(); // Initialize with current time
174 const MIN_MESSAGE_DURATION = 800; // Show messages for at least 0.8 seconds - quick but readable
175 
176 try {
177 await vscode.window.withProgress({
178 location: vscode.ProgressLocation.Notification,
179 title: "",
180 cancellable: true
181 }, async (progress, cancellationToken) => {
182 // Initialize progress tracking
183 (progress as any)._lastPercent = 0;
184 progress.report({ message: currentMessage, increment: 0 });
186 // Read streaming prompt using proper extension resource path
187 const promptPath = context.asAbsolutePath(path.join('prompts', 'streamingThemePrompt.txt'));
188 let streamingPrompt: string;
190 try {
191 streamingPrompt = fs.readFileSync(promptPath, 'utf8');
192 } catch (error) {
193 throw new Error(`Failed to load theme generation prompt from ${promptPath}. Please reinstall the extension. Error: ${error}`);
194 }
195 
196 // Inject current theme context for iteration support
197 const currentThemeContext = formatCurrentThemeContext();
198 const userPrompt = currentThemeContext
199 ? `${currentThemeContext}\n${themeDescription}`
200 : `Theme description: ${themeDescription}`;
201 
202 // Create streaming completion for comprehensive themes
203 const stream = await openai.chat.completions.create({
204 model: selectedModel,
205 messages: [
206 { role: "system", content: streamingPrompt },
207 { role: "user", content: userPrompt }
208 ],
209 stream: true
210 });
211 
212 let buffer = '';
213 let errorCount = 0;
214 const maxErrors = 5; // Allow some failed settings before giving up
215 
216 for await (const chunk of stream) {
217 // Check for cancellation at the start of each chunk
218 if (cancellationToken.isCancellationRequested) {
219 break;
220 }
221 
222 const content = chunk.choices[0]?.delta?.content || '';
223 buffer += content;
224 
225 // Process complete lines
226 const lines = buffer.split('\n');
227 buffer = lines.pop() || ''; // Keep incomplete line in buffer
228 
229 for (const line of lines) {
230 if (!line.trim()) {continue;}
232 // Check for cancellation before processing each line
233 if (cancellationToken.isCancellationRequested) {
234 break;
235 }
236 
237 const parseResult = parseStreamingThemeLine(line);
239 if (parseResult.success) {
240 const setting = parseResult.setting;
242 // Apply the setting immediately
243 const applyResult = await applyStreamingThemeSetting(setting, hasWorkspaceFolders);
245 if (applyResult.success) {
246 // Handle different setting types
⋯ 188 lines hidden (lines 247–434)
247 if (setting.type === 'count') {
248 expectedSettingsCount = setting.total;
249 currentMessage = `🎯 Planning ${setting.total} theme settings...`;
250 progress.report({ message: currentMessage });
252 } else if (setting.type === 'selector') {
253 settingsApplied++;
254 accumulatedSelectors[setting.name] = setting.color;
256 // Update progress percentage using actual expected count
257 const progressPercent = Math.min(Math.floor((settingsApplied / expectedSettingsCount) * 100), 99);
258 progress.report({
259 message: currentMessage,
260 increment: progressPercent - (progress as any)._lastPercent || 0
261 });
262 (progress as any)._lastPercent = progressPercent;
264 } else if (setting.type === 'token') {
265 settingsApplied++;
266 accumulatedTokenColors.push({
267 scope: setting.scope,
268 settings: {
269 foreground: setting.color,
270 ...(setting.fontStyle && { fontStyle: setting.fontStyle })
271 }
272 });
274 // Update progress percentage using actual expected count
275 const progressPercent = Math.min(Math.floor((settingsApplied / expectedSettingsCount) * 100), 99);
276 progress.report({
277 message: currentMessage,
278 increment: progressPercent - (progress as any)._lastPercent || 0
279 });
280 (progress as any)._lastPercent = progressPercent;
282 } else if (setting.type === 'message') {
283 // Only update message if enough time has passed or it's the first message
284 const now = Date.now();
285 if (now - lastMessageTime >= MIN_MESSAGE_DURATION || lastMessageTime === 0) {
286 currentMessage = `${setting.content}`;
287 lastMessageTime = now;
288 progress.report({ message: currentMessage });
289 }
290 }
291 } else {
292 errorCount++;
293 console.warn(`Failed to apply setting: ${line}`, applyResult.error);
295 if (errorCount >= maxErrors) {
296 throw new Error(`Too many failed settings (${errorCount}). Last error: ${applyResult.error.message}`);
297 }
298 }
299 } else {
300 errorCount++;
301 console.warn(`Failed to parse line: ${line}`, parseResult.error);
303 if (errorCount >= maxErrors) {
304 throw new Error(`Too many parsing errors (${errorCount}). Last error: ${parseResult.error}`);
305 }
306 }
307 }
309 // Break out of chunk loop if cancelled
310 if (cancellationToken.isCancellationRequested) {
311 break;
312 }
313 }
314 
315 // Handle cancellation if requested
316 if (cancellationToken.isCancellationRequested) {
317 wasCancelled = true;
318 const choice = await handleCancellationChoice(settingsApplied, themeDescription);
320 if (choice === 'reset') {
321 // Execute the reset theme command to remove all applied settings
322 await vscode.commands.executeCommand('vibeThemer.resetTheme');
323 return; // Exit early, no success popup needed
324 }
325 // If 'keep', continue with normal completion flow
326 }
327 
328 // Process any remaining content in buffer (only if not cancelled)
329 if (buffer.trim() && !cancellationToken.isCancellationRequested) {
330 const parseResult = parseStreamingThemeLine(buffer.trim());
331 if (parseResult.success) {
332 const applyResult = await applyStreamingThemeSetting(parseResult.setting, hasWorkspaceFolders);
333 if (applyResult.success) {
334 const setting = parseResult.setting;
335 if (setting.type === 'count') {
336 expectedSettingsCount = setting.total;
337 } else if (setting.type === 'selector') {
338 settingsApplied++;
339 accumulatedSelectors[setting.name] = setting.color;
340 } else if (setting.type === 'token') {
341 settingsApplied++;
342 accumulatedTokenColors.push({
343 scope: setting.scope,
344 settings: {
345 foreground: setting.color,
346 ...(setting.fontStyle && { fontStyle: setting.fontStyle })
347 }
348 });
349 }
350 // MESSAGE types don't need accumulation, they're just progress updates
351 }
352 }
353 }
354 
355 // Update final progress message based on whether it was cancelled
356 const finalMessage = wasCancelled
357 ? `🛑 Theme cancelled. Kept ${settingsApplied} settings.`
358 : `🎉 Theme complete! ${settingsApplied} colors applied`;
360 progress.report({ message: finalMessage, increment: 100 });
361 });
362 
363 // Build theme data structure for reference (always build it, even if cancelled)
364 const coreColors = {
365 primary: accumulatedSelectors["activityBar.background"] || "#007acc",
366 secondary: accumulatedSelectors["statusBar.background"] || "#444444",
367 accent: accumulatedSelectors["activityBarBadge.background"] || "#ff8c00",
368 background: accumulatedSelectors["editor.background"] || "#1e1e1e",
369 foreground: accumulatedSelectors["editor.foreground"] || "#d4d4d4"
370 };
371 
372 lastGeneratedThemeRef.current = {
373 name: `Custom Theme - ${themeDescription}`,
374 description: `Theme generated from: "${themeDescription}"`,
375 colors: coreColors,
376 tokenColors: accumulatedTokenColors
377 };
378 
379 // Show success popup only if generation completed normally (not cancelled)
380 if (!wasCancelled) {
381 await showStreamingThemeSuccessPopup(themeDescription, settingsApplied, context);
382 }
384 } catch (error: any) {
385 // Enhanced error handling for streaming
386 const isNetworkError = error.message.includes('fetch') ||
387 error.message.includes('network') ||
388 error.message.includes('connection');
390 const isAPIError = error.message.includes('API') ||
391 error.message.includes('429') ||
392 error.message.includes('quota');
394 const isStreamingError = error.message.includes('stream') ||
395 error.message.includes('parsing') ||
396 error.message.includes('settings');
397 
398 // Provide contextual error messages based on error type
399 let errorPrefix: string;
400 let suggestedAction: string | undefined;
401 
402 if (isNetworkError) {
403 errorPrefix = '🌐 Connection issue during theme generation';
404 suggestedAction = 'Please check your internet connection and try again';
405 } else if (isAPIError) {
406 errorPrefix = '🔑 OpenAI API issue during theme creation';
407 suggestedAction = 'Please check your API key validity and quota limits in your OpenAI dashboard';
408 } else if (isStreamingError) {
409 errorPrefix = '⚠️ Theme generation was interrupted';
410 suggestedAction = `${settingsApplied} settings were successfully applied before the interruption. You can try generating again to continue, or use "Reset Theme Customizations" to start fresh.`;
411 } else {
412 errorPrefix = '❌ Theme generation encountered an error';
413 suggestedAction = 'Please try again with a different description, or contact support if the issue persists';
414 }
415 
416 const fullMessage = suggestedAction
417 ? `${errorPrefix}: ${error.message}. ${suggestedAction}`
418 : `${errorPrefix}: ${error.message}`;
419 
420 vscode.window.showErrorMessage(fullMessage, { modal: true });
422 // Log detailed error information for debugging
423 console.error('Streaming theme generation error:', {
424 originalError: error,
425 errorType: isNetworkError ? 'network' : isAPIError ? 'api' : isStreamingError ? 'streaming' : 'unknown',
426 clientState: getCurrentClientState(),
427 themeDescription,
428 selectedModel,
429 settingsApplied,
430 accumulatedSelectors: Object.keys(accumulatedSelectors).length,
431 accumulatedTokenColors: accumulatedTokenColors.length
432 });
433 }

The buffering is the clever part. Network chunks do not respect line boundaries. So the loop accumulates into a buffer, splits on newlines, and holds the trailing partial line back for the next chunk. Each complete line is parsed and, if valid, applied at once. That is the live-repaint effect. Progress tracks against the model's own COUNT: estimate, or falls back to 120 when none arrives.

Cancellation is handled with care. The token is checked at the top of every chunk and every line. On cancel, the user chooses: keep the partial theme, or reset to the previous state. The error handler at the bottom sorts failures into network, API, or streaming by string-matching the error message, then picks a friendlier note. Brittle, but cosmetic.

Error classification by error.message.includes(...).

src/services/themeGenerationService.ts · 434 lines
src/services/themeGenerationService.ts434 lines · TypeScript
⋯ 383 lines hidden (lines 1–383)
1import * as vscode from 'vscode';
2import { ensureOpenAIClient as ensureOpenAIClientCore, getCurrentClientState } from './openaiCore';
3import { applyStreamingThemeSetting, parseStreamingThemeLine, StreamingThemeSetting, getCurrentThemeState } from './themeCore';
4import { getSelectedOpenAIModel } from '../commands/modelSelectCommand';
5import { OpenAIServiceResult, OpenAIServiceError } from '../types/theme';
6import { showThemePromptPicker } from '../utils/promptPicker.js';
7import * as fs from "fs";
8import * as path from "path";
9 
10/**
11 * Handles user choice when theme generation is cancelled with partial results.
12 * Offers to keep the partial theme or reset to the previous state.
13 */
14async function handleCancellationChoice(
15 settingsApplied: number,
16 themeDescription: string
17): Promise<'keep' | 'reset'> {
18 if (settingsApplied === 0) {
19 // No settings applied, just acknowledge cancellation
20 await vscode.window.showInformationMessage(
21 '🚫 Theme generation cancelled. No changes were made.',
22 { modal: true }
23 );
24 return 'keep'; // Nothing to reset
25 }
26 
27 const message = `🛑 Theme generation was cancelled after applying ${settingsApplied} settings.
28 
29Would you like to keep the partial theme or reset to your previous state?
30 
31Keep: Maintain the ${settingsApplied} color settings that were already applied
32Reset: Remove all applied settings and return to your original theme`;
33 
34 const choice = await vscode.window.showWarningMessage(
35 message,
36 {
37 modal: true,
38 detail: 'The partial theme may look incomplete since not all UI elements were styled.'
39 },
40 'Keep Partial Theme',
41 'Reset to Original'
42 );
43 
44 return choice === 'Reset to Original' ? 'reset' : 'keep';
46 
47/**
48 * Shows a success popup after streaming theme application with recap and reset option.
49 * Emphasizes the importance of using the reset command to remove the theme.
50 */
51async function showStreamingThemeSuccessPopup(
52 themeDescription: string,
53 settingsApplied: number,
54 context: vscode.ExtensionContext
55): Promise<void> {
56 const completenessMessage = settingsApplied < 50
57 ? "⚠️ Partial theme generated - you may want to regenerate for more comprehensive styling"
58 : settingsApplied < 80
59 ? "✅ Good theme coverage - most UI elements should be styled"
60 : "🎨 Comprehensive theme generated - all major UI areas styled!";
62 const message = `🎨 Theme "${themeDescription}" applied successfully!\n\nApplied ${settingsApplied} color settings in real-time\n\n${completenessMessage}\n\n⚠️ IMPORTANT: This theme overrides your VS Code settings. Use "Reset Theme Customizations" command to remove it and return to your original theme.`;
64 const action = await vscode.window.showInformationMessage(
65 message,
66 {
67 modal: true,
68 detail: 'Your theme was applied live as AI generated each setting. To return to your original theme, you must use the Reset command - simply changing themes in VS Code settings will not remove these customizations.'
69 },
70 'Keep Theme',
71 'Reset Theme (Remove All Customizations)'
72 );
73 
74 if (action === 'Reset Theme (Remove All Customizations)') {
75 // Execute the reset theme command
76 await vscode.commands.executeCommand('vibeThemer.resetTheme');
77 }
79 
80/**
81 * Formats current theme state into AI-readable context for better iteration support.
82 * Returns formatted string describing existing theme customizations.
83 */
84function formatCurrentThemeContext(): string {
85 const themeStateResult = getCurrentThemeState();
87 if (!themeStateResult.success) {
88 return ''; // No context on error - fallback to standard generation
89 }
91 const { state } = themeStateResult;
93 if (!state.hasCustomizations) {
94 return ''; // No existing customizations
95 }
97 const lines: string[] = [];
99 // Add color customizations context
100 const colorCount = Object.keys(state.colorCustomizations).length;
101 if (colorCount > 0) {
102 lines.push(`CURRENT THEME CONTEXT:`);
103 lines.push(`Active workbench color overrides (${colorCount} settings):`);
105 Object.entries(state.colorCustomizations).forEach(([key, value]) => {
106 lines.push(`- ${key}: ${value}`);
107 });
108 }
110 // Add token color customizations context
111 const tokenCount = Object.keys(state.tokenColorCustomizations).length;
112 if (tokenCount > 0) {
113 if (lines.length > 0) {
114 lines.push(''); // Add spacing
115 }
116 lines.push(`Active syntax highlighting overrides (${tokenCount} settings):`);
118 Object.entries(state.tokenColorCustomizations).forEach(([key, value]) => {
119 lines.push(`- ${key}: ${JSON.stringify(value)}`);
120 });
121 }
123 if (lines.length > 0) {
124 lines.push(''); // Add spacing before user prompt
125 lines.push('User request:');
126 return lines.join('\n');
127 }
129 return '';
131 
132/**
133 * Orchestrates the streaming theme generation workflow: prompts user, calls OpenAI with streaming, applies theme settings in real-time.
134 *
135 * This function demonstrates real-time theme application as the AI generates settings.
136 * Each setting is applied immediately as it's received, providing dynamic visual feedback.
137 *
138 * Updates the lastGeneratedThemeRef with the accumulated theme data.
139 */
140export async function runThemeGenerationWorkflow(
141 context: vscode.ExtensionContext,
142 lastGeneratedThemeRef: { current?: any }
143) {
144 // Use the enhanced OpenAI client with better error handling and state management
145 const openaiResult = await ensureOpenAIClientCore(context);
146 if (!openaiResult.success) {
147 // The ensureOpenAIClient already handled user interaction and error display
148 const clientState = getCurrentClientState();
149 if (clientState.status === 'error') {
150 console.error('Theme generation aborted due to OpenAI client error:', clientState.error);
151 }
152 return;
153 }
154 
155 const openai = openaiResult.data;
156 
157 const themeDescription = await showThemePromptPicker();
158 if (!themeDescription) {
159 vscode.window.showInformationMessage('No theme description provided. Try again when you\'re ready to create or modify your perfect coding atmosphere! 🎨', { modal: true });
160 return;
161 }
162 
163 const selectedModel = getSelectedOpenAIModel(context) || "gpt-4.1";
164 
165 // Streaming theme data accumulation
166 const accumulatedSelectors: Record<string, string> = {};
167 const accumulatedTokenColors: any[] = [];
168 let settingsApplied = 0;
169 let expectedSettingsCount = 120; // Default fallback value
170 let wasCancelled = false;
171 const hasWorkspaceFolders = (vscode.workspace.workspaceFolders?.length ?? 0) > 0;
172 let currentMessage = "🤖 AI analyzing your vibe...";
173 let lastMessageTime = Date.now(); // Initialize with current time
174 const MIN_MESSAGE_DURATION = 800; // Show messages for at least 0.8 seconds - quick but readable
175 
176 try {
177 await vscode.window.withProgress({
178 location: vscode.ProgressLocation.Notification,
179 title: "",
180 cancellable: true
181 }, async (progress, cancellationToken) => {
182 // Initialize progress tracking
183 (progress as any)._lastPercent = 0;
184 progress.report({ message: currentMessage, increment: 0 });
186 // Read streaming prompt using proper extension resource path
187 const promptPath = context.asAbsolutePath(path.join('prompts', 'streamingThemePrompt.txt'));
188 let streamingPrompt: string;
190 try {
191 streamingPrompt = fs.readFileSync(promptPath, 'utf8');
192 } catch (error) {
193 throw new Error(`Failed to load theme generation prompt from ${promptPath}. Please reinstall the extension. Error: ${error}`);
194 }
195 
196 // Inject current theme context for iteration support
197 const currentThemeContext = formatCurrentThemeContext();
198 const userPrompt = currentThemeContext
199 ? `${currentThemeContext}\n${themeDescription}`
200 : `Theme description: ${themeDescription}`;
201 
202 // Create streaming completion for comprehensive themes
203 const stream = await openai.chat.completions.create({
204 model: selectedModel,
205 messages: [
206 { role: "system", content: streamingPrompt },
207 { role: "user", content: userPrompt }
208 ],
209 stream: true
210 });
211 
212 let buffer = '';
213 let errorCount = 0;
214 const maxErrors = 5; // Allow some failed settings before giving up
215 
216 for await (const chunk of stream) {
217 // Check for cancellation at the start of each chunk
218 if (cancellationToken.isCancellationRequested) {
219 break;
220 }
221 
222 const content = chunk.choices[0]?.delta?.content || '';
223 buffer += content;
224 
225 // Process complete lines
226 const lines = buffer.split('\n');
227 buffer = lines.pop() || ''; // Keep incomplete line in buffer
228 
229 for (const line of lines) {
230 if (!line.trim()) {continue;}
232 // Check for cancellation before processing each line
233 if (cancellationToken.isCancellationRequested) {
234 break;
235 }
236 
237 const parseResult = parseStreamingThemeLine(line);
239 if (parseResult.success) {
240 const setting = parseResult.setting;
242 // Apply the setting immediately
243 const applyResult = await applyStreamingThemeSetting(setting, hasWorkspaceFolders);
245 if (applyResult.success) {
246 // Handle different setting types
247 if (setting.type === 'count') {
248 expectedSettingsCount = setting.total;
249 currentMessage = `🎯 Planning ${setting.total} theme settings...`;
250 progress.report({ message: currentMessage });
252 } else if (setting.type === 'selector') {
253 settingsApplied++;
254 accumulatedSelectors[setting.name] = setting.color;
256 // Update progress percentage using actual expected count
257 const progressPercent = Math.min(Math.floor((settingsApplied / expectedSettingsCount) * 100), 99);
258 progress.report({
259 message: currentMessage,
260 increment: progressPercent - (progress as any)._lastPercent || 0
261 });
262 (progress as any)._lastPercent = progressPercent;
264 } else if (setting.type === 'token') {
265 settingsApplied++;
266 accumulatedTokenColors.push({
267 scope: setting.scope,
268 settings: {
269 foreground: setting.color,
270 ...(setting.fontStyle && { fontStyle: setting.fontStyle })
271 }
272 });
274 // Update progress percentage using actual expected count
275 const progressPercent = Math.min(Math.floor((settingsApplied / expectedSettingsCount) * 100), 99);
276 progress.report({
277 message: currentMessage,
278 increment: progressPercent - (progress as any)._lastPercent || 0
279 });
280 (progress as any)._lastPercent = progressPercent;
282 } else if (setting.type === 'message') {
283 // Only update message if enough time has passed or it's the first message
284 const now = Date.now();
285 if (now - lastMessageTime >= MIN_MESSAGE_DURATION || lastMessageTime === 0) {
286 currentMessage = `${setting.content}`;
287 lastMessageTime = now;
288 progress.report({ message: currentMessage });
289 }
290 }
291 } else {
292 errorCount++;
293 console.warn(`Failed to apply setting: ${line}`, applyResult.error);
295 if (errorCount >= maxErrors) {
296 throw new Error(`Too many failed settings (${errorCount}). Last error: ${applyResult.error.message}`);
297 }
298 }
299 } else {
300 errorCount++;
301 console.warn(`Failed to parse line: ${line}`, parseResult.error);
303 if (errorCount >= maxErrors) {
304 throw new Error(`Too many parsing errors (${errorCount}). Last error: ${parseResult.error}`);
305 }
306 }
307 }
309 // Break out of chunk loop if cancelled
310 if (cancellationToken.isCancellationRequested) {
311 break;
312 }
313 }
314 
315 // Handle cancellation if requested
316 if (cancellationToken.isCancellationRequested) {
317 wasCancelled = true;
318 const choice = await handleCancellationChoice(settingsApplied, themeDescription);
320 if (choice === 'reset') {
321 // Execute the reset theme command to remove all applied settings
322 await vscode.commands.executeCommand('vibeThemer.resetTheme');
323 return; // Exit early, no success popup needed
324 }
325 // If 'keep', continue with normal completion flow
326 }
327 
328 // Process any remaining content in buffer (only if not cancelled)
329 if (buffer.trim() && !cancellationToken.isCancellationRequested) {
330 const parseResult = parseStreamingThemeLine(buffer.trim());
331 if (parseResult.success) {
332 const applyResult = await applyStreamingThemeSetting(parseResult.setting, hasWorkspaceFolders);
333 if (applyResult.success) {
334 const setting = parseResult.setting;
335 if (setting.type === 'count') {
336 expectedSettingsCount = setting.total;
337 } else if (setting.type === 'selector') {
338 settingsApplied++;
339 accumulatedSelectors[setting.name] = setting.color;
340 } else if (setting.type === 'token') {
341 settingsApplied++;
342 accumulatedTokenColors.push({
343 scope: setting.scope,
344 settings: {
345 foreground: setting.color,
346 ...(setting.fontStyle && { fontStyle: setting.fontStyle })
347 }
348 });
349 }
350 // MESSAGE types don't need accumulation, they're just progress updates
351 }
352 }
353 }
354 
355 // Update final progress message based on whether it was cancelled
356 const finalMessage = wasCancelled
357 ? `🛑 Theme cancelled. Kept ${settingsApplied} settings.`
358 : `🎉 Theme complete! ${settingsApplied} colors applied`;
360 progress.report({ message: finalMessage, increment: 100 });
361 });
362 
363 // Build theme data structure for reference (always build it, even if cancelled)
364 const coreColors = {
365 primary: accumulatedSelectors["activityBar.background"] || "#007acc",
366 secondary: accumulatedSelectors["statusBar.background"] || "#444444",
367 accent: accumulatedSelectors["activityBarBadge.background"] || "#ff8c00",
368 background: accumulatedSelectors["editor.background"] || "#1e1e1e",
369 foreground: accumulatedSelectors["editor.foreground"] || "#d4d4d4"
370 };
371 
372 lastGeneratedThemeRef.current = {
373 name: `Custom Theme - ${themeDescription}`,
374 description: `Theme generated from: "${themeDescription}"`,
375 colors: coreColors,
376 tokenColors: accumulatedTokenColors
377 };
378 
379 // Show success popup only if generation completed normally (not cancelled)
380 if (!wasCancelled) {
381 await showStreamingThemeSuccessPopup(themeDescription, settingsApplied, context);
382 }
384 } catch (error: any) {
385 // Enhanced error handling for streaming
386 const isNetworkError = error.message.includes('fetch') ||
387 error.message.includes('network') ||
388 error.message.includes('connection');
390 const isAPIError = error.message.includes('API') ||
391 error.message.includes('429') ||
392 error.message.includes('quota');
394 const isStreamingError = error.message.includes('stream') ||
395 error.message.includes('parsing') ||
396 error.message.includes('settings');
397 
398 // Provide contextual error messages based on error type
399 let errorPrefix: string;
400 let suggestedAction: string | undefined;
401 
402 if (isNetworkError) {
403 errorPrefix = '🌐 Connection issue during theme generation';
404 suggestedAction = 'Please check your internet connection and try again';
405 } else if (isAPIError) {
406 errorPrefix = '🔑 OpenAI API issue during theme creation';
407 suggestedAction = 'Please check your API key validity and quota limits in your OpenAI dashboard';
408 } else if (isStreamingError) {
409 errorPrefix = '⚠️ Theme generation was interrupted';
410 suggestedAction = `${settingsApplied} settings were successfully applied before the interruption. You can try generating again to continue, or use "Reset Theme Customizations" to start fresh.`;
411 } else {
412 errorPrefix = '❌ Theme generation encountered an error';
413 suggestedAction = 'Please try again with a different description, or contact support if the issue persists';
414 }
⋯ 20 lines hidden (lines 415–434)
415 
416 const fullMessage = suggestedAction
417 ? `${errorPrefix}: ${error.message}. ${suggestedAction}`
418 : `${errorPrefix}: ${error.message}`;
419 
420 vscode.window.showErrorMessage(fullMessage, { modal: true });
422 // Log detailed error information for debugging
423 console.error('Streaming theme generation error:', {
424 originalError: error,
425 errorType: isNetworkError ? 'network' : isAPIError ? 'api' : isStreamingError ? 'streaming' : 'unknown',
426 clientState: getCurrentClientState(),
427 themeDescription,
428 selectedModel,
429 settingsApplied,
430 accumulatedSelectors: Object.keys(accumulatedSelectors).length,
431 accumulatedTokenColors: accumulatedTokenColors.length
432 });
433 }
Act I · A vibe becomes a theme1.6🎬 Parser + writer

Parsing a line, writing a setting

src/services/themeCore.ts

themeCore.ts is the biggest file and wears three hats. It parses a streaming line into a typed StreamingThemeSetting. It applies that setting to VS Code config. It reads the current theme back out for iteration. The parser itself is a clean startsWith cascade returning a discriminated union. Easy to read, easy to extend.

parseStreamingThemeLine — COUNT / SELECTOR / TOKEN / MESSAGE.

src/services/themeCore.ts · 688 lines
src/services/themeCore.ts688 lines · TypeScript
⋯ 314 lines hidden (lines 1–314)
1/**
2 * Core theme application logic with integrated orchestration.
3 * Combines pure domain logic with theme application lifecycle management.
4 */
5 
6import * as vscode from 'vscode';
7import {
8 ConfigurationScope,
9 ThemeCustomizations,
10 ThemeApplicationResult,
11 ThemeApplicationError,
12 TokenColorRule,
13 CurrentThemeState,
14 CurrentThemeResult
15} from '../types/theme';
16 
17/**
18 * Determines the appropriate configuration scope based on workspace context.
19 * Encodes the business rule: prefer workspace if available, fall back to global.
20 */
21export const determineConfigurationScope = (hasWorkspaceFolders: boolean): ConfigurationScope => {
22 if (hasWorkspaceFolders) {
23 return {
24 type: 'both',
25 primary: vscode.ConfigurationTarget.Global,
26 fallback: vscode.ConfigurationTarget.Workspace
27 };
28 }
30 return {
31 type: 'global',
32 target: vscode.ConfigurationTarget.Global
33 };
34};
35 
36/**
37 * Transforms token colors into the format expected by VS Code.
38 * Handles the impedance mismatch between our domain model and VS Code's API.
39 */
40export const prepareTokenColorCustomizations = (
41 tokenColors: readonly TokenColorRule[],
42 existingCustomizations?: Record<string, unknown> | {}
43): Record<string, unknown> => {
44 const base = existingCustomizations || {};
46 if (tokenColors.length === 0) {
47 return base as Record<string, unknown>;
48 }
49 
50 return {
51 ...base,
52 textMateRules: tokenColors
53 };
54};
55 
56/**
57 * Creates a structured error from an unknown cause.
58 * Provides consistent error handling across the application.
59 */
60export const createThemeApplicationError = (
61 message: string,
62 cause: unknown,
63 recoverable: boolean = true,
64 suggestedAction?: string
65): ThemeApplicationError => ({
66 message,
67 cause,
68 recoverable,
69 suggestedAction
70});
71 
72/**
73 * Validates theme customizations before application.
74 * Ensures we don't attempt to apply invalid data.
75 */
76export const validateThemeCustomizations = (
77 customizations: ThemeCustomizations
78): ThemeApplicationResult => {
79 // Basic validation - could be expanded with more sophisticated rules
80 if (!customizations.colorCustomizations || typeof customizations.colorCustomizations !== 'object') {
81 return {
82 success: false,
83 error: createThemeApplicationError(
84 'Invalid color customizations provided',
85 new Error('Color customizations must be a valid object'),
86 false,
87 'Check the theme generation logic'
88 )
89 };
90 }
91 
92 // Validate color format (supports 3, 6, and 8-digit hex codes, plus alpha transparency)
93 const invalidColors = Object.entries(customizations.colorCustomizations)
94 .filter(([_, color]) => {
95 if (typeof color !== 'string') {return true;}
97 // Allow 3-digit (#rgb), 6-digit (#rrggbb), and 8-digit (#rrggbbaa) hex codes
98 // Also allow CSS color keywords like 'transparent'
99 const hexPattern = /^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$|^#[0-9a-fA-F]{8}$/;
100 const isValidHex = hexPattern.test(color);
101 const isValidKeyword = ['transparent', 'inherit', 'initial', 'unset'].includes(color.toLowerCase());
103 return !isValidHex && !isValidKeyword;
104 })
105 .map(([key, _]) => key);
106 
107 if (invalidColors.length > 0) {
108 return {
109 success: false,
110 error: createThemeApplicationError(
111 `Invalid color format for: ${invalidColors.join(', ')}`,
112 new Error('Colors must be valid hex codes (#rgb, #rrggbb, #rrggbbaa) or CSS color keywords'),
113 false,
114 'Use the color normalization utilities'
115 )
116 };
117 }
118 
119 return { success: true, appliedScope: { type: 'global', target: vscode.ConfigurationTarget.Global } };
120};
121 
122/**
123 * Determines which configuration targets to attempt based on scope.
124 * Encodes the retry logic as a pure transformation.
125 */
126export const getConfigurationTargets = (scope: ConfigurationScope): vscode.ConfigurationTarget[] => {
127 switch (scope.type) {
128 case 'workspace':
129 return [scope.target];
130 case 'global':
131 return [scope.target];
132 case 'both':
133 return [scope.primary, scope.fallback];
134 }
135};
136 
137/**
138 * Creates success result with applied scope information.
139 */
140export const createSuccessResult = (appliedScope: ConfigurationScope): ThemeApplicationResult => ({
141 success: true,
142 appliedScope
143});
144 
145/**
146 * Creates failure result with error information.
147 */
148export const createFailureResult = (error: ThemeApplicationError): ThemeApplicationResult => ({
149 success: false,
150 error
151});
152 
153/**
154 * Applies theme customizations with comprehensive error handling and user feedback.
155 * This is the main entry point that orchestrates the entire theme application process.
156 *
157 * Design principles:
158 * - Functional composition of smaller, focused operations
159 * - Explicit error handling with rich context
160 * - Direct VS Code API usage for simplicity
161 * - Type safety that prevents invalid operations
162 */
163export const applyThemeCustomizations = async (
164 colorCustomizations: Record<string, string>,
165 tokenColors: TokenColorRule[] | undefined,
166 themeDescription: string,
167 suppressNotifications: boolean = false
168): Promise<ThemeApplicationResult> => {
169 // Create our domain object with validated input
170 const customizations: ThemeCustomizations = {
171 colorCustomizations,
172 tokenColors: tokenColors || [],
173 description: themeDescription
174 };
175 
176 // Validate before attempting application
177 const validationResult = validateThemeCustomizations(customizations);
178 if (!validationResult.success) {
179 if (!suppressNotifications) {
180 await vscode.window.showErrorMessage(validationResult.error.message);
181 }
182 return validationResult;
183 }
184 
185 // Determine where to apply the theme based on workspace context
186 const hasWorkspaceFolders = (vscode.workspace.workspaceFolders?.length ?? 0) > 0;
187 const scope = determineConfigurationScope(hasWorkspaceFolders);
188 const targets = getConfigurationTargets(scope);
189 
190 // Apply theme with fallback strategy
191 const result = await applyWithFallback(customizations, targets);
193 // Provide user feedback
194 if (result.success && !suppressNotifications) {
195 const scopeDescription = describeScopeApplication(result.appliedScope);
196 const message = `Theme "${themeDescription}" applied ${scopeDescription}`;
197 await vscode.window.showInformationMessage(message);
198 } else if (!result.success && !suppressNotifications) {
199 await vscode.window.showErrorMessage(result.error.message);
200 }
201 
202 return result;
203};
204 
205/**
206 * Applies theme customizations with fallback to alternative configuration targets.
207 * Implements the retry logic as a pure functional composition.
208 */
209const applyWithFallback = async (
210 customizations: ThemeCustomizations,
211 targets: vscode.ConfigurationTarget[]
212): Promise<ThemeApplicationResult> => {
213 let lastError: unknown;
214 
215 for (const target of targets) {
216 try {
217 await applySingleTarget(customizations, target);
219 // Success - determine which scope was actually used
220 const appliedScope: ConfigurationScope = targets.length === 1
221 ? (target === vscode.ConfigurationTarget.Workspace
222 ? { type: 'workspace', target: vscode.ConfigurationTarget.Workspace }
223 : { type: 'global', target: vscode.ConfigurationTarget.Global })
224 : { type: 'both', primary: targets[0], fallback: targets[1] };
226 return createSuccessResult(appliedScope);
227 } catch (error) {
228 lastError = error;
229 // Continue to next target if available
230 }
231 }
232 
233 // All targets failed
234 return createFailureResult(
235 createThemeApplicationError(
236 'Failed to apply theme to any configuration target',
237 lastError,
238 true,
239 'Check VS Code permissions and try restarting the editor'
240 )
241 );
242};
243 
244/**
245 * Applies theme customizations to a single configuration target.
246 * Separated for clarity and testability.
247 */
248const applySingleTarget = async (
249 customizations: ThemeCustomizations,
250 target: vscode.ConfigurationTarget
251): Promise<void> => {
252 const config = vscode.workspace.getConfiguration();
254 // Apply color customizations
255 await config.update(
256 'workbench.colorCustomizations',
257 customizations.colorCustomizations,
258 target
259 );
260 
261 // Apply token colors if present
262 if (customizations.tokenColors.length > 0) {
263 const existingTokenCustomizations = config.get('editor.tokenColorCustomizations') as Record<string, unknown> | undefined;
264 const newTokenCustomizations = prepareTokenColorCustomizations(
265 customizations.tokenColors,
266 existingTokenCustomizations
267 );
269 await config.update(
270 'editor.tokenColorCustomizations',
271 newTokenCustomizations,
272 target
273 );
274 }
275};
276 
277/**
278 * Describes how the scope was applied for user feedback.
279 */
280const describeScopeApplication = (scope: ConfigurationScope): string => {
281 switch (scope.type) {
282 case 'workspace':
283 return 'to workspace settings';
284 case 'global':
285 return 'to global settings';
286 case 'both':
287 return 'to global settings (with workspace fallback)';
288 }
289};
290 
291/**
292 * Represents a single streaming theme setting.
293 * Encodes the business rule that each setting must have a type and target.
294 */
295export type StreamingThemeSetting =
296 | { readonly type: 'count'; readonly total: number }
297 | { readonly type: 'selector'; readonly name: string; readonly color: string }
298 | { readonly type: 'token'; readonly scope: string; readonly color: string; readonly fontStyle?: string }
299 | { readonly type: 'message'; readonly content: string };
300 
301/**
302 * Result of parsing a streaming theme line.
303 * Success contains the parsed setting, failure contains error information.
304 */
305export type StreamingParseResult =
306 | { readonly success: true; readonly setting: StreamingThemeSetting }
307 | { readonly success: false; readonly error: string; readonly line: string };
308 
309/**
310 * Parses a single line from streaming theme generation.
311 * Handles COUNT:, SELECTOR:, TOKEN:, and MESSAGE: format lines.
312 *
313 * Business rule: Each line must follow the exact streaming format specification.
314 */
315export const parseStreamingThemeLine = (line: string): StreamingParseResult => {
316 const trimmedLine = line.trim();
318 if (!trimmedLine) {
319 return {
320 success: false,
321 error: 'Empty line',
322 line
323 };
324 }
325 
326 if (trimmedLine.startsWith('COUNT:')) {
327 const content = trimmedLine.substring(6); // Remove 'COUNT:'
328 const total = parseInt(content.trim(), 10);
330 if (isNaN(total) || total <= 0) {
331 return {
332 success: false,
333 error: 'Invalid count format - expected positive integer',
334 line
335 };
336 }
337 
338 return {
339 success: true,
340 setting: {
341 type: 'count',
342 total
343 }
344 };
345 }
346 
347 if (trimmedLine.startsWith('SELECTOR:')) {
348 const content = trimmedLine.substring(9); // Remove 'SELECTOR:'
349 const [name, color] = content.split('=');
351 if (!name || !color) {
352 return {
353 success: false,
354 error: 'Invalid selector format - expected name=color',
355 line
356 };
357 }
358 
359 if (!validateStreamingColor(color)) {
360 return {
361 success: false,
362 error: 'Invalid color format',
363 line
364 };
365 }
366 
367 return {
368 success: true,
369 setting: {
370 type: 'selector',
371 name: name.trim(),
372 color: color.trim()
373 }
374 };
375 }
⋯ 313 lines hidden (lines 376–688)
376 
377 if (trimmedLine.startsWith('TOKEN:')) {
378 const content = trimmedLine.substring(6); // Remove 'TOKEN:'
379 const [scope, colorAndStyle] = content.split('=');
381 if (!scope || !colorAndStyle) {
382 return {
383 success: false,
384 error: 'Invalid token format - expected scope=color[,fontStyle]',
385 line
386 };
387 }
388 
389 const [color, fontStyle] = colorAndStyle.split(',');
391 if (!validateStreamingColor(color)) {
392 return {
393 success: false,
394 error: 'Invalid color format',
395 line
396 };
397 }
398 
399 return {
400 success: true,
401 setting: {
402 type: 'token',
403 scope: scope.trim(),
404 color: color.trim(),
405 fontStyle: fontStyle?.trim()
406 }
407 };
408 }
409 
410 if (trimmedLine.startsWith('MESSAGE:')) {
411 const content = trimmedLine.substring(8); // Remove 'MESSAGE:'
413 if (!content.trim()) {
414 return {
415 success: false,
416 error: 'Empty message content',
417 line
418 };
419 }
420 
421 return {
422 success: true,
423 setting: {
424 type: 'message',
425 content: content.trim()
426 }
427 };
428 }
429 
430 return {
431 success: false,
432 error: 'Line must start with COUNT:, SELECTOR:, TOKEN:, or MESSAGE:',
433 line
434 };
435};
436 
437/**
438 * Validates that a color string meets basic format requirements.
439 * Reuses hex validation logic for streaming theme colors.
440 * Also accepts REMOVE for theme iteration support.
441 */
442const validateStreamingColor = (color: string): boolean => {
443 // Accept REMOVE for theme iteration
444 if (color.trim().toUpperCase() === 'REMOVE') {
445 return true;
446 }
448 // Hex color validation (3, 6, or 8 digit hex codes)
449 const hexPattern = /^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$|^#[0-9a-fA-F]{8}$/;
450 const isValidHex = hexPattern.test(color);
451 const isValidKeyword = ['transparent', 'inherit', 'initial', 'unset'].includes(color.toLowerCase());
453 return isValidHex || isValidKeyword;
454};
455 
456/**
457 * Applies a single streaming theme setting immediately.
458 * Enables real-time theme updates as settings are received.
459 */
460export const applyStreamingThemeSetting = async (
461 setting: StreamingThemeSetting,
462 hasWorkspaceFolders: boolean
463): Promise<ThemeApplicationResult> => {
464 try {
465 // Handle count and message types - they don't need VS Code config updates
466 if (setting.type === 'count' || setting.type === 'message') {
467 const scope = determineConfigurationScope(hasWorkspaceFolders);
468 return createSuccessResult(scope);
469 }
470 
471 const config = vscode.workspace.getConfiguration();
472 const scope = determineConfigurationScope(hasWorkspaceFolders);
473 const targets = getConfigurationTargets(scope);
474 
475 if (setting.type === 'selector') {
476 // Apply color customization incrementally
477 for (const target of targets) {
478 try {
479 const existingColors = config.get('workbench.colorCustomizations') as Record<string, string> || {};
481 let updatedColors: Record<string, string>;
483 if (setting.color.trim().toUpperCase() === 'REMOVE') {
484 // Remove the setting by creating a new object without it
485 updatedColors = { ...existingColors };
486 delete updatedColors[setting.name];
487 } else {
488 // Add or update the setting
489 updatedColors = {
490 ...existingColors,
491 [setting.name]: setting.color
492 };
493 }
495 await config.update('workbench.colorCustomizations', updatedColors, target);
497 // Success on first target - determine which scope was used
498 const appliedScope: ConfigurationScope = targets.length === 1
499 ? (target === vscode.ConfigurationTarget.Workspace
500 ? { type: 'workspace', target: vscode.ConfigurationTarget.Workspace }
501 : { type: 'global', target: vscode.ConfigurationTarget.Global })
502 : { type: 'both', primary: targets[0], fallback: targets[1] };
504 return createSuccessResult(appliedScope);
505 } catch (error) {
506 // Continue to next target
507 continue;
508 }
509 }
510 } else if (setting.type === 'token') {
511 // Apply token color customization incrementally
512 for (const target of targets) {
513 try {
514 const existingTokens = config.get('editor.tokenColorCustomizations') as Record<string, unknown> || {};
515 const existingRules = (existingTokens.textMateRules as any[]) || [];
517 let updatedRules: any[];
519 if (setting.color.trim().toUpperCase() === 'REMOVE') {
520 // Remove the token rule by filtering it out
521 updatedRules = existingRules.filter(rule => rule.scope !== setting.scope);
522 } else {
523 // Create new token rule
524 const newRule = {
525 scope: setting.scope,
526 settings: {
527 foreground: setting.color,
528 ...(setting.fontStyle && { fontStyle: setting.fontStyle })
529 }
530 };
532 // Update existing rules or add new one
533 updatedRules = [...existingRules.filter(rule => rule.scope !== setting.scope), newRule];
534 }
536 const updatedTokens = {
537 ...existingTokens,
538 textMateRules: updatedRules
539 };
541 await config.update('editor.tokenColorCustomizations', updatedTokens, target);
543 // Success on first target
544 const appliedScope: ConfigurationScope = targets.length === 1
545 ? (target === vscode.ConfigurationTarget.Workspace
546 ? { type: 'workspace', target: vscode.ConfigurationTarget.Workspace }
547 : { type: 'global', target: vscode.ConfigurationTarget.Global })
548 : { type: 'both', primary: targets[0], fallback: targets[1] };
550 return createSuccessResult(appliedScope);
551 } catch (error) {
552 // Continue to next target
553 continue;
554 }
555 }
556 }
557 
558 // All targets failed (only selectors and tokens reach here)
559 const settingIdentifier = setting.type === 'selector' ? setting.name :
560 setting.type === 'token' ? setting.scope : 'unknown';
562 return createFailureResult(
563 createThemeApplicationError(
564 `Failed to apply ${setting.type} setting: ${settingIdentifier}`,
565 new Error('All configuration targets failed'),
566 true,
567 'Check VS Code permissions and try restarting the editor'
568 )
569 );
570 
571 } catch (error) {
572 return createFailureResult(
573 createThemeApplicationError(
574 `Error applying streaming theme setting`,
575 error,
576 true,
577 'Check the setting format and try again'
578 )
579 );
580 }
581};
582 
583// =============================================================================
584// Current Theme State Reading
585// =============================================================================
586 
587/**
588 * Reads current workbench color customizations from VS Code configuration.
589 * Checks both workspace and global scopes to capture the complete state.
590 */
591export const getCurrentColorCustomizations = (): Record<string, string> => {
592 const config = vscode.workspace.getConfiguration();
594 // Get both workspace and global settings
595 const workspaceColors = config.get<Record<string, string>>('workbench.colorCustomizations', {});
596 const globalColors = config.get<Record<string, string>>('workbench.colorCustomizations', {});
598 // Workspace settings take precedence over global
599 return {
600 ...globalColors,
601 ...workspaceColors
602 };
603};
604 
605/**
606 * Reads current token color customizations from VS Code configuration.
607 * Combines workspace and global scopes with workspace taking precedence.
608 */
609export const getCurrentTokenColorCustomizations = (): Record<string, unknown> => {
610 const config = vscode.workspace.getConfiguration();
612 // Get both workspace and global settings
613 const workspaceTokens = config.get<Record<string, unknown>>('editor.tokenColorCustomizations', {});
614 const globalTokens = config.get<Record<string, unknown>>('editor.tokenColorCustomizations', {});
616 // Workspace settings take precedence over global
617 return {
618 ...globalTokens,
619 ...workspaceTokens
620 };
621};
622 
623/**
624 * Determines the effective scope of current customizations.
625 * Returns where the customizations are actually stored.
626 */
627export const getCurrentCustomizationScope = (): 'workspace' | 'global' | 'both' => {
628 const config = vscode.workspace.getConfiguration();
630 const workspaceColors = config.get<Record<string, string>>('workbench.colorCustomizations', {});
631 const globalColors = config.get<Record<string, string>>('workbench.colorCustomizations', {});
632 const workspaceTokens = config.get<Record<string, unknown>>('editor.tokenColorCustomizations', {});
633 const globalTokens = config.get<Record<string, unknown>>('editor.tokenColorCustomizations', {});
635 const hasWorkspaceCustomizations = Object.keys(workspaceColors).length > 0 || Object.keys(workspaceTokens).length > 0;
636 const hasGlobalCustomizations = Object.keys(globalColors).length > 0 || Object.keys(globalTokens).length > 0;
638 if (hasWorkspaceCustomizations && hasGlobalCustomizations) {
639 return 'both';
640 }
642 if (hasWorkspaceCustomizations) {
643 return 'workspace';
644 }
646 if (hasGlobalCustomizations) {
647 return 'global';
648 }
650 return 'global'; // Default scope when no customizations exist
651};
652 
653/**
654 * Reads the complete current theme state from VS Code configuration.
655 * This captures both color and token customizations with scope information.
656 */
657export const getCurrentThemeState = (): CurrentThemeResult => {
658 try {
659 const colorCustomizations = getCurrentColorCustomizations();
660 const tokenColorCustomizations = getCurrentTokenColorCustomizations();
661 const scope = getCurrentCustomizationScope();
663 const hasCustomizations =
664 Object.keys(colorCustomizations).length > 0 ||
665 Object.keys(tokenColorCustomizations).length > 0;
667 return {
668 success: true,
669 state: {
670 colorCustomizations,
671 tokenColorCustomizations,
672 hasCustomizations,
673 scope
674 }
675 };
677 } catch (error) {
678 return {
679 success: false,
680 error: createThemeApplicationError(
681 'Failed to read current theme state',
682 error,
683 true,
684 'Check VS Code configuration and try again'
685 )
686 };
687 }
688};

applyStreamingThemeSetting is where settings hit the editor. A REMOVE value deletes the override and lets the base theme show through. Anything else merges into the existing customizations object before the write. The scope logic lives here too, and so does one of the more interesting bugs. Act IV takes it apart.

applyStreamingThemeSetting — note the loop over targets that returns on the first success.

src/services/themeCore.ts · 688 lines
src/services/themeCore.ts688 lines · TypeScript
⋯ 459 lines hidden (lines 1–459)
1/**
2 * Core theme application logic with integrated orchestration.
3 * Combines pure domain logic with theme application lifecycle management.
4 */
5 
6import * as vscode from 'vscode';
7import {
8 ConfigurationScope,
9 ThemeCustomizations,
10 ThemeApplicationResult,
11 ThemeApplicationError,
12 TokenColorRule,
13 CurrentThemeState,
14 CurrentThemeResult
15} from '../types/theme';
16 
17/**
18 * Determines the appropriate configuration scope based on workspace context.
19 * Encodes the business rule: prefer workspace if available, fall back to global.
20 */
21export const determineConfigurationScope = (hasWorkspaceFolders: boolean): ConfigurationScope => {
22 if (hasWorkspaceFolders) {
23 return {
24 type: 'both',
25 primary: vscode.ConfigurationTarget.Global,
26 fallback: vscode.ConfigurationTarget.Workspace
27 };
28 }
30 return {
31 type: 'global',
32 target: vscode.ConfigurationTarget.Global
33 };
34};
35 
36/**
37 * Transforms token colors into the format expected by VS Code.
38 * Handles the impedance mismatch between our domain model and VS Code's API.
39 */
40export const prepareTokenColorCustomizations = (
41 tokenColors: readonly TokenColorRule[],
42 existingCustomizations?: Record<string, unknown> | {}
43): Record<string, unknown> => {
44 const base = existingCustomizations || {};
46 if (tokenColors.length === 0) {
47 return base as Record<string, unknown>;
48 }
49 
50 return {
51 ...base,
52 textMateRules: tokenColors
53 };
54};
55 
56/**
57 * Creates a structured error from an unknown cause.
58 * Provides consistent error handling across the application.
59 */
60export const createThemeApplicationError = (
61 message: string,
62 cause: unknown,
63 recoverable: boolean = true,
64 suggestedAction?: string
65): ThemeApplicationError => ({
66 message,
67 cause,
68 recoverable,
69 suggestedAction
70});
71 
72/**
73 * Validates theme customizations before application.
74 * Ensures we don't attempt to apply invalid data.
75 */
76export const validateThemeCustomizations = (
77 customizations: ThemeCustomizations
78): ThemeApplicationResult => {
79 // Basic validation - could be expanded with more sophisticated rules
80 if (!customizations.colorCustomizations || typeof customizations.colorCustomizations !== 'object') {
81 return {
82 success: false,
83 error: createThemeApplicationError(
84 'Invalid color customizations provided',
85 new Error('Color customizations must be a valid object'),
86 false,
87 'Check the theme generation logic'
88 )
89 };
90 }
91 
92 // Validate color format (supports 3, 6, and 8-digit hex codes, plus alpha transparency)
93 const invalidColors = Object.entries(customizations.colorCustomizations)
94 .filter(([_, color]) => {
95 if (typeof color !== 'string') {return true;}
97 // Allow 3-digit (#rgb), 6-digit (#rrggbb), and 8-digit (#rrggbbaa) hex codes
98 // Also allow CSS color keywords like 'transparent'
99 const hexPattern = /^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$|^#[0-9a-fA-F]{8}$/;
100 const isValidHex = hexPattern.test(color);
101 const isValidKeyword = ['transparent', 'inherit', 'initial', 'unset'].includes(color.toLowerCase());
103 return !isValidHex && !isValidKeyword;
104 })
105 .map(([key, _]) => key);
106 
107 if (invalidColors.length > 0) {
108 return {
109 success: false,
110 error: createThemeApplicationError(
111 `Invalid color format for: ${invalidColors.join(', ')}`,
112 new Error('Colors must be valid hex codes (#rgb, #rrggbb, #rrggbbaa) or CSS color keywords'),
113 false,
114 'Use the color normalization utilities'
115 )
116 };
117 }
118 
119 return { success: true, appliedScope: { type: 'global', target: vscode.ConfigurationTarget.Global } };
120};
121 
122/**
123 * Determines which configuration targets to attempt based on scope.
124 * Encodes the retry logic as a pure transformation.
125 */
126export const getConfigurationTargets = (scope: ConfigurationScope): vscode.ConfigurationTarget[] => {
127 switch (scope.type) {
128 case 'workspace':
129 return [scope.target];
130 case 'global':
131 return [scope.target];
132 case 'both':
133 return [scope.primary, scope.fallback];
134 }
135};
136 
137/**
138 * Creates success result with applied scope information.
139 */
140export const createSuccessResult = (appliedScope: ConfigurationScope): ThemeApplicationResult => ({
141 success: true,
142 appliedScope
143});
144 
145/**
146 * Creates failure result with error information.
147 */
148export const createFailureResult = (error: ThemeApplicationError): ThemeApplicationResult => ({
149 success: false,
150 error
151});
152 
153/**
154 * Applies theme customizations with comprehensive error handling and user feedback.
155 * This is the main entry point that orchestrates the entire theme application process.
156 *
157 * Design principles:
158 * - Functional composition of smaller, focused operations
159 * - Explicit error handling with rich context
160 * - Direct VS Code API usage for simplicity
161 * - Type safety that prevents invalid operations
162 */
163export const applyThemeCustomizations = async (
164 colorCustomizations: Record<string, string>,
165 tokenColors: TokenColorRule[] | undefined,
166 themeDescription: string,
167 suppressNotifications: boolean = false
168): Promise<ThemeApplicationResult> => {
169 // Create our domain object with validated input
170 const customizations: ThemeCustomizations = {
171 colorCustomizations,
172 tokenColors: tokenColors || [],
173 description: themeDescription
174 };
175 
176 // Validate before attempting application
177 const validationResult = validateThemeCustomizations(customizations);
178 if (!validationResult.success) {
179 if (!suppressNotifications) {
180 await vscode.window.showErrorMessage(validationResult.error.message);
181 }
182 return validationResult;
183 }
184 
185 // Determine where to apply the theme based on workspace context
186 const hasWorkspaceFolders = (vscode.workspace.workspaceFolders?.length ?? 0) > 0;
187 const scope = determineConfigurationScope(hasWorkspaceFolders);
188 const targets = getConfigurationTargets(scope);
189 
190 // Apply theme with fallback strategy
191 const result = await applyWithFallback(customizations, targets);
193 // Provide user feedback
194 if (result.success && !suppressNotifications) {
195 const scopeDescription = describeScopeApplication(result.appliedScope);
196 const message = `Theme "${themeDescription}" applied ${scopeDescription}`;
197 await vscode.window.showInformationMessage(message);
198 } else if (!result.success && !suppressNotifications) {
199 await vscode.window.showErrorMessage(result.error.message);
200 }
201 
202 return result;
203};
204 
205/**
206 * Applies theme customizations with fallback to alternative configuration targets.
207 * Implements the retry logic as a pure functional composition.
208 */
209const applyWithFallback = async (
210 customizations: ThemeCustomizations,
211 targets: vscode.ConfigurationTarget[]
212): Promise<ThemeApplicationResult> => {
213 let lastError: unknown;
214 
215 for (const target of targets) {
216 try {
217 await applySingleTarget(customizations, target);
219 // Success - determine which scope was actually used
220 const appliedScope: ConfigurationScope = targets.length === 1
221 ? (target === vscode.ConfigurationTarget.Workspace
222 ? { type: 'workspace', target: vscode.ConfigurationTarget.Workspace }
223 : { type: 'global', target: vscode.ConfigurationTarget.Global })
224 : { type: 'both', primary: targets[0], fallback: targets[1] };
226 return createSuccessResult(appliedScope);
227 } catch (error) {
228 lastError = error;
229 // Continue to next target if available
230 }
231 }
232 
233 // All targets failed
234 return createFailureResult(
235 createThemeApplicationError(
236 'Failed to apply theme to any configuration target',
237 lastError,
238 true,
239 'Check VS Code permissions and try restarting the editor'
240 )
241 );
242};
243 
244/**
245 * Applies theme customizations to a single configuration target.
246 * Separated for clarity and testability.
247 */
248const applySingleTarget = async (
249 customizations: ThemeCustomizations,
250 target: vscode.ConfigurationTarget
251): Promise<void> => {
252 const config = vscode.workspace.getConfiguration();
254 // Apply color customizations
255 await config.update(
256 'workbench.colorCustomizations',
257 customizations.colorCustomizations,
258 target
259 );
260 
261 // Apply token colors if present
262 if (customizations.tokenColors.length > 0) {
263 const existingTokenCustomizations = config.get('editor.tokenColorCustomizations') as Record<string, unknown> | undefined;
264 const newTokenCustomizations = prepareTokenColorCustomizations(
265 customizations.tokenColors,
266 existingTokenCustomizations
267 );
269 await config.update(
270 'editor.tokenColorCustomizations',
271 newTokenCustomizations,
272 target
273 );
274 }
275};
276 
277/**
278 * Describes how the scope was applied for user feedback.
279 */
280const describeScopeApplication = (scope: ConfigurationScope): string => {
281 switch (scope.type) {
282 case 'workspace':
283 return 'to workspace settings';
284 case 'global':
285 return 'to global settings';
286 case 'both':
287 return 'to global settings (with workspace fallback)';
288 }
289};
290 
291/**
292 * Represents a single streaming theme setting.
293 * Encodes the business rule that each setting must have a type and target.
294 */
295export type StreamingThemeSetting =
296 | { readonly type: 'count'; readonly total: number }
297 | { readonly type: 'selector'; readonly name: string; readonly color: string }
298 | { readonly type: 'token'; readonly scope: string; readonly color: string; readonly fontStyle?: string }
299 | { readonly type: 'message'; readonly content: string };
300 
301/**
302 * Result of parsing a streaming theme line.
303 * Success contains the parsed setting, failure contains error information.
304 */
305export type StreamingParseResult =
306 | { readonly success: true; readonly setting: StreamingThemeSetting }
307 | { readonly success: false; readonly error: string; readonly line: string };
308 
309/**
310 * Parses a single line from streaming theme generation.
311 * Handles COUNT:, SELECTOR:, TOKEN:, and MESSAGE: format lines.
312 *
313 * Business rule: Each line must follow the exact streaming format specification.
314 */
315export const parseStreamingThemeLine = (line: string): StreamingParseResult => {
316 const trimmedLine = line.trim();
318 if (!trimmedLine) {
319 return {
320 success: false,
321 error: 'Empty line',
322 line
323 };
324 }
325 
326 if (trimmedLine.startsWith('COUNT:')) {
327 const content = trimmedLine.substring(6); // Remove 'COUNT:'
328 const total = parseInt(content.trim(), 10);
330 if (isNaN(total) || total <= 0) {
331 return {
332 success: false,
333 error: 'Invalid count format - expected positive integer',
334 line
335 };
336 }
337 
338 return {
339 success: true,
340 setting: {
341 type: 'count',
342 total
343 }
344 };
345 }
346 
347 if (trimmedLine.startsWith('SELECTOR:')) {
348 const content = trimmedLine.substring(9); // Remove 'SELECTOR:'
349 const [name, color] = content.split('=');
351 if (!name || !color) {
352 return {
353 success: false,
354 error: 'Invalid selector format - expected name=color',
355 line
356 };
357 }
358 
359 if (!validateStreamingColor(color)) {
360 return {
361 success: false,
362 error: 'Invalid color format',
363 line
364 };
365 }
366 
367 return {
368 success: true,
369 setting: {
370 type: 'selector',
371 name: name.trim(),
372 color: color.trim()
373 }
374 };
375 }
376 
377 if (trimmedLine.startsWith('TOKEN:')) {
378 const content = trimmedLine.substring(6); // Remove 'TOKEN:'
379 const [scope, colorAndStyle] = content.split('=');
381 if (!scope || !colorAndStyle) {
382 return {
383 success: false,
384 error: 'Invalid token format - expected scope=color[,fontStyle]',
385 line
386 };
387 }
388 
389 const [color, fontStyle] = colorAndStyle.split(',');
391 if (!validateStreamingColor(color)) {
392 return {
393 success: false,
394 error: 'Invalid color format',
395 line
396 };
397 }
398 
399 return {
400 success: true,
401 setting: {
402 type: 'token',
403 scope: scope.trim(),
404 color: color.trim(),
405 fontStyle: fontStyle?.trim()
406 }
407 };
408 }
409 
410 if (trimmedLine.startsWith('MESSAGE:')) {
411 const content = trimmedLine.substring(8); // Remove 'MESSAGE:'
413 if (!content.trim()) {
414 return {
415 success: false,
416 error: 'Empty message content',
417 line
418 };
419 }
420 
421 return {
422 success: true,
423 setting: {
424 type: 'message',
425 content: content.trim()
426 }
427 };
428 }
429 
430 return {
431 success: false,
432 error: 'Line must start with COUNT:, SELECTOR:, TOKEN:, or MESSAGE:',
433 line
434 };
435};
436 
437/**
438 * Validates that a color string meets basic format requirements.
439 * Reuses hex validation logic for streaming theme colors.
440 * Also accepts REMOVE for theme iteration support.
441 */
442const validateStreamingColor = (color: string): boolean => {
443 // Accept REMOVE for theme iteration
444 if (color.trim().toUpperCase() === 'REMOVE') {
445 return true;
446 }
448 // Hex color validation (3, 6, or 8 digit hex codes)
449 const hexPattern = /^#[0-9a-fA-F]{3}$|^#[0-9a-fA-F]{6}$|^#[0-9a-fA-F]{8}$/;
450 const isValidHex = hexPattern.test(color);
451 const isValidKeyword = ['transparent', 'inherit', 'initial', 'unset'].includes(color.toLowerCase());
453 return isValidHex || isValidKeyword;
454};
455 
456/**
457 * Applies a single streaming theme setting immediately.
458 * Enables real-time theme updates as settings are received.
459 */
460export const applyStreamingThemeSetting = async (
461 setting: StreamingThemeSetting,
462 hasWorkspaceFolders: boolean
463): Promise<ThemeApplicationResult> => {
464 try {
465 // Handle count and message types - they don't need VS Code config updates
466 if (setting.type === 'count' || setting.type === 'message') {
467 const scope = determineConfigurationScope(hasWorkspaceFolders);
468 return createSuccessResult(scope);
469 }
470 
471 const config = vscode.workspace.getConfiguration();
472 const scope = determineConfigurationScope(hasWorkspaceFolders);
473 const targets = getConfigurationTargets(scope);
474 
475 if (setting.type === 'selector') {
476 // Apply color customization incrementally
477 for (const target of targets) {
478 try {
479 const existingColors = config.get('workbench.colorCustomizations') as Record<string, string> || {};
481 let updatedColors: Record<string, string>;
483 if (setting.color.trim().toUpperCase() === 'REMOVE') {
484 // Remove the setting by creating a new object without it
485 updatedColors = { ...existingColors };
486 delete updatedColors[setting.name];
487 } else {
488 // Add or update the setting
489 updatedColors = {
490 ...existingColors,
491 [setting.name]: setting.color
492 };
493 }
495 await config.update('workbench.colorCustomizations', updatedColors, target);
497 // Success on first target - determine which scope was used
498 const appliedScope: ConfigurationScope = targets.length === 1
499 ? (target === vscode.ConfigurationTarget.Workspace
500 ? { type: 'workspace', target: vscode.ConfigurationTarget.Workspace }
501 : { type: 'global', target: vscode.ConfigurationTarget.Global })
502 : { type: 'both', primary: targets[0], fallback: targets[1] };
504 return createSuccessResult(appliedScope);
505 } catch (error) {
506 // Continue to next target
507 continue;
508 }
509 }
⋯ 179 lines hidden (lines 510–688)
510 } else if (setting.type === 'token') {
511 // Apply token color customization incrementally
512 for (const target of targets) {
513 try {
514 const existingTokens = config.get('editor.tokenColorCustomizations') as Record<string, unknown> || {};
515 const existingRules = (existingTokens.textMateRules as any[]) || [];
517 let updatedRules: any[];
519 if (setting.color.trim().toUpperCase() === 'REMOVE') {
520 // Remove the token rule by filtering it out
521 updatedRules = existingRules.filter(rule => rule.scope !== setting.scope);
522 } else {
523 // Create new token rule
524 const newRule = {
525 scope: setting.scope,
526 settings: {
527 foreground: setting.color,
528 ...(setting.fontStyle && { fontStyle: setting.fontStyle })
529 }
530 };
532 // Update existing rules or add new one
533 updatedRules = [...existingRules.filter(rule => rule.scope !== setting.scope), newRule];
534 }
536 const updatedTokens = {
537 ...existingTokens,
538 textMateRules: updatedRules
539 };
541 await config.update('editor.tokenColorCustomizations', updatedTokens, target);
543 // Success on first target
544 const appliedScope: ConfigurationScope = targets.length === 1
545 ? (target === vscode.ConfigurationTarget.Workspace
546 ? { type: 'workspace', target: vscode.ConfigurationTarget.Workspace }
547 : { type: 'global', target: vscode.ConfigurationTarget.Global })
548 : { type: 'both', primary: targets[0], fallback: targets[1] };
550 return createSuccessResult(appliedScope);
551 } catch (error) {
552 // Continue to next target
553 continue;
554 }
555 }
556 }
557 
558 // All targets failed (only selectors and tokens reach here)
559 const settingIdentifier = setting.type === 'selector' ? setting.name :
560 setting.type === 'token' ? setting.scope : 'unknown';
562 return createFailureResult(
563 createThemeApplicationError(
564 `Failed to apply ${setting.type} setting: ${settingIdentifier}`,
565 new Error('All configuration targets failed'),
566 true,
567 'Check VS Code permissions and try restarting the editor'
568 )
569 );
570 
571 } catch (error) {
572 return createFailureResult(
573 createThemeApplicationError(
574 `Error applying streaming theme setting`,
575 error,
576 true,
577 'Check the setting format and try again'
578 )
579 );
580 }
581};
582 
583// =============================================================================
584// Current Theme State Reading
585// =============================================================================
586 
587/**
588 * Reads current workbench color customizations from VS Code configuration.
589 * Checks both workspace and global scopes to capture the complete state.
590 */
591export const getCurrentColorCustomizations = (): Record<string, string> => {
592 const config = vscode.workspace.getConfiguration();
594 // Get both workspace and global settings
595 const workspaceColors = config.get<Record<string, string>>('workbench.colorCustomizations', {});
596 const globalColors = config.get<Record<string, string>>('workbench.colorCustomizations', {});
598 // Workspace settings take precedence over global
599 return {
600 ...globalColors,
601 ...workspaceColors
602 };
603};
604 
605/**
606 * Reads current token color customizations from VS Code configuration.
607 * Combines workspace and global scopes with workspace taking precedence.
608 */
609export const getCurrentTokenColorCustomizations = (): Record<string, unknown> => {
610 const config = vscode.workspace.getConfiguration();
612 // Get both workspace and global settings
613 const workspaceTokens = config.get<Record<string, unknown>>('editor.tokenColorCustomizations', {});
614 const globalTokens = config.get<Record<string, unknown>>('editor.tokenColorCustomizations', {});
616 // Workspace settings take precedence over global
617 return {
618 ...globalTokens,
619 ...workspaceTokens
620 };
621};
622 
623/**
624 * Determines the effective scope of current customizations.
625 * Returns where the customizations are actually stored.
626 */
627export const getCurrentCustomizationScope = (): 'workspace' | 'global' | 'both' => {
628 const config = vscode.workspace.getConfiguration();
630 const workspaceColors = config.get<Record<string, string>>('workbench.colorCustomizations', {});
631 const globalColors = config.get<Record<string, string>>('workbench.colorCustomizations', {});
632 const workspaceTokens = config.get<Record<string, unknown>>('editor.tokenColorCustomizations', {});
633 const globalTokens = config.get<Record<string, unknown>>('editor.tokenColorCustomizations', {});
635 const hasWorkspaceCustomizations = Object.keys(workspaceColors).length > 0 || Object.keys(workspaceTokens).length > 0;
636 const hasGlobalCustomizations = Object.keys(globalColors).length > 0 || Object.keys(globalTokens).length > 0;
638 if (hasWorkspaceCustomizations && hasGlobalCustomizations) {
639 return 'both';
640 }
642 if (hasWorkspaceCustomizations) {
643 return 'workspace';
644 }
646 if (hasGlobalCustomizations) {
647 return 'global';
648 }
650 return 'global'; // Default scope when no customizations exist
651};
652 
653/**
654 * Reads the complete current theme state from VS Code configuration.
655 * This captures both color and token customizations with scope information.
656 */
657export const getCurrentThemeState = (): CurrentThemeResult => {
658 try {
659 const colorCustomizations = getCurrentColorCustomizations();
660 const tokenColorCustomizations = getCurrentTokenColorCustomizations();
661 const scope = getCurrentCustomizationScope();
663 const hasCustomizations =
664 Object.keys(colorCustomizations).length > 0 ||
665 Object.keys(tokenColorCustomizations).length > 0;
667 return {
668 success: true,
669 state: {
670 colorCustomizations,
671 tokenColorCustomizations,
672 hasCustomizations,
673 scope
674 }
675 };
677 } catch (error) {
678 return {
679 success: false,
680 error: createThemeApplicationError(
681 'Failed to read current theme state',
682 error,
683 true,
684 'Check VS Code configuration and try again'
685 )
686 };
687 }
688};
Act II · The supporting cast2.0🎬 Orientation

The satellites and the seam

Around the spine sit the smaller pieces: the thin OpenAI service wrapper, the three satellite commands, an orphaned color utility, and four files that call themselves tests but are not. Act II is where the structural critiques land.

Act II · The supporting cast2.1🎬 Q&A — is this a layer?

openaiService: a layer, or an echo?

src/services/openaiService.ts

The architecture promises a Service layer that wraps Core with "public APIs and orchestration." openaiService.ts is meant to be that wrapper. Open it. Almost every function is a one-line forward to its openaiCore twin.

initialize → core, get → core, ensure → core (unwrapping the Result). The pattern repeats.

src/services/openaiService.ts · 110 lines
src/services/openaiService.ts110 lines · TypeScript
⋯ 36 lines hidden (lines 1–36)
1/**
2 * OpenAI Service - Elegant orchestration of OpenAI client management.
3 *
4 * This module provides a refined interface for managing OpenAI client lifecycle.
5 * It embodies functional programming principles with domain-driven design, creating
6 * a beautiful separation between business logic and infrastructure concerns.
7 *
8 * Design Philosophy:
9 * - Pure functions for domain logic, side effects isolated to adapters
10 * - Rich type system that makes invalid states unrepresentable
11 * - Functional composition over imperative control flow
12 * - Explicit error handling with structured failure information
13 * - Dependency injection for testability and flexibility
14 */
15 
16import * as vscode from 'vscode';
17import OpenAI from 'openai';
18import { getSelectedOpenAIModel } from '../commands/modelSelectCommand';
19import {
20 initializeOpenAIClient as initializeOpenAIClientCore,
21 getCurrentOpenAIClient,
22 ensureOpenAIClient as ensureOpenAIClientCore,
23 getCurrentClientState,
24 resetOpenAIClient as resetOpenAIClientCore
25} from './openaiCore';
26 
27/**
28 * Initializes the OpenAI client with comprehensive error handling and user feedback.
29 *
30 * This is the main public API that maintains backward compatibility while leveraging
31 * our new functional architecture under the hood. The function transforms the legacy
32 * interface into our refined domain model and delegates to the pure business logic.
33 *
34 * @param context - The extension context used to access secrets storage
35 * @returns A boolean indicating whether initialization was successful
36 */
37export async function initializeOpenAIClient(context: vscode.ExtensionContext): Promise<boolean> {
38 // Call the core implementation directly and return success status
39 const result = await initializeOpenAIClientCore(context);
40 return result.success;
42 
43/**
44 * Get the current OpenAI client instance.
45 *
46 * Returns the active client if initialized and ready, or undefined if not available.
47 * This function maintains the simple interface while benefiting from the improved
48 * state management of our new architecture.
49 *
50 * @returns The current OpenAI client or undefined if not initialized
51 */
52export function getOpenAIClient(): OpenAI | undefined {
53 return getCurrentOpenAIClient();
55 
56/**
57 * Ensures the OpenAI client is initialized and available.
58 *
59 * If not initialized, prompts the user for the API key and initializes the client.
60 * This function provides the same interface as before but with improved error handling,
61 * validation, and user experience through our new architecture.
62 *
63 * @param context - The extension context used for initialization if needed
64 * @returns The OpenAI client instance or undefined if initialization fails
65 */
66export async function ensureOpenAIClient(context: vscode.ExtensionContext): Promise<OpenAI | undefined> {
67 // Use our new architecture with rich error handling
68 const result = await ensureOpenAIClientCore(context);
70 // Transform result to legacy format for backward compatibility
71 if (result.success) {
72 return result.data;
73 } else {
74 // The error has already been shown to the user by the application layer
75 // We just need to return undefined to maintain the legacy contract
76 return undefined;
77 }
⋯ 32 lines hidden (lines 79–110)
79 
80/**
81 * Gets the current state of the OpenAI client for debugging or status display.
82 *
83 * This is a new function that exposes the rich state information from our
84 * improved architecture. Useful for commands that need to show client status.
85 *
86 * @returns Current client state with detailed information
87 */
88export function getOpenAIClientState() {
89 return getCurrentClientState();
91 
92/**
93 * Resets the OpenAI client and clears stored credentials.
94 *
95 * This is a new function that leverages our improved architecture to provide
96 * clean reset functionality with proper error handling.
97 *
98 * @param context - The extension context for accessing storage
99 * @returns Promise that resolves when reset is complete
100 */
101export async function resetOpenAIClient(context: vscode.ExtensionContext): Promise<void> {
102 const result = await resetOpenAIClientCore(context);
104 // For the public API, we don't need to return the result
105 // Errors are handled internally and shown to the user
106 if (!result.success) {
107 // Log for debugging but don't throw - the user has already been notified
108 console.error('OpenAI client reset failed:', result.error);
109 }
Act II · The supporting cast2.2🎬 Gallery

The three satellite commands

src/commands/resetThemeCommand.tssrc/commands/modelSelectCommand.tssrc/commands/clearApiKeyCommand.ts

Reset is the most important command in the extension. The README begs users to run it. It is also the simplest: blank both color and token customizations across workspace and global scope with Promise.allSettled, so a failure in one target never blocks the others.

Clear all four (scope × setting) targets, tolerate partial failure.

src/commands/resetThemeCommand.ts · 39 lines
src/commands/resetThemeCommand.ts39 lines · TypeScript
⋯ 8 lines hidden (lines 1–8)
1import * as vscode from 'vscode';
2 
3/**
4 * Registers the command to reset theme customizations.
5 */
6export function registerResetThemeCommand(context: vscode.ExtensionContext, lastGeneratedThemeRef?: { current?: any }) {
7 const resetThemeCommand = vscode.commands.registerCommand('vibeThemer.resetTheme', async () => {
8 try {
9 const config = vscode.workspace.getConfiguration();
11 // Clear both workspace and global settings
12 const resetPromises = [
13 config.update('workbench.colorCustomizations', undefined, vscode.ConfigurationTarget.Workspace),
14 config.update('editor.tokenColorCustomizations', undefined, vscode.ConfigurationTarget.Workspace),
15 config.update('workbench.colorCustomizations', undefined, vscode.ConfigurationTarget.Global),
16 config.update('editor.tokenColorCustomizations', undefined, vscode.ConfigurationTarget.Global)
17 ];
19 await Promise.allSettled(resetPromises);
21 // Clear the last generated theme
22 if (lastGeneratedThemeRef) {
23 lastGeneratedThemeRef.current = undefined;
24 }
⋯ 15 lines hidden (lines 25–39)
26 vscode.window.showInformationMessage(
27 '🔄 Theme customizations cleared successfully! Your original VS Code theme has been restored.',
28 {
29 modal: true,
30 detail: 'All Vibe Themer color overrides have been removed. You can now change to a different base theme in VS Code settings or generate a new Vibe Themer theme.'
31 }
32 );
33 } catch (error: any) {
34 vscode.window.showErrorMessage(`Failed to reset theme: ${error.message}`);
35 }
36 });
38 context.subscriptions.push(resetThemeCommand);

Model selection lists the account's GPT models, filtered to ids starting gpt, and saves the pick to globalState. Clearing the key delegates to resetOpenAIClient. Nothing fancy. All three are small and correct.

src/commands/modelSelectCommand.ts · 43 lines
src/commands/modelSelectCommand.ts43 lines · TypeScript
⋯ 5 lines hidden (lines 1–5)
1import * as vscode from 'vscode';
2import { ensureOpenAIClient } from '../services/openaiService';
3 
4const MODEL_KEY = 'openaiModel';
5 
6export async function selectOpenAIModel(context: vscode.ExtensionContext) {
7 const openai = await ensureOpenAIClient(context);
8 if (!openai) {return;}
9 let models: string[] = [];
10 try {
11 const response = await openai.models.list();
12 models = response.data
13 .map((m: any) => m.id)
14 .filter((modelId: string) => modelId.toLowerCase().startsWith('gpt'));
15 } catch (err: any) {
16 vscode.window.showErrorMessage('🔑 Failed to fetch OpenAI models: ' + err.message + '\n\nPlease check your API key and internet connection.', { modal: true });
17 return;
18 }
19 if (!models.length) {
20 vscode.window.showWarningMessage('⚠️ No OpenAI models available for your API key. Please verify your API key has the necessary permissions.', { modal: true });
21 return;
22 }
23 const current = context.globalState.get<string>(MODEL_KEY);
24 const pick = await vscode.window.showQuickPick(models, {
25 title: '🤖 Select OpenAI Model for Theme Generation',
26 placeHolder: current ? `Currently using: ${current}` : 'Choose which AI model to use (GPT-4 recommended for best themes)',
27 canPickMany: false,
28 ignoreFocusOut: true
29 });
30 if (pick) {
31 await context.globalState.update(MODEL_KEY, pick);
32 vscode.window.showInformationMessage(`🎯 OpenAI model updated to: ${pick}\n\nThis model will be used for all future theme generations.`, { modal: true });
33 }
⋯ 9 lines hidden (lines 35–43)
35 
36export async function resetOpenAIModel(context: vscode.ExtensionContext) {
37 await context.globalState.update(MODEL_KEY, undefined);
38 vscode.window.showInformationMessage('🔄 OpenAI model selection has been reset to default.\n\nThe extension will now use the default model for theme generation.', { modal: true });
40 
41export function getSelectedOpenAIModel(context: vscode.ExtensionContext): string | undefined {
42 return context.globalState.get<string>(MODEL_KEY);
src/commands/clearApiKeyCommand.ts · 53 lines
src/commands/clearApiKeyCommand.ts53 lines · TypeScript
⋯ 21 lines hidden (lines 1–21)
1/**
2 * Clear API Key Command - Elegant credential management.
3 *
4 * This module provides a clean interface for clearing stored OpenAI credentials.
5 * It leverages our refactored architecture to provide proper error handling,
6 * state management, and user feedback.
7 */
8 
9import * as vscode from 'vscode';
10import OpenAI from 'openai';
11import { resetOpenAIClient } from '../services/openaiService';
12 
13/**
14 * Registers the command to clear the OpenAI API key and reset the OpenAI client.
15 *
16 * Now uses our improved architecture for robust credential management with
17 * comprehensive error handling and proper state cleanup.
18 *
19 * @param context - The extension context for command registration and credential access
20 * @param openaiRef - Legacy reference maintained for backward compatibility
21 */
22export function registerClearApiKeyCommand(
23 context: vscode.ExtensionContext,
24 openaiRef?: { current?: OpenAI }
25) {
26 const clearApiKeyCommand = vscode.commands.registerCommand(
27 'vibeThemer.clearApiKey',
28 async () => {
29 try {
30 // Use our new architecture for robust credential clearing
31 await resetOpenAIClient(context);
33 // Clear legacy reference for backward compatibility
34 if (openaiRef) {
35 openaiRef.current = undefined;
36 }
38 // Success feedback is handled by the service layer
39 // No need for duplicate messaging here
41 } catch (error) {
42 // Fallback error handling in case the service layer fails
43 console.error('Failed to clear API key:', error);
44 vscode.window.showErrorMessage(
45 'Failed to clear API key. Please try again or restart VS Code.',
46 { modal: true }
47 );
48 }
49 }
50 );
52 context.subscriptions.push(clearApiKeyCommand);
⋯ 1 line hidden (lines 53–53)
Act II · The supporting cast2.3🎬 Counterfactual

colorUtils: 108 lines nobody calls

src/utils/colorUtils.ts

colorUtils.ts is a competent little color library — normalize any CSS color to hex, detect dark vs. light, compute a contrast color, adjust brightness. The header comment even calls it part of the "Dynamic Theme Changer extension," an earlier name. There is one problem: nothing imports it.

normalizeColor and friends — exported, tested by no one, called by no one.

src/utils/colorUtils.ts · 108 lines
src/utils/colorUtils.ts108 lines · TypeScript
⋯ 7 lines hidden (lines 1–7)
1/**
2 * Color utility functions for the Dynamic Theme Changer extension
3 */
4 
5/**
6 * Normalizes a color string to a valid hex format
7 */
8export function normalizeColor(color: string): string {
9 if (!color) {return '#000000';}
11 // Clean input
12 color = color.trim().replace(/['"]/g, '');
14 // Convert RGB to hex
15 const rgbMatch = color.match(/rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/i);
16 if (rgbMatch) {
17 const [, r, g, b] = rgbMatch;
18 return `#${Number(r).toString(16).padStart(2, '0')}${Number(g).toString(16).padStart(2, '0')}${Number(b).toString(16).padStart(2, '0')}`;
19 }
21 // Convert HSL to hex
22 const hslMatch = color.match(/hsl\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)/i);
23 if (hslMatch) {
24 const [, h, s, l] = hslMatch;
25 return hslToHex(Number(h), Number(s) / 100, Number(l) / 100);
26 }
28 // Add # prefix if missing
29 if (!color.startsWith('#')) {
30 color = '#' + color;
31 }
33 // Expand shorthand hex (#rgb -> #rrggbb)
34 if (color.length === 4 && /^#[0-9a-fA-F]{3}$/i.test(color)) {
35 const [, r, g, b] = color;
36 color = `#${r}${r}${g}${g}${b}${b}`;
37 }
39 // Validate and fix hex length
40 if (/^#[0-9a-f]{1,5}$/i.test(color)) {
41 color = color.padEnd(7, '0');
42 } else if (/^#[0-9a-f]{7,}$/i.test(color)) {
43 color = color.substring(0, 7);
44 }
46 // Extract hex part if embedded in string
47 const hexMatch = color.match(/#[0-9a-f]{6}/i);
48 if (hexMatch) {
49 color = hexMatch[0];
50 }
52 return /^#[0-9a-f]{6}$/i.test(color) ? color.toLowerCase() : '#000000';
⋯ 55 lines hidden (lines 54–108)
54 
55function hslToHex(h: number, s: number, l: number): string {
56 h /= 360;
57 const hue2rgb = (p: number, q: number, t: number) => {
58 if (t < 0) {t += 1;}
59 if (t > 1) {t -= 1;}
60 if (t < 1/6) {return p + (q - p) * 6 * t;}
61 if (t < 1/2) {return q;}
62 if (t < 2/3) {return p + (q - p) * (2/3 - t) * 6;}
63 return p;
64 };
66 let r: number, g: number, b: number;
67 if (s === 0) {
68 r = g = b = l;
69 } else {
70 const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
71 const p = 2 * l - q;
72 r = hue2rgb(p, q, h + 1/3);
73 g = hue2rgb(p, q, h);
74 b = hue2rgb(p, q, h - 1/3);
75 }
77 return `#${Math.round(r * 255).toString(16).padStart(2, '0')}${Math.round(g * 255).toString(16).padStart(2, '0')}${Math.round(b * 255).toString(16).padStart(2, '0')}`;
79 
80/**
81 * Determines if a color represents a dark theme
82 */
83export function isDarkTheme(backgroundColor: string): boolean {
84 const hex = normalizeColor(backgroundColor);
85 const r = parseInt(hex.slice(1, 3), 16);
86 const g = parseInt(hex.slice(3, 5), 16);
87 const b = parseInt(hex.slice(5, 7), 16);
88 const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
89 return luminance < 0.5;
91 
92/**
93 * Gets a contrasting color (black or white) for the given background color
94 */
95export function getContrastColor(backgroundColor: string): string {
96 return isDarkTheme(backgroundColor) ? '#ffffff' : '#000000';
98 
99/**
100 * Adjusts the brightness of a color by a multiplier
101 */
102export function adjustColor(color: string, multiplier: number): string {
103 const hex = normalizeColor(color);
104 const r = Math.min(255, Math.max(0, Math.round(parseInt(hex.slice(1, 3), 16) * multiplier)));
105 const g = Math.min(255, Math.max(0, Math.round(parseInt(hex.slice(3, 5), 16) * multiplier)));
106 const b = Math.min(255, Math.max(0, Math.round(parseInt(hex.slice(5, 7), 16) * multiplier)));
107 return `#${r.toString(16).padStart(2, '0')}${g.toString(16).padStart(2, '0')}${b.toString(16).padStart(2, '0')}`;
Act II · The supporting cast2.4🎬 Evidence

The "tests" that aren't

src/utils/themeStateTest.tssrc/utils/countParsingTest.ts

Four files end in Test.ts. None is a test. Each is a manual command that reads some state, or runs a parser over hard-coded inputs, and prints the result to a console or output channel. They fire only when you invoke them by hand, from the palette, in a Development host.

Hard-coded inputs, console.log assertions-by-eyeball. No runner, no pass/fail gate.

src/utils/countParsingTest.ts · 46 lines
src/utils/countParsingTest.ts46 lines · TypeScript
⋯ 10 lines hidden (lines 1–10)
1/**
2 * Simple test utility to verify COUNT parsing functionality works correctly.
3 */
4 
5import * as vscode from 'vscode';
6import { parseStreamingThemeLine } from '../services/themeCore';
7 
8/**
9 * Test function to verify COUNT line parsing works correctly.
10 */
11export async function testCountParsing(): Promise<void> {
12 console.log('🧪 Testing COUNT line parsing...');
13 
14 const testCases = [
15 'COUNT:125',
16 'COUNT:80',
17 'COUNT:150',
18 'COUNT:invalid',
19 'COUNT:-10',
20 'COUNT:',
21 'SELECTOR:editor.background=#1a1a1a',
22 'MESSAGE:Testing message...'
23 ];
24 
25 for (const testLine of testCases) {
26 console.log(`\n📝 Testing: "${testLine}"`);
28 const result = parseStreamingThemeLine(testLine);
30 if (result.success) {
31 console.log(`✅ Parsed successfully:`, result.setting);
33 if (result.setting.type === 'count') {
34 console.log(`🎯 Extracted count: ${result.setting.total}`);
35 }
36 } else {
37 console.log(`❌ Parse failed: ${result.error}`);
38 }
39 }
40 
⋯ 6 lines hidden (lines 41–46)
41 // Show summary
42 vscode.window.showInformationMessage(
43 '🧪 COUNT parsing test completed! Check the Output panel for detailed results.',
44 { modal: false }
45 );

Reads current theme state and prints it. A manual inspector.

src/utils/themeStateTest.ts · 54 lines
src/utils/themeStateTest.ts54 lines · TypeScript
1/**
2 * Simple manual test for current theme state reading functionality.
3 * This can be invoked from the VS Code extension host to verify our implementation.
4 */
5 
6import * as vscode from 'vscode';
7import { getCurrentThemeState } from '../services/themeCore';
8 
9/**
10 * Manual test function to verify current theme state reading.
11 * Call this from the extension to test the functionality.
12 */
13export async function testCurrentThemeReading(): Promise<void> {
14 console.log('🧪 Testing current theme state reading...');
16 // Read the current theme state
17 const result = getCurrentThemeState();
19 if (!result.success) {
20 console.error('❌ Failed to read current theme state:', result.error);
21 vscode.window.showErrorMessage(`Theme state test failed: ${result.error.message}`);
22 return;
23 }
25 const { state } = result;
27 console.log('✅ Current theme state reading successful!');
28 console.log('📊 Theme State Summary:');
29 console.log(` - Has customizations: ${state.hasCustomizations}`);
30 console.log(` - Scope: ${state.scope}`);
31 console.log(` - Color customizations count: ${Object.keys(state.colorCustomizations).length}`);
32 console.log(` - Token customizations count: ${Object.keys(state.tokenColorCustomizations).length}`);
34 if (state.hasCustomizations) {
35 console.log('🎨 Current Color Customizations:');
36 Object.entries(state.colorCustomizations).forEach(([key, value]) => {
37 console.log(` - ${key}: ${value}`);
38 });
40 if (Object.keys(state.tokenColorCustomizations).length > 0) {
41 console.log('🔤 Current Token Customizations:');
42 console.log(JSON.stringify(state.tokenColorCustomizations, null, 2));
43 }
44 } else {
45 console.log('🚫 No theme customizations currently active');
46 }
48 // Show user-friendly message
49 const message = state.hasCustomizations
50 ? `Found ${Object.keys(state.colorCustomizations).length} color settings and token customizations in ${state.scope} scope`
51 : 'No theme customizations currently active';
53 vscode.window.showInformationMessage(`Theme State Test: ${message}`);

Re-implements the context-formatting logic to preview what the AI would receive.

src/utils/contextInjectionTest.ts · 94 lines
src/utils/contextInjectionTest.ts94 lines · TypeScript
1import * as vscode from 'vscode';
2import { getCurrentThemeState } from '../services/themeCore.js';
3 
4/**
5 * Tests the context injection by simulating the formatting logic.
6 * This is a development utility to verify the feature works correctly.
7 */
8export async function testContextInjection(): Promise<void> {
9 const outputChannel = vscode.window.createOutputChannel('Vibe Themer Context Test');
10 outputChannel.show();
12 try {
13 outputChannel.appendLine('=== Context Injection Test ===');
14 outputChannel.appendLine('Testing current theme state formatting...\n');
16 // Get current theme state
17 const themeStateResult = getCurrentThemeState();
19 if (!themeStateResult.success) {
20 outputChannel.appendLine('❌ Failed to read current theme state');
21 outputChannel.appendLine(`Error: ${themeStateResult.error.message}`);
22 return;
23 }
25 const { state } = themeStateResult;
27 if (!state.hasCustomizations) {
28 outputChannel.appendLine('ℹ️ No current theme customizations detected');
29 outputChannel.appendLine('Context injection would be skipped (standard generation)');
31 vscode.window.showInformationMessage(
32 'No theme customizations found. Generate a theme first, then test again.'
33 );
34 return;
35 }
37 // Simulate the context formatting logic
38 const lines: string[] = [];
40 const colorCount = Object.keys(state.colorCustomizations).length;
41 if (colorCount > 0) {
42 lines.push('CURRENT THEME CONTEXT:');
43 lines.push(`Active workbench color overrides (${colorCount} settings):`);
45 Object.entries(state.colorCustomizations).slice(0, 5).forEach(([key, value]) => {
46 lines.push(`- ${key}: ${value}`);
47 });
49 if (colorCount > 5) {
50 lines.push(`... and ${colorCount - 5} more settings`);
51 }
52 }
54 const tokenCount = Object.keys(state.tokenColorCustomizations).length;
55 if (tokenCount > 0) {
56 if (lines.length > 0) {
57 lines.push('');
58 }
59 lines.push(`Active syntax highlighting overrides (${tokenCount} settings):`);
61 Object.entries(state.tokenColorCustomizations).slice(0, 3).forEach(([key, value]) => {
62 lines.push(`- ${key}: ${JSON.stringify(value)}`);
63 });
65 if (tokenCount > 3) {
66 lines.push(`... and ${tokenCount - 3} more settings`);
67 }
68 }
70 if (lines.length > 0) {
71 lines.push('');
72 lines.push('User request:');
74 const contextString = lines.join('\n');
76 outputChannel.appendLine('✅ Context injection would be active');
77 outputChannel.appendLine('Generated context string:');
78 outputChannel.appendLine('---');
79 outputChannel.appendLine(contextString);
80 outputChannel.appendLine('---');
82 outputChannel.appendLine(`\nScope: ${state.scope}`);
83 outputChannel.appendLine(`Total settings: ${colorCount + tokenCount}`);
85 vscode.window.showInformationMessage(
86 `✅ Context injection ready! ${colorCount + tokenCount} settings would be sent to AI for better iteration support.`
87 );
88 }
90 } catch (error) {
91 outputChannel.appendLine(`❌ Test failed: ${error}`);
92 vscode.window.showErrorMessage(`Context injection test failed: ${error}`);
93 }

Runs the parser over REMOVE-valued lines and tallies pass/fail by eye.

src/utils/removeValueTest.ts · 56 lines
src/utils/removeValueTest.ts56 lines · TypeScript
1/**
2 * Test utility to verify REMOVE value functionality in theme iteration.
3 */
4 
5import * as vscode from 'vscode';
6import { parseStreamingThemeLine } from '../services/themeCore';
7 
8/**
9 * Test function to verify REMOVE value parsing and handling works correctly.
10 */
11export async function testRemoveValues(): Promise<void> {
12 console.log('🧪 Testing REMOVE value functionality...');
13 
14 const testCases = [
15 'SELECTOR:editor.background=REMOVE',
16 'SELECTOR:activityBar.background=remove',
17 'SELECTOR:statusBar.background=Remove',
18 'TOKEN:comment=REMOVE',
19 'TOKEN:keyword=remove,bold',
20 'SELECTOR:editor.foreground=#ffffff',
21 'TOKEN:string=#00ff00,italic'
22 ];
23 
24 let successCount = 0;
25 let failureCount = 0;
26 
27 for (const testLine of testCases) {
28 console.log(`\n📝 Testing: "${testLine}"`);
30 const result = parseStreamingThemeLine(testLine);
32 if (result.success) {
33 console.log(`✅ Parsed successfully:`, result.setting);
34 successCount++;
36 if (result.setting.type === 'selector') {
37 const isRemove = result.setting.color.trim().toUpperCase() === 'REMOVE';
38 console.log(`🎯 Selector "${result.setting.name}" - ${isRemove ? 'REMOVE' : 'COLOR'}: ${result.setting.color}`);
39 } else if (result.setting.type === 'token') {
40 const isRemove = result.setting.color.trim().toUpperCase() === 'REMOVE';
41 console.log(`🎯 Token "${result.setting.scope}" - ${isRemove ? 'REMOVE' : 'COLOR'}: ${result.setting.color}`);
42 }
43 } else {
44 console.log(`❌ Parse failed: ${result.error}`);
45 failureCount++;
46 }
47 }
48 
49 // Show summary
50 console.log(`\n📊 Test Summary: ${successCount} passed, ${failureCount} failed`);
52 vscode.window.showInformationMessage(
53 `🧪 REMOVE value test completed! ${successCount} passed, ${failureCount} failed. Check the Output panel for detailed results.`,
54 { modal: false }
55 );

Meanwhile the project is wired for real tests. .vscode-test.mjs, @vscode/test-cli, and @vscode/test-electron are all installed, and the test script is defined. But the runner points at out/test/**/*.test.js, and no test/ directory exists. So npm test finds nothing and exits green. The harness is a stage with no actors.

Act III · Build, ship, document3.1🎬 Pipeline

Build, bundle, and the packaging scars

esbuild.js.github/workflows/ci.ymlpackage.json

esbuild bundles src/extension.ts into one CJS file, with vscode marked external because the host provides it. The two runtime dependencies, openai and zod, get bundled in. That bundling was itself a hard-won fix. CHANGELOG v1.0.8 is titled "MAJOR FIX" for shipping without its dependencies and breaking every install.

esbuild.js · 54 lines
esbuild.js54 lines · JavaScript
⋯ 25 lines hidden (lines 1–25)
1const esbuild = require('esbuild');
2 
3const production = process.argv.includes('--production');
4const watch = process.argv.includes('--watch');
5 
6/**
7 * @type {import('esbuild').Plugin}
8 */
9const esbuildProblemMatcherPlugin = {
10 name: 'esbuild-problem-matcher',
11 
12 setup(build) {
13 build.onStart(() => {
14 console.log('[watch] build started');
15 });
16 build.onEnd((result) => {
17 result.errors.forEach(({ text, location }) => {
18 console.error(`✘ [ERROR] ${text}`);
19 console.error(` ${location.file}:${location.line}:${location.column}:`);
20 });
21 console.log('[watch] build finished');
22 });
23 },
24};
25 
26async function main() {
27 const ctx = await esbuild.context({
28 entryPoints: ['src/extension.ts'],
29 bundle: true,
30 format: 'cjs',
31 minify: production,
32 sourcemap: !production,
33 sourcesContent: false,
34 platform: 'node',
35 outfile: 'out/extension.js',
36 external: ['vscode'],
37 logLevel: 'silent',
38 plugins: [
39 /* add to the end of plugins array */
40 esbuildProblemMatcherPlugin,
41 ],
42 });
⋯ 12 lines hidden (lines 43–54)
43 if (watch) {
44 await ctx.watch();
45 } else {
46 await ctx.rebuild();
47 await ctx.dispose();
48 }
50 
51main().catch(e => {
52 console.error(e);
53 process.exit(1);
54});

The manifest: 5 user commands + 4 dev commands, activation events, the two runtime deps, and the compile/test scripts.

package.json · 106 lines
package.json106 lines · JSON
1{
2 "name": "vibe-themer",
3 "displayName": "Vibe Themer",
4 "description": "🎨 AI-powered VS Code theme generator. Describe your vibe, watch real-time streaming as OpenAI creates comprehensive custom themes with a complete theme. 🤖 100% vibe-coded using VS Code's agentic AI!",
5 "version": "1.1.0",
6 "publisher": "mseeks",
7 "icon": "icon.png",
8 "repository": {
9 "type": "git",
10 "url": "https://github.com/mseeks/vibe-themer"
11 },
12 "sponsor": {
13 "url": "https://github.com/sponsors/mseeks"
14 },
15 "engines": {
16 "vscode": "^1.74.0"
17 },
18 "activationEvents": [
19 "onCommand:vibeThemer.changeTheme",
20 "onCommand:vibeThemer.clearApiKey",
21 "onCommand:vibeThemer.resetTheme",
22 "onCommand:vibeThemer.selectModel",
23 "onCommand:vibeThemer.resetModel"
24 ],
25 "categories": [
26 "Themes",
27 "Other"
28 ],
29 "keywords": [
30 "theme",
31 "color",
32 "ai",
33 "openai",
34 "customization"
35 ],
36 "main": "./out/extension.js",
37 "contributes": {
38 "commands": [
39 {
40 "command": "vibeThemer.changeTheme",
41 "title": "Vibe Themer: Change Theme"
42 },
43 {
44 "command": "vibeThemer.clearApiKey",
45 "title": "Vibe Themer: Clear OpenAI API Key"
46 },
47 {
48 "command": "vibeThemer.resetTheme",
49 "title": "Vibe Themer: Reset Theme Customizations"
50 },
51 {
52 "command": "vibeThemer.selectModel",
53 "title": "Vibe Themer: Select OpenAI Model"
54 },
55 {
56 "command": "vibeThemer.resetModel",
57 "title": "Vibe Themer: Reset OpenAI Model Selection"
58 },
59 {
60 "command": "vibeThemer.testThemeState",
61 "title": "Vibe Themer: Test Current Theme State (Dev)",
62 "when": "vibeThemer.development"
63 },
64 {
65 "command": "vibeThemer.testCountParsing",
66 "title": "Vibe Themer: Test COUNT Parsing (Dev)",
67 "when": "vibeThemer.development"
68 },
69 {
70 "command": "vibeThemer.testContextInjection",
71 "title": "Vibe Themer: Test Context Injection (Dev)",
72 "when": "vibeThemer.development"
73 },
74 {
75 "command": "vibeThemer.testRemoveValue",
76 "title": "Vibe Themer: Test REMOVE Value Handling (Dev)",
77 "when": "vibeThemer.development"
78 }
79 ]
80 },
81 "scripts": {
82 "vscode:prepublish": "npm run compile",
83 "compile": "npm run check-types && npm run lint && node ./esbuild.js",
84 "check-types": "tsc --noEmit",
85 "watch": "npm run check-types && node ./esbuild.js --watch",
86 "pretest": "npm run compile && npm run lint",
87 "lint": "eslint src --ext ts",
88 "test": "vscode-test"
89 },
90 "devDependencies": {
91 "@types/mocha": "^10.0.6",
92 "@types/node": "^20.17.58",
93 "@types/vscode": "^1.74.0",
94 "@typescript-eslint/eslint-plugin": "^7.7.1",
95 "@typescript-eslint/parser": "^7.7.1",
96 "@vscode/test-cli": "^0.0.12",
97 "@vscode/test-electron": "^2.3.9",
98 "esbuild": "^0.25.12",
99 "eslint": "^8.57.0",
100 "typescript": "^5.4.5"
101 },
102 "dependencies": {
103 "openai": "^4.104.0",
104 "zod": "^3.25.76"
105 }

CI is honest about its scope. It runs npm ci, then npm run compile (types, lint, bundle) on every push and PR. A comment notes that the electron suite is left out because it needs a headless display. So CI guards compilation and style. It says nothing about behavior.

The whole oracle: install, then compile. No test step (there are no tests to run).

.github/workflows/ci.yml · 30 lines
.github/workflows/ci.yml30 lines · YAML
⋯ 15 lines hidden (lines 1–15)
1name: CI
2 
3on:
4 push:
5 branches: [main]
6 pull_request:
7 
8# Cancel superseded runs on the same ref to save CI minutes.
9concurrency:
10 group: ci-${{ github.ref }}
11 cancel-in-progress: true
12 
13permissions:
14 contents: read
15 
16jobs:
17 check:
18 name: check (types · lint · build)
19 runs-on: ubuntu-latest
20 steps:
21 - uses: actions/checkout@v4
22 - uses: actions/setup-node@v4
23 with:
24 node-version: "22"
25 cache: npm
26 # npm ci installs the committed lockfile exactly; `compile` runs
27 # tsc --noEmit + eslint + the esbuild bundle. The @vscode/test-electron
28 # suite needs a headless VS Code (xvfb) and is left out of the oracle.
29 - run: npm ci
30 - run: npm run compile
Act III · Build, ship, document3.2🎬 Claim vs. reality

Documentation: thorough, and ahead of the code

package.json

For a 2,600-line extension the documentation is unusually rich. A polished README. An ARCHITECTURE doc. Two ADRs that bother with an alternatives-considered section, a detailed CHANGELOG, and a 300-line Copilot file defining the project's voice. The intent and the craft are real.

But the docs describe a project slightly bigger than the one that exists. ADR-002's Phase 3, AI-generated prompt suggestions, is written up as "next up," and suggestionCore.ts was built as its foundation. It never shipped. Only the curated fallbacks exist, yet the Copilot file still calls it the current frontier. Smaller drifts pile up underneath. The engine version in package.json (^1.74.0) disagrees with the README's "1.68.0 or higher," and the CHANGELOG points at design docs that left the tree.

Act IV · The verdict4.1🎬 Confirmed defects

Confirmed bugs

This review was itself a fan-out. Seven agents swept the code by dimension. Then every behavioral claim went to an adversarial verifier whose only job was to refute it against the source. Thirteen findings survived. But the verifiers are language models. They can read code, not run it, so two of their loudest findings still needed a human check. Both were over-claims. In a project whose identity is "zero manual edits," the review process earns the same scrutiny as the code.

What remains is real. One finding is a security bug to fix today. The rest are a cost leak, a few correctness slips, some UX rough edges. None is catastrophic. And the buffer "double-apply" the panel worried about was refuted: the trailing-buffer flush runs only on clean completion, so it never double-counts.

WhereWhat's actually wrongImpact
🔒 SecuritythemeGenerationService.ts:426On any generation error, console.error logs getCurrentClientState(), which holds the plaintext apiKey (openaiCore.ts:49)The OpenAI key lands in the extension-host console/logs the first time generation fails. The one to fix now.
💸 CoststreamingThemePrompt.txt5 sections duplicated verbatim — PROFILES, SIDE BAR, MINIMAP, EDITOR GROUPS & TABS, EDITOR COLORS~19% of every request's input tokens wasted, on every call, forever
🪲 CorrectnessthemeCore.ts:591–651getCurrent*Customizations / …Scope read the same merged key twice via config.get instead of config.inspectWorkspace vs global can't be told apart; harmless today only because nothing user-facing reads scope
🪲 Correctnessextension.ts:49Each command passes a fresh { current: lastGeneratedTheme } wrapper, so writes to .current never reach the module variableThe "last generated theme" is never actually remembered; the ref-cell is a no-op
🐞 Dev-onlypackage.json:75 vs extension.ts:90Command id testRemoveValue (manifest) ≠ testRemoveValues (handler)The dev palette entry resolves to no handler — a one-character typo
🎛️ UXthemeGenerationService.ts:173lastMessageTime starts at Date.now(), so the intended === 0 first-message bypass never firesThe first AI progress message can be withheld up to 800 ms
🎛️ UXthemeGenerationService.ts:56–60Completeness label uses hardcoded <50 / <80 thresholds unrelated to expectedSettingsCountA complete 45-setting theme gets mislabeled "Partial"
The findings that survived adversarial verification and a human re-check. Severity descends down the table.
Act IV · The verdict4.2🎬 Grades

State of the project: a scorecard

Across seven dimensions the panel landed on an overall C, nudged to C+ here once the two over-claims above deflated the correctness picture. The pattern is consistent end to end: nothing is broken enough to fail, nothing is finished enough to shine.

DimensionGradeOne-line read
Architecture & structureCCoherent happy path and an exemplary suggestionCore, undercut by a passthrough "service" layer, a services→commands backward edge, and dev harnesses living in src/utils
Correctness & bugsCOne real security bug plus a cluster of minor slips; the two scary headline bugs deflated on fact-check
Code quality & idiomsCGenuine discriminated-union typing beside 16 anys, three copies of the scope-construction block, and JSDoc that oversells
Testing, CI & packagingDBuild and packaging are sound; tests are zero, behind a false-green npm test
Security & dependenciesCGood secrets baseline, pulled down by the key-logging leak; end-user dependency risk is actually fine
Product, UX & promptCThe core experience works; the token tax, modal-heavy flow, and undiscoverable iteration hold it back
Docs, hygiene & roadmapCUnusually thorough, but describes a slightly bigger project than exists
Per-dimension grades. Testing is the clear outlier — and the root cause of most of the rest reaching the marketplace unseen.

What's genuinely good

What should worry you

Act IV · The verdict4.3🎬 Prioritized

What to fix, in order

Sequenced by leverage, not by size. The first two are an afternoon's work and buy most of the value.

#EffortFix
P0SStop logging the key. Redact clientState (mask/omit apiKey) before console.error at themeGenerationService.ts:426. Security-critical, one line.
P1SDe-duplicate the prompt. Delete the second copy of the 5 repeated sections; verify every selector still appears once. Cuts ~19% off every call's input tokens — a recurring cost/latency win.
P1M**Make npm test honest.** Add unit tests for parseStreamingThemeLine, color validation, and a package.json↔handler command-id check (which would've caught the testRemoveValue(s) typo). Wire into CI to end the packaging-regression pattern.
P1MFix the scope reader. Rewrite getCurrent*Customizations / …Scope to use config.inspect()'s globalValue / workspaceValue so workspace and global are actually distinguished.
P2MSweep dead code & the redundant layer. Delete colorUtils.ts, applyThemeCustomizations, the ThemeData/lastGeneratedTheme ref (and its no-op wrapper); delete openaiService.ts or give it a job; move the *Test.ts harnesses out of src/utils.
P2SReconcile the docs. Engine version (README ↔ package.json), dead CHANGELOG doc links, the two 002-* ADR files, ARCHITECTURE module list, and mark ADR-002 Phase 3 deferred. Fix the command-id typo and the lastMessageTime init while you're in there.
P3MUX polish. Non-modal success popup; a principled completeness signal tied to expectedSettingsCount instead of <50/<80; a discoverable "Refine Current Theme" entry once the scope fix lands.
OptSMake the default model configurable with a graceful fallback. gpt-4.1 is valid today, but hardcoding it is fragile if it's ever retired or an account lacks access.
Two small P0/P1 fixes — key redaction and prompt dedup — return the most value per minute.
Act IV · The verdict4.4🎬 Synthesis

Can you vouch for it?

For what it is, mostly yes. This is a single-purpose, AI-authored hobby extension that turns sentences into themes. The happy path is coherent, and the code is more disciplined than most things its size. But "vouch" has one hard blocker: the API-key logging. No extension should write a user's secret to a log. Not on an error path, not even locally. Fix that one line and the rest is polish: a prompt to dedupe, a few tests to write, some dead code to sweep.

The deeper lesson is the one the project set out to demonstrate. "Zero manual edits" produced something that compiles, ships, and works. That is a genuine result, and not a small one. It also produced exactly the failures an unsupervised loop produces. A secret in a log. A prompt paying rent on duplicated text. A feature documented but never wired. A test harness with no tests. The repository is the proof of concept and the cautionary tale at once. Even this review, itself AI-run, needed a human to catch where its own reviewers over-reached. The machine gets you most of the way. The last mile is still the one that matters.