🏗️ Arquitetura em 4 Camadas
Layer 1: utils/theme.ts -- Semantic Color Palette
6 temas nomeados, cada um mapeando ~70 tokens semanticos para valores raw (RGB, hex ou ANSI)
Layer 2: ink/colorize.ts -- Chalk Normalization
Detecta ambiente terminal no module load, boost ou clamp do nivel de cor do chalk
Layer 3: ink/styles.ts -- Layout + Style Types
Tipos TypeScript Styles e TextStyles que componentes Ink Box/Text aceitam
Layer 4: design-system/color.ts -- Theme-Aware Colorizer
Helper curried que aceita theme key ou raw color, resolve e delega para colorize()
🌈 Os Seis Temas
💡 Daltonized: Azul em vez de Verde
Temas daltonized substituem sistematicamente distincoes verde-vermelho por azul-vermelho. Deuteranopia (forma mais comum de daltonismo) afeta o canal verde. success em dark-daltonized e azul brilhante, nao verde.
⚡ Terminal Environment Fixes
VS Code Boost
xterm.js suporta truecolor desde 2017, mas chalk detecta como level 2. rgb(215,119,87) (Claude orange) vira salmon lavado.
TERM_PROGRAM=vscode AND level=2 -> level=3tmux Clamp
tmux parseia truecolor corretamente mas re-emite apenas se outer terminal anunciar Tc/RGB. Sem isso, backgrounds sao dropados.
TMUX AND level > 2 -> level=2💡 Ordenacao importa
Boost roda primeiro. Se alguem esta no VS Code dentro do tmux, o boost acontece e o clamp re-clamp de volta para 2. O clamp vence pois limitacao do tmux e um hard constraint. Escape hatch: CLAUDE_CODE_TMUX_TRUECOLOR=1.
🎯 colorize() Dispatch Table
String-prefix dispatch significa que o formato de cor e auto-descritivo:
if (color.startsWith('ansi:')) -> chalk.red / chalk.bgRed
if (color.startsWith('#')) -> chalk.hex(color)
if (color.startsWith('ansi256')) -> chalk.ansi256(N)
if (color.startsWith('rgb')) -> chalk.rgb(r,g,b)
🔗 design-system/color.ts -- Theme Bridge
Unico local onde theme key strings sao resolvidas para raw color values. Retorna funcao curried, nao string:
export function color(
c: keyof Theme | Color | undefined,
theme: ThemeName,
type: ColorType = 'foreground',
): (text: string) => string {
return text => {
if (c.startsWith('rgb(') || c.startsWith('#') || ...)
return colorize(text, c, type) // raw bypass
return colorize(text, getTheme(theme)[c], type) // theme lookup
}
}
Separacao Arquitetural
styles.ts e colorize.ts sao completamente inconscientes de temas. Apenas design-system/color.ts faz a ponte de theme tokens para raw colors.
🎮 Comandos /theme e /color
/theme
Abre picker interativo com preview ao vivo. usePreviewTheme() seta tema temporario. Enter salva, Escape cancela e restaura.
/color
Para sessoes sub-agent. 8 cores: red, blue, green, yellow, purple, orange, pink, cyan. Proibido para teammates de swarm.
💡 Reset usa "default", nao string vazia
String vazia seria falsy e nao seria escrita por guard de truthiness em sessionStorage.ts. O sentinel "default" garante que o reset persiste entre restarts de sessao.
👥 AgentColorManager
Mapeia agent type strings para slots de cor do tema via sufixo _FOR_SUBAGENTS_ONLY:
AGENT_COLOR_TO_THEME_COLOR = {
red: 'red_FOR_SUBAGENTS_ONLY',
blue: 'blue_FOR_SUBAGENTS_ONLY',
...
} as const satisfies Record<AgentColorName, keyof Theme>
O satisfies garante em compile-time que cada entrada aponta para uma chave valida do tipo Theme. Agent type general-purpose retorna undefined -- intencionalmente sem cor.
🗺️ Fluxo Completo de Resolucao de Cores
flowchart TD
A[User selects theme via /theme] --> B[useTheme setTheme called]
B --> C[ThemeSetting saved to settings.json]
C --> D[ThemeName resolved at runtime\nauto -> dark or light]
D --> E[getTheme returns Theme object]
E --> F[Component calls color fn\nfrom design-system/color.ts]
F --> G{Is c a raw color?\nstarts with rgb/hash/ansi}
G -- Yes --> H[colorize directly]
G -- No --> I[getTheme key lookup\nraw value]
I --> H
H --> J{chalk.level}
J -- 3 truecolor --> K[chalk.rgb / chalk.hex]
J -- 2 256-color --> L[chalk.ansi256]
J -- 0/1 no color --> M[plain string]
K --> N[ANSI escape to terminal]
L --> N
M --> N