Skip to content

feat(tui): elegir tema auto/oscuro/claro con la tecla t - #40

Open
Nxssie wants to merge 4 commits into
mainfrom
feat/light-mode
Open

Nxssie wants to merge 4 commits into
mainfrom
feat/light-mode

Conversation

@Nxssie

@Nxssie Nxssie commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

El modo claro ya estaba casi entero —casi todos los tokens de la paleta son parejas AdaptiveColor— pero no había forma de pedirlo: el lado lo elige termenv preguntándole al terminal, y cuando no puede (SSH, algunos multiplexores) cae a oscuro sin decir nada. No había override, ni un sitio donde ver en qué modo estás.

Ahora t cicla auto → oscuro → claro, se aplica en el frame siguiente sin reiniciar y se recuerda entre ejecuciones. auto es exactamente el comportamiento de antes: termenv decide, con la misma caída a oscuro.

  • La paleta se muda a internal/tui/theme.go, con los mismos valores y el papel de cada token anotado al lado. modelColors pasa a AdaptiveColor y renderBar recibe lipgloss.TerminalColor, que es lo que le deja seguir pintando igual desde el fichero nuevo. Es un movimiento, no un rediseño: los violetas de marca y los píxeles de la mascota no se tocan.
  • El ajuste vive en ~/.config/nan/settings.json ({"theme":"auto"}), con lectura-modificación-escritura que preserva las claves que no son nuestras: el fichero es compartido y no lo escribimos solo nosotros. Ausente, corrupto o con un modo desconocido → auto. La lectura usa json.Decoder con UseNumber para no reescribir números ajenos cambiándoles el tipo, y rechaza datos sobrantes al final del fichero.
  • Se ve y se cambia desde dentro: fila Theme en la pestaña About (dice además a qué se resolvió auto), entrada en la ayuda y pista en el pie en terminales anchos. Si guardar falla, el tema se aplica igual en esa ejecución; el aviso al usuario queda como follow-up.
  • No hay pestaña Settings, ni flag, ni subcomando nan theme: la decisión era la tecla global y la fila en About.
Fichero Cambio
internal/tui/theme.go Nuevo: paleta extraída + modo, ciclo, aplicar, leer y guardar el ajuste
internal/tui/theme_test.go Nuevo: 13 tests del ajuste, el ciclo, el renderer y el contrato de repintado
internal/tui/tui.go Fuera el bloque de paleta; tecla t, fila Theme, ayuda, pista del pie, y Run() aplica el modo antes del primer frame
internal/tui/config_test.go Adaptado a las firmas nuevas (newModel y renderAbout)
internal/session/session.go dir() pasa a Dir() para que el ajuste caiga en el mismo directorio de config que la sesión
.gitignore /.atl/, estado local de las herramientas, anclado a la raíz

Pruebas

  • go build ./..., go vet ./..., gofmt -l internal/ cmd/ (sin salida) y go test ./... -count=1: todo en verde. Es lo mismo que corre CI, más el cross-compile de los seis targets.
  • La suite nueva cubre: ida y vuelta del ajuste, claves ajenas preservadas, fichero ausente/corrupto/modo desconocido → auto, negativa a guardar sobre un fichero que no parsea, orden del ciclo, y que auto es el único camino que no llama a SetHasDarkBackground (comprobado por grep además de por test).
  • El contrato que importa —pulsar t repinta el panel— se comprueba de extremo a extremo: perfil truecolor fijado y fondo oscuro contra claro, afirmando sobre los bytes pintados y no sobre el flag del renderer. Los tests que tocan ese flag global lo restauran en t.Cleanup, así que no dependen del orden de ejecución.
  • La tecla t está guardada como las demás globales (no se dispara con la ayuda abierta ni dentro del wizard).
  • Rebasado sobre main actual (d929e5f, ya con fix(codex): los perfiles sin las herramientas que el cluster no streamea, y precios nuevos en Costs #35–chore: 0.1.23 #39 dentro) sin conflictos y vuelto a verificar después: los símbolos del tema y las cinco zonas que upstream también tocó (Update, renderAbout, renderSetup, renderHelp, Run) quedan enteras en los dos sentidos, sin funciones duplicadas ni restos del merge.

No verificado

  • El resultado visual en un terminal real (detección por SSH o multiplexor, mascota y banner sobre fondo blanco) necesita un terminal interactivo. La suite no lo cubre; queda pendiente de comprobación manual.

Nxssie added 4 commits October 7, 2026 13:27
Pure move of the palette var block (8 AdaptiveColor tokens plus
modelColors) out of tui.go, with a comment mapping each token to its
role. No value changes; tui.go keeps its lipgloss import.
The palette was already AdaptiveColor pairs, but which side renders was
decided solely by termenv background detection, which silently falls
back to dark when the terminal cannot be asked. Members on light
terminals over SSH or multiplexers had no override.

- ThemeMode (auto/dark/light) persisted in ~/.config/nan/settings.json,
  read-modify-write so foreign keys survive and an unparseable file is
  never flattened; missing/corrupt/unknown loads as auto.
- applyTheme pins lipgloss's renderer background flag; auto stays the
  documented no-op so detection keeps its current behavior.
- t cycles the mode from any tab, applies it on the next frame and
  persists it; Run() applies the saved mode before the first frame.
- About gained an Appearance row naming what auto resolved to; help and
  the wide footer hint mention the key.
- modelColors became AdaptiveColor pairs with light-tuned values so the
  five categorical colors stay distinguishable on white.
Findings from the pre-PR review (risk/resilience/readability/reliability
over a8057e0..d134a9b); none blocked, these are the ones worth fixing
now:

- reliability CRITICAL: the core contract was never asserted end to
  end. New test renders the tab bar with the profile pinned to truecolor
  and the background pinned dark vs light, asserting the panel actually
  repaints — coverage no longer stops at the renderer flag.
- test hygiene: every test that mutates lipgloss's global background
  flag (and its color profile) now restores the previous value via
  t.Cleanup, removing hidden order coupling for -shuffle and future
  tests in the same binary.
- coverage: the third press of the cycle must persist "auto" to the
  file (raw bytes, not loadThemeFrom, which maps unknowns back to auto);
  saveThemeTo normalizes unknown modes to auto; a refused save still
  applies the mode in memory.
- risk: settings.json is shared, so its read side now decodes with
  json.Number instead of float64 — a foreign integer past 2^53 survives
  the round-trip byte-faithfully, with readJSONConfig's refusal
  semantics (missing/empty tolerated, unparseable and trailing data
  never rewritten).
- readability: the config-dir rule is exported once as session.Dir and
  reused by settingsPath, so the theme file cannot drift away from
  session.json.

Deferred as follow-ups: user-visible notice when the theme save fails
(deliberate swallow, documented), cwd-relative fallback semantics,
folding the remaining fixed colors into theme.go, hue-name tokens.
The `.atl/` directory is local tooling state (skill registry output), not
shipped content. Anchored to the repo root so it does not silently swallow a
same-named path at any depth.
@Nxssie
Nxssie requested a review from borjaperfra October 7, 2026 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants