feat(yahweh): theme integration en banner + card + nakui-explorer consume themed

Iter 4 de integración nakui↔yahweh. Los widgets banner y card
ofrecen variants _themed(cx, ...) que leen Theme::global(cx).
Versiones sin theme preservadas para apps sin theme global.

yahweh-widget-card:
- Nueva dep yahweh-theme.
- pub fn card_themed(cx: &App) -> Div: card() + bg(theme.bg_panel).

yahweh-widget-banner:
- Nueva dep yahweh-theme.
- pub fn banner_themed(cx, kind, msg) -> Div: deriva (bg, fg) según
  kind + theme.is_dark. Info usa accent del theme; Success/Warning/
  Error usan hue fijo (verde/amber/rojo) + lightness flippeada.
- pub fn themed_colors(kind, theme) -> (Background, Hsla) helper.
- 3 tests nuevos del derivation.

nakui-explorer:
- Nueva dep yahweh-theme.
- main() instala Theme::install_default antes de open_window.
- render: 5 vars rgb() locales → theme slots (bg_app/fg_text/etc).
- card() → card_themed(cx). banner() → banner_themed(cx).
- Accents semánticos del log (seed azul, morphism verde) quedan
  locales: son señales del dominio, no chrome.

Tests: 112 → 115 (+3). Stack intacto.

Beneficio: cambiar de Theme refleja en nakui-explorer automático.
Próximo candidato: migrar MetaApp (paleta hardcoded de 6 colors).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Sergio
2026-05-10 10:11:29 +00:00
parent 1be7bf9b1e
commit 9632d8c4a7
8 changed files with 200 additions and 25 deletions
+30 -23
View File
@@ -31,13 +31,18 @@ use gpui::{
};
use nakui_core::event_log::{EventLog, LogEntry};
use yahweh_meta_runtime::{preview_value, short_hash, short_uuid};
use yahweh_widget_banner::{banner, Banner};
use yahweh_widget_card::card;
use yahweh_theme::Theme;
use yahweh_widget_banner::{banner_themed, Banner};
use yahweh_widget_card::card_themed;
const REFRESH_INTERVAL: Duration = Duration::from_secs(2);
fn main() {
Application::new().run(|cx: &mut App| {
// Theme global instalado al boot — los widgets themed lo
// requieren, y simplifica el chrome del app a una paleta
// consistente.
Theme::install_default(cx);
let bounds = Bounds::centered(None, gpui::size(px(900.), px(640.)), cx);
cx.open_window(
WindowOptions {
@@ -139,14 +144,17 @@ fn load_log(path: &std::path::Path) -> Result<Vec<LogEntry>, String> {
}
impl Render for Explorer {
fn render(&mut self, _w: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
let bg = rgb(0x14171c);
let card_bg = rgb(0x1d2128);
let text_dim = rgb(0x9ba1ad);
let text = rgb(0xe6e8ec);
fn render(&mut self, _w: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
// Colores cromáticos del chrome del app vienen del Theme
// global (instalado en main). Los acentos por kind (seed
// azul, morphism verde) siguen siendo locales: son señales
// semánticas del log, no del chrome.
let theme = Theme::global(cx).clone();
let bg = theme.bg_app.clone();
let text = theme.fg_text;
let text_dim = theme.fg_muted;
let accent_seed = rgb(0x88c0d0);
let accent_morphism = rgb(0xa3be8c);
let _ = card_bg;
let (seed_count, morphism_count, top_breakdown) = self.breakdown();
@@ -162,9 +170,9 @@ impl Render for Explorer {
let header = div()
.px(px(16.))
.py(px(12.))
.bg(card_bg)
.bg(theme.bg_panel.clone())
.border_b_1()
.border_color(rgb(0x2a2f38))
.border_color(theme.border)
.text_color(text)
.text_size(px(14.))
.child(header_text);
@@ -184,20 +192,21 @@ impl Render for Explorer {
div()
.px(px(16.))
.py(px(6.))
.bg(rgb(0x16191f))
.bg(theme.bg_panel_alt.clone())
.text_color(text_dim)
.text_size(px(11.))
.child(breakdown_line)
});
// Banner de error vía widget compartido yahweh-widget-banner.
// Padding extra (px 16/8) por convención del explorer; el
// default del widget es 12/6 — el override mantiene la
// visual del header.
let error_banner = self
.error
.as_ref()
.map(|e| banner(Banner::Error, e.clone()).px(px(16.)).py(px(8.)).text_size(px(12.)));
// Banner de error themed: deriva (bg, fg) del Theme actual
// según `Banner::Error` + `is_dark`. Padding extra (16/8)
// del header preservado via overrides del builder.
let error_banner = self.error.as_ref().map(|e| {
banner_themed(cx, Banner::Error, e.clone())
.px(px(16.))
.py(px(8.))
.text_size(px(12.))
});
// Renderea las últimas N entries (la timeline crece hacia abajo
// en append-order; mostramos las más recientes primero para
@@ -225,8 +234,7 @@ impl Render for Explorer {
.as_ref()
.map(|h| format!("schema={}", short_hash(h)))
.unwrap_or_else(|| "schema=(legacy)".into());
card()
.bg(card_bg)
card_themed(cx)
.border_l_4()
.border_color(accent_seed)
.child(
@@ -291,8 +299,7 @@ impl Render for Explorer {
.as_ref()
.map(|h| format!("schema={}", short_hash(h)))
.unwrap_or_else(|| "schema=(legacy)".into());
card()
.bg(card_bg)
card_themed(cx)
.border_l_4()
.border_color(accent_morphism)
.child(