From 58854576280421e6660104946117f5fea9d5c7b5 Mon Sep 17 00:00:00 2001 From: Sergio Date: Sun, 10 May 2026 10:20:08 +0000 Subject: [PATCH] feat(yahweh-widget-meta-form): paleta del chrome migrada a Theme::global(cx) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Iter 5 de integración. MetaApp::render tenía 7 vars con colors hardcoded (bg/panel/border/text/text_dim/accent/accent_active); ahora salen de Theme::global(cx) que el shell instala al boot. confirm_delete_banner usa themed_colors(Warning/Error) para sus colors base. render: - 7 let X = rgb(0x...) → theme.bg_app/bg_panel/border/fg_text/ fg_muted/accent/accent_strong. - toast_div / error_banner: banner() → banner_themed(cx). Firmas internas: - render_sidebar/main/list/entity_ref_selector/form cambian Rgba → Hsla (Background donde aplica para panel). gpui::Div acepta ambos via Into, uso interno no cambia. confirm_delete_banner: - 6 colors hardcoded → themed_colors(Warning) para banner base, themed_colors(Error) para Confirm, theme.bg_panel_alt+fg_text para Cancel. NO migra esta iter (ornament hardcoded para una pasada futura): row hovers, bordes sutiles entre filas, bg de inputs custom, bg de botones del EntityRef selector, color del icon ✕ de delete. Smoke test del binario verificado: bootstrap completo OK, panic esperado en open_window sin display. 115 tests verdes (sin cambio: los tests del widget no acceden al render). Beneficio: theme switcher (cuando llegue) cambia toda la paleta con 1 sola llamada Theme::set(cx, ...). Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 66 ++++++++++++++ .../ui_engine/widgets/meta-form/src/lib.rs | 89 +++++++++++-------- 2 files changed, 117 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2fc90ff..c91679a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,72 @@ ratio/diff ver `git show `. ## 2026-05-10 +### 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/ +accent_active`) que se pasaban a las funciones internas +(`render_sidebar`/`render_main`/`render_list`/`render_form`/ +`render_entity_ref_selector`). Ahora salen del `Theme::global(cx)` +que el binario shell instala al boot. El `confirm_delete_banner` +también usa `themed_colors(Banner::Warning)` / `themed_colors(Banner::Error)` +para sus colors base. + +Cambios en `MetaApp::render`: +- 7 `let X = gpui::rgb(0x...)` → derive del theme: + - `bg` ← `theme.bg_app` (Background, soporta gradientes). + - `panel` ← `theme.bg_panel`. + - `border` ← `theme.border` (Hsla). + - `text` ← `theme.fg_text`. + - `text_dim` ← `theme.fg_muted`. + - `accent` ← `theme.accent`. + - `accent_active` ← `theme.accent_strong`. +- `toast_div` y `error_banner`: `banner(...)` → `banner_themed(cx, ...)`. + +Cambios de firma (internas, no API público): +- `render_sidebar` / `render_main` / `render_list` / + `render_entity_ref_selector` / `render_form` cambian Rgba → + Hsla en sus parámetros de color (Background donde aplica para + `panel`). Los métodos `bg/text_color/border_color` de gpui::Div + aceptan ambos via `Into`, así que el uso interno no cambia. + +Cambios en `render_confirm_delete_banner`: +- 6 colors hardcoded amber/red/gray → `themed_colors(Warning)` para + banner base, `themed_colors(Error)` para botón Confirm, + `theme.bg_panel_alt + fg_text` para botón Cancel. +- Cambiar de Theme ahora cambia toda la paleta del modal. + +Lo que **NO** migra esta iter (queda como ornament hardcoded; iter +futura si emerge la necesidad): +- Row hovers misceláneos en `render_list` (px 0x232a36 / 0x1f2630 + para selected/hover de filas). +- Borders sutiles entre filas (px 0x232a36). +- Bg de inputs custom (px 0x171a20). +- Bg de botones en `render_entity_ref_selector` (px 0x2c3540). +- Color rojo del icon `✕` de delete (px 0xd07070) y su hover + (px 0x4a2020). + +Estos son detalles ornamentales que un theme switcher real +querría integrar; los aislo para una pasada futura cuando esté +claro qué slots semánticos del theme conviene agregar (ej. +`bg_row_selected` distinto de `bg_row_hover`, `accent_destructive`, +etc.). + +`nakui-ui` shell ya instalaba `Theme::install_default(cx)` desde la +iter pasada — sigue siendo el contract entre el shell y el widget. +Smoke test del binario verificado: bootstrap completo OK, panic +esperado en open_window sin display. + +Tests stack: 115 verdes (sin cambio — los tests del widget no +acceden al render). + +Beneficio operativo: +- El theme switcher (cuando llegue) cambia toda la paleta principal + de `MetaApp` con 1 sola llamada `Theme::set(cx, ...)`. +- `MetaApp` y `nakui-explorer` comparten el mismo theme global en + un mismo proceso (si llegan a vivir juntos). +- Los `confirm_delete_banner` y los toasts del MetaApp respetan + is_dark: el contrast ajusta automatic. + ### feat(yahweh): theme integration en `banner` + `card` + `nakui-explorer` consume themed Iter 4 de la integración. Los widgets `banner` y `card` ahora ofrecen variants `_themed(cx, ...)` que leen `Theme::global(cx)`. diff --git a/crates/modules/ui_engine/widgets/meta-form/src/lib.rs b/crates/modules/ui_engine/widgets/meta-form/src/lib.rs index 5f60583..1c8f2fa 100644 --- a/crates/modules/ui_engine/widgets/meta-form/src/lib.rs +++ b/crates/modules/ui_engine/widgets/meta-form/src/lib.rs @@ -34,7 +34,8 @@ use yahweh_meta_runtime::{ MetaBackend, WriteOutcome, }; use yahweh_meta_schema::{Action, FieldKind, FieldSpec, FormView, ListView, Module, View}; -use yahweh_widget_banner::{banner, Banner}; +use yahweh_theme::Theme; +use yahweh_widget_banner::{banner_themed, themed_colors, Banner}; use yahweh_widget_text_input::TextInput; /// Estado del runtime de UI. Toda la persistencia/ejecución está @@ -440,25 +441,34 @@ fn lookup_field<'a>(v: &'a Value, path: &str) -> Option<&'a Value> { impl Render for MetaApp { fn render(&mut self, _w: &mut Window, cx: &mut Context) -> impl IntoElement { - let bg = gpui::rgb(0x14171c); - let panel = gpui::rgb(0x1d2128); - let border = gpui::rgb(0x2a2f38); - let text = gpui::rgb(0xe6e8ec); - let text_dim = gpui::rgb(0x9ba1ad); - let accent = gpui::rgb(0x88c0d0); - let accent_active = gpui::rgb(0xa3be8c); + // Paleta del chrome viene del Theme global. Derivamos los + // 7 slots que las funciones render_* usan; las firmas + // siguen tomando los colors individuales (Hsla / Background) + // para preservar el shape del API interno. + // + // Si el caller no instaló un Theme, `Theme::global` panicea. + // Convención: el binario shell instala el theme en main. + let theme = Theme::global(cx).clone(); + let bg = theme.bg_app.clone(); + let panel = theme.bg_panel.clone(); + let border = theme.border; + let text = theme.fg_text; + let text_dim = theme.fg_muted; + let accent = theme.accent; + let accent_active = theme.accent_strong; - let sidebar = self.render_sidebar(cx, panel, border, text, text_dim, accent_active); + let sidebar = + self.render_sidebar(cx, panel.clone(), border, text, text_dim, accent_active); let main_panel = self.render_main(cx, panel, border, text, text_dim, accent); let confirm_banner = self.render_confirm_delete_banner(cx); let toast_div = self .toast .as_ref() - .map(|t| banner(Banner::Success, t.clone())); + .map(|t| banner_themed(cx, Banner::Success, t.clone())); let error_banner = self .load_error .as_ref() - .map(|e| banner(Banner::Error, e.clone())); + .map(|e| banner_themed(cx, Banner::Error, e.clone())); div() .flex() @@ -511,12 +521,15 @@ impl MetaApp { let id_short = short_uuid(&id_owned); let entity_for_confirm = entity_owned.clone(); - let banner_bg = gpui::rgb(0x4a3a1a); - let banner_text = gpui::rgb(0xf0e0a0); - let confirm_bg = gpui::rgb(0x6a2222); - let confirm_text = gpui::rgb(0xffd0d0); - let cancel_bg = gpui::rgb(0x2a2f38); - let cancel_text = gpui::rgb(0xc0c8d0); + // Banner base usa los colors themed del kind Warning (amber + // que sigue is_dark del theme actual). Los buttons confirm + // (rojo) y cancel (gris) usan los colors themed del Error + // y un derivative del border respectivamente. + let theme = Theme::global(cx); + let (banner_bg, banner_text) = themed_colors(Banner::Warning, theme); + let (confirm_bg, confirm_text) = themed_colors(Banner::Error, theme); + let cancel_bg: gpui::Background = theme.bg_panel_alt.clone(); + let cancel_text = theme.fg_text; Some( div() @@ -605,11 +618,11 @@ impl MetaApp { fn render_sidebar( &self, cx: &mut Context, - panel: gpui::Rgba, - border: gpui::Rgba, - text: gpui::Rgba, - text_dim: gpui::Rgba, - accent_active: gpui::Rgba, + panel: gpui::Background, + border: gpui::Hsla, + text: gpui::Hsla, + text_dim: gpui::Hsla, + accent_active: gpui::Hsla, ) -> gpui::Div { let mut sidebar = div() .w(px(240.)) @@ -694,11 +707,11 @@ impl MetaApp { fn render_main( &mut self, cx: &mut Context, - panel: gpui::Rgba, - border: gpui::Rgba, - text: gpui::Rgba, - text_dim: gpui::Rgba, - accent: gpui::Rgba, + panel: gpui::Background, + border: gpui::Hsla, + text: gpui::Hsla, + text_dim: gpui::Hsla, + accent: gpui::Hsla, ) -> gpui::Div { let main = div() .flex_grow() @@ -747,10 +760,10 @@ impl MetaApp { mut main: gpui::Div, lv: &ListView, mod_idx: usize, - border: gpui::Rgba, - text: gpui::Rgba, - text_dim: gpui::Rgba, - accent: gpui::Rgba, + border: gpui::Hsla, + text: gpui::Hsla, + text_dim: gpui::Hsla, + accent: gpui::Hsla, ) -> gpui::Div { let mut header = div() .flex() @@ -927,9 +940,9 @@ impl MetaApp { cx: &mut Context, field_name: String, target_entity: String, - text: gpui::Rgba, - text_dim: gpui::Rgba, - accent: gpui::Rgba, + text: gpui::Hsla, + text_dim: gpui::Hsla, + accent: gpui::Hsla, ) -> gpui::Div { let _ = text; let rows = self.list_rows(&target_entity); @@ -1004,10 +1017,10 @@ impl MetaApp { mut main: gpui::Div, fv: &FormView, mod_idx: usize, - _border: gpui::Rgba, - text: gpui::Rgba, - text_dim: gpui::Rgba, - accent: gpui::Rgba, + _border: gpui::Hsla, + text: gpui::Hsla, + text_dim: gpui::Hsla, + accent: gpui::Hsla, ) -> gpui::Div { // En modo edit, el título refleja eso para que el user no // se confunda creyendo que hace alta nueva.