Building tools. Learning to build tools. Learning to build learning tools.
Building a QuickPick with named presets, live swatches, and custom hue input.
VS Code’s showQuickPick is the standard way to present a list of choices
to the user. It’s the same widget behind the Command Palette, file switcher, and
symbol search. For Color Identity, it becomes a color picker with 14 named presets,
an “automatic” option, and a custom hue entry.
The function signature tells the story:
export async function showColorPicker(
currentHue: number,
themeProfile: ThemeProfile,
swatchDir: string
): Promise<{ hue: number | null } | undefined>
Three possible return values:
{ hue: 220 } — user picked a specific hue (preset or custom).{ hue: null } — user picked “Automatic” (use the hash-based hue).undefined — user dismissed the picker without choosing.export const COLOR_PRESETS: ColorPreset[] = [
{ label: 'Red', hue: 0 },
{ label: 'Orange', hue: 30 },
{ label: 'Yellow', hue: 55 },
{ label: 'Lime', hue: 80 },
{ label: 'Green', hue: 120 },
{ label: 'Mint', hue: 150 },
{ label: 'Teal', hue: 175 },
{ label: 'Cyan', hue: 190 },
{ label: 'Blue', hue: 220 },
{ label: 'Indigo', hue: 245 },
{ label: 'Purple', hue: 270 },
{ label: 'Magenta', hue: 300 },
{ label: 'Pink', hue: 330 },
{ label: 'Rose', hue: 350 },
];
Fourteen presets spaced around the color wheel. The spacing isn’t uniform — there are more entries in the warm range (Red through Yellow, 0–55°) and the cool range (Teal through Indigo, 175–245°) because those regions contain more perceptually distinct colors. The gap between Green (120°) and Mint (150°) is wider because that region of the wheel has less visual variety.
Each preset becomes a QuickPickItem with an icon, description, and optional
“currently selected” indicator:
for (const preset of COLOR_PRESETS) {
const previewHex = hslToHex(
preset.hue,
themeProfile.baseSaturation,
themeProfile.baseLightness
);
const swatchUri = generateColorSwatch(swatchDir, previewHex);
items.push({
label: preset.label,
description: `hue ${preset.hue}° · ${previewHex}`,
detail: currentHue === preset.hue
? '$(check) Currently selected' : undefined,
iconPath: swatchUri,
_hue: preset.hue,
});
}
Several things happening here:
generateColorSwatch creates a 16×16
solid-color PNG file and returns its URI. QuickPick items support iconPath
for displaying small images. We’ll see how these PNGs are generated in Section 7.
$(check) syntax is a VS Code
Codicon —
a built-in icon set. It renders as a checkmark in the detail line.
_hue is a custom property added via
a type intersection. It piggybacks the hue value on the QuickPick item so we can
retrieve it when the user makes a selection.
QuickPickItem supports label, description (shown
to the right of the label, muted), and detail (shown below the label, smaller).
The kind property can be set to QuickPickItemKind.Separator to
add visual dividers between groups of items. Color Identity uses separators to set apart
the “Automatic” option, the color presets, and the “Custom Hue” option.
If the user picks “Custom Hue…”, the flow chains into an input box:
if ((picked as PickItem)._action === 'custom') {
const input = await vscode.window.showInputBox({
title: 'Color Identity: Custom Hue',
prompt: 'Enter a hue value (0 = red, 120 = green, 240 = blue)',
value: String(currentHue),
validateInput: (value) => {
const n = Number(value);
if (isNaN(n) || n < 0 || n > 360) {
return 'Please enter a number between 0 and 360';
}
return undefined;
},
});
if (input === undefined) {
return undefined;
}
return { hue: Number(input) };
}
The validateInput callback runs on every keystroke and shows an inline error
message if the input is invalid. The user can’t submit an invalid value — the
OK button is disabled while the validation message is visible. The value field
pre-populates the input with the current hue, so the user can adjust from their current
position rather than starting from scratch.
Notice the flow: QuickPick → InputBox is a two-step interaction. The user first picks from a list, and only sees the text input if they explicitly choose the custom option. This keeps the common case (picking a preset) fast, while still supporting power users who want precise control.
The color picker is a complete UI component: 14 theme-aware presets with swatch icons, an automatic option, and a validated custom input. But where do those swatch PNG files come from? That’s the most surprising part of the whole extension.