feat(yahweh-widget-theme-switcher): control para ciclar themes en runtime

Iter 6. Cierra el ciclo del theme: ya teníamos paleta themed +
widgets que la consumen, faltaba el control UI para rotar entre
presets en vivo. Ahora hay un botón yahweh que muestra el theme
actual y al click avanza al siguiente.

crates/modules/ui_engine/widgets/theme-switcher/:
- pub fn theme_switcher(cx: &mut App) -> impl IntoElement: botón
  clickable con bg=panel_alt, hover=row_hover, label "Tema: <name> ▸".
  Al click hace Theme::set(cx, Theme::next_after(current.name)).
- 2 tests #[gpui::test]: smoke + verificación de cambio de global.
- Dev-dep gpui con test-support.

Migración:
- nakui-explorer: header pasa a flex_row con label flex_grow + switcher
  alineado derecha.
- yahweh-widget-meta-form (sidebar): mismo pattern en el header
  "Nakui" del sidebar.

Tests stack: 115 → 117.

Beneficio: click cambia toda la paleta en vivo. 6 presets disponibles
(Nebula, Aurora, Sunset, Flat Dark, Solarized Light, High Contrast)
ciclables circularmente.

Limitación: TextInput entities tienen colors hardcoded; migrar
text_input al theme es iter futura.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-05-10 10:37:49 +00:00
parent 5885457628
commit 8fdef818cc
9 changed files with 188 additions and 2 deletions
+54
View File
@@ -6,6 +6,60 @@ ratio/diff ver `git show <sha>`.
## 2026-05-10
### feat(yahweh-widget-theme-switcher): control para ciclar themes en runtime
Iter 6. Cierra el ciclo del theme: ya teníamos paleta themed +
widgets que la consumen, faltaba el control UI para rotar entre
presets en vivo. Ahora hay un botón yahweh que muestra el theme
actual y al click avanza al siguiente. `nakui-ui` y `nakui-explorer`
lo incrustan en sus headers — un click cambia toda la paleta.
Crate nuevo: `crates/modules/ui_engine/widgets/theme-switcher/`
(`yahweh-widget-theme-switcher`):
- **Deps**: `gpui` + `yahweh-theme`. Sin nada más.
- **`pub fn theme_switcher(cx: &mut App) -> impl IntoElement`**:
botón clickable con `id`, padding consistente (`px(8/4)`),
bg = `theme.bg_panel_alt`, hover = `bg_row_hover`. Muestra
`"Tema: <name> ▸"` y al click hace
`Theme::set(cx, Theme::next_after(current.name))`.
- 2 tests `#[gpui::test]`:
- `switcher_constructs_with_theme_installed` — smoke: el
constructor lee el global y devuelve un IntoElement sin panic.
- `theme_set_changes_global` — verifica que `Theme::set` reemplaza
el global y que el siguiente `Theme::global` devuelve el nuevo.
- Dev-dep `gpui` con `test-support` para habilitar TestAppContext.
Migración de consumers:
- **`nakui-explorer`**: nueva dep `yahweh-widget-theme-switcher`.
El header pasa de `div().px().py()...child(text)` a
`div().flex_row().child(div().flex_grow().child(text)).child(theme_switcher(cx))`.
El switcher queda alineado a la derecha vía `flex_grow` del label.
- **`yahweh-widget-meta-form`**: nueva dep. El sidebar header
("Nakui" + 12px padding) gana el switcher con el mismo patrón
flex_row + flex_grow.
Tests stack: 115 → **117** (+2 del switcher). Cada crate compila
individualmente.
Beneficio operativo:
- Click en el switcher cambia toda la paleta en vivo: bg del app,
panels, banners (los que usan `_themed`), confirm modal, todo.
- 6 presets disponibles via `Theme::all()` (Nebula, Aurora,
Sunset, Flat Dark, Solarized Light, High Contrast). El switcher
cicla circularmente.
- Apps adoptantes del `Theme` heredan el switch sin esfuerzo.
Decisión técnica: el handler usa `Theme::set(cx, ...)` que
invalida el global. GPUI marca todos los views como dirty y
re-renderea — los widgets que leen `Theme::global` en su `render`
ven el nuevo automáticamente. No requiere `cx.observe_global`
explícito en cada widget consumidor.
Limitación: TextInput entities ya creadas no se actualizan visualmente
si el theme cambia los colors del input bg/border (esos colors
están hardcoded en `yahweh-widget-text-input`). Migrar text_input
al theme es una iter futura — bajo scope porque actualmente vive
suficientemente bien con sus defaults dark.
### feat(yahweh-widget-meta-form): paleta del chrome migrada a `Theme::global(cx)`
Iter 5 de integración. El `MetaApp::render` tenía 7 vars locales
con colors hardcoded (`bg/panel/border/text/text_dim/accent/