Color Profile
Overview
A named color palette defining foreground, background, cursor, selection, and 16 ANSI colors. Used for terminal emulators, code editors, or any component that needs switchable color themes. Includes a set of built-in presets (Solarized, Dracula, Nord, etc.) and supports user-created custom profiles.
Behavioral Requirements
Profile structure
- profile-structure: Each profile MUST have: a unique ID (UUID), a display name, an appearance preference (dark/light/auto), font name, font size, cursor style, a color palette, and a deletable flag.
- palette-format: The color palette MUST contain: foreground, background, cursor, and selection colors as
#rrggbbhex strings, plus an array of exactly 16 ANSI colors (indices 0–15: 8 normal + 8 bright).
Built-in profiles
-
built-in-profiles: The app MUST ship with at least these built-in profiles (non-deletable, non-editable):
Name Appearance Background Foreground Solarized Dark dark #002b36 #839496 Solarized Light light #fdf6e3 #657b83 Dracula dark #282a36 #f8f8f2 Nord dark #2e3440 #d8dee9 Tokyo Night dark #1a1b26 #a9b1d6 GitHub Light light #ffffff #24292e Gruvbox Dark dark #282828 #ebdbb2 Catppuccin Mocha dark #1e1e2e #cdd6f4 -
stable-builtin-uuids: Built-in profiles MUST have stable, fixed UUIDs so references survive app updates.
User profiles
- duplicate-profile: Users MUST be able to duplicate any profile to create a custom copy.
- editable-custom-profiles: Custom profiles MUST be editable: name, appearance, font size, cursor style.
- deletable-custom-only: Custom profiles MUST be deletable. Built-in profiles MUST NOT be deletable.
Active profile
- single-active-profile: Exactly one profile MUST be active at a time. The active profile ID MUST be persisted in user settings.
- fallback-to-default: If the stored active profile ID is invalid (deleted or not found), the app MUST fall back to the first built-in profile (Solarized Dark).
Appearance mode
- auto-appearance-mode: Profiles with
autoappearance MUST follow the system dark/light mode — using a dark profile when in dark mode and a light profile when in light mode. The specific dark/light mapping is a Design Decision.
Appearance
Profile list (in settings)
- Each row shows: circular color swatch (background color), profile name, appearance badge (D/L/A)
- Active profile is highlighted
- Built-in profiles grouped first, then custom profiles
Profile detail editor
- General: Name field (editable for custom, read-only for built-in), appearance picker
- Font: Font name (read-only), size stepper (range: 8–72pt, default: 13pt)
- Cursor: Style picker (block, underline, bar)
- Colors: Swatches for FG/BG/cursor/selection, 4×4 grid for 16 ANSI colors
- Preview: Sample terminal text with profile colors applied
States
| State | Behavior |
|---|---|
| No custom profiles | Only built-in profiles shown |
| Profile selected | Detail editor shows profile settings |
| Profile duplicated | New profile created with " Copy" suffix, auto-selected |
| Profile deleted | Selection falls back to previous or first built-in |
| Active profile changed | All consumers immediately update their colors |
Accessibility
- swatch-a11y-labels: Color swatches MUST have accessible labels describing the color (e.g., "Background: dark blue").
- keyboard-navigable-list: The profile list MUST be keyboard-navigable.
Conformance Test Vectors
| ID | Requirements | Input | Expected |
|---|---|---|---|
| profile-001 | built-in-profiles | Launch fresh app | 8 built-in profiles available |
| profile-002 | stable-builtin-uuids | Reference Solarized Dark by UUID across updates | UUID is stable |
| profile-003 | duplicate-profile | Duplicate Dracula | New "Dracula Copy" profile created, editable, deletable |
| profile-004 | deletable-custom-only | Attempt to delete Solarized Dark | Delete action disabled/hidden |
| profile-005 | fallback-to-default | Set active profile to invalid UUID | Falls back to Solarized Dark |
| profile-006 | auto-appearance-mode | Set profile to auto, switch system to dark mode | Dark-appropriate colors applied |
Edge Cases
- Many custom profiles: List SHOULD scroll; performance SHOULD remain smooth with 50+ profiles.
- Profile storage corruption: Fall back to built-in defaults.
- Hex color parsing:
#rrggbbandrrggbbformats MUST both be accepted. Invalid hex returns nil/default.
Configuration
This ingredient has no configurable options.
Logging
Subsystem: {{bundle_id}} | Category: ColorProfile
| Event | Level | Message |
|---|---|---|
| Active profile changed | debug | ColorProfile: active profile changed to "{{name}}" ({{id}}) |
| Profile duplicated | debug | ColorProfile: duplicated "{{source}}" as "{{new}}" |
| Profile deleted | debug | ColorProfile: deleted "{{name}}" ({{id}}) |
| Fallback to default | debug | ColorProfile: invalid active ID, falling back to Solarized Dark |
Platform Notes
- Swift:
struct ColorProfile: Codable, Identifiable, EquatablewithTerminalColorPalettesub-struct. Store profiles inUserDefaultsas JSON or in app's document package. UseNSColor(hex:)extension for parsing. Apply to SwiftTerm viainstallColors(). - Kotlin:
data class ColorProfilewith@Serializable. Store in SharedPreferences as JSON. Parse hex withColor(android.graphics.Color.parseColor("#rrggbb")). - TypeScript: Interface with hex string fields. Store in localStorage as JSON. Parse with CSS
colorproperty directly.
Design Decisions
None yet.