From 1be7bf9b1e381ac503609b4d77d385fee6c31ca0 Mon Sep 17 00:00:00 2001 From: Sergio Date: Sun, 10 May 2026 10:02:23 +0000 Subject: [PATCH] feat(yahweh-widget-card): container card-shape compartido para timeline entries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Iter 3 de integración nakui↔yahweh. El card visual pattern (padding consistente + rounded + flex_col + gap) que vivía duplicado en cada timeline entry de nakui-explorer ahora es un widget yahweh reusable. crates/modules/ui_engine/widgets/card/: - pub fn card() -> Div: flex_col + px(12) + py(8) + mb(4) + rounded(4) + gap(2). Sin colores (caller decide via builder). - 1 test smoke. nakui-explorer: - Los 2 timeline entry patterns (Seed/Morphism) pasan de ~7 calls a ~3, intención "card with accent" emerge del nombre. Tests stack: 111 → 112. App-agnostic — el widget no impone paleta, permite themes diversos. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 44 ++++++++++++ Cargo.lock | 8 +++ Cargo.toml | 1 + crates/apps/nakui-explorer/Cargo.toml | 1 + crates/apps/nakui-explorer/src/main.rs | 19 +----- .../modules/ui_engine/widgets/card/Cargo.toml | 9 +++ .../modules/ui_engine/widgets/card/src/lib.rs | 67 +++++++++++++++++++ 7 files changed, 133 insertions(+), 16 deletions(-) create mode 100644 crates/modules/ui_engine/widgets/card/Cargo.toml create mode 100644 crates/modules/ui_engine/widgets/card/src/lib.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index d141805..bf095a2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,50 @@ ratio/diff ver `git show `. ## 2026-05-10 +### feat(yahweh-widget-card): container card-shape compartido para timeline entries +Iteración 3 de la integración nakui ↔ yahweh. El "card visual" +pattern (padding consistente + rounded + flex_col + gap) que vivía +duplicado en cada timeline entry de `nakui-explorer` ahora es un +widget yahweh reusable. Sin acoplamiento a colores: el caller +decide bg/border/accent. + +Crate nuevo: `crates/modules/ui_engine/widgets/card/` +(`yahweh-widget-card`): +- **Dep**: solo `gpui`. App-agnostic. +- **`pub fn card() -> Div`**: container con `flex_col` + `px(12)` + + `py(8)` + `mb(4)` + `rounded(4)` + `gap(2)`. Sin colores + aplicados. +- El return es `Div` GPUI — el caller compone con `.bg(...)`, + `.border_l_4()`, `.border_color(...)`, `.child(...)`, hover, + on_click, etc., según necesite. +- 1 test smoke (constructor no panicea). + +Migración de `nakui-explorer`: +- Nueva dep `yahweh-widget-card`. +- Los 2 patterns de timeline entry (Seed y Morphism) pasan de: + ```rust + div().flex().flex_col().px(12).py(8).mb(4).bg(card_bg) + .rounded(4).border_l_4().border_color(accent).gap(2)... + ``` + a: + ```rust + card().bg(card_bg).border_l_4().border_color(accent)... + ``` +- Reducción ~7 calls → ~3 por entry; legibilidad mejor (la + intención "card with accent" emerge del nombre `card()`). + +Tests stack: 111 → **112 verdes** (+1 del crate card). Cada crate +afectado compila y testea individualmente. + +Beneficio operativo: +- Si `MetaApp` o cualquier futura app necesita un container + card-shape (ej. info card, expanded list row), `card()` está + ya disponible. +- Cambiar el padding/rounded/gap canónico = un cambio en un solo + lugar. +- El widget no impone colores → no fuerza una paleta y permite + themes diversos por app/contexto. + ### feat(yahweh-widget-banner): widget compartido para toasts/errores cross-app Patrón visual común a `yahweh-widget-meta-form` (toast success + error_banner) y `nakui-explorer` (error_banner): un `div` con bg diff --git a/Cargo.lock b/Cargo.lock index 797eb7c..e1a9803 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6461,6 +6461,7 @@ dependencies = [ "uuid", "yahweh-meta-runtime", "yahweh-widget-banner", + "yahweh-widget-card", ] [[package]] @@ -13006,6 +13007,13 @@ dependencies = [ "gpui", ] +[[package]] +name = "yahweh-widget-card" +version = "0.1.0" +dependencies = [ + "gpui", +] + [[package]] name = "yahweh-widget-container-core" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 6367f2d..4c0a9f1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ members = [ "crates/modules/ui_engine/widgets/text_input", "crates/modules/ui_engine/widgets/meta-form", "crates/modules/ui_engine/widgets/banner", + "crates/modules/ui_engine/widgets/card", # ============================================================ # modules/nakui/ — ERP matemático (nakui absorbido) diff --git a/crates/apps/nakui-explorer/Cargo.toml b/crates/apps/nakui-explorer/Cargo.toml index 046a982..e2b394c 100644 --- a/crates/apps/nakui-explorer/Cargo.toml +++ b/crates/apps/nakui-explorer/Cargo.toml @@ -9,6 +9,7 @@ description = "Explorador GPUI del event log de Nakui: timeline de seeds + morph nakui-core = { path = "../../modules/nakui/core" } yahweh-meta-runtime = { path = "../../modules/ui_engine/libs/meta-runtime" } yahweh-widget-banner = { path = "../../modules/ui_engine/widgets/banner" } +yahweh-widget-card = { path = "../../modules/ui_engine/widgets/card" } gpui = { workspace = true } serde_json = { workspace = true } uuid = { workspace = true, features = ["serde"] } diff --git a/crates/apps/nakui-explorer/src/main.rs b/crates/apps/nakui-explorer/src/main.rs index 51fa042..cfc5fe2 100644 --- a/crates/apps/nakui-explorer/src/main.rs +++ b/crates/apps/nakui-explorer/src/main.rs @@ -32,6 +32,7 @@ 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; const REFRESH_INTERVAL: Duration = Duration::from_secs(2); @@ -224,17 +225,10 @@ impl Render for Explorer { .as_ref() .map(|h| format!("schema={}", short_hash(h))) .unwrap_or_else(|| "schema=(legacy)".into()); - div() - .flex() - .flex_col() - .px(px(12.)) - .py(px(8.)) - .mb(px(4.)) + card() .bg(card_bg) - .rounded(px(4.)) .border_l_4() .border_color(accent_seed) - .gap(px(2.)) .child( div() .flex() @@ -297,17 +291,10 @@ impl Render for Explorer { .as_ref() .map(|h| format!("schema={}", short_hash(h))) .unwrap_or_else(|| "schema=(legacy)".into()); - div() - .flex() - .flex_col() - .px(px(12.)) - .py(px(8.)) - .mb(px(4.)) + card() .bg(card_bg) - .rounded(px(4.)) .border_l_4() .border_color(accent_morphism) - .gap(px(2.)) .child( div() .flex() diff --git a/crates/modules/ui_engine/widgets/card/Cargo.toml b/crates/modules/ui_engine/widgets/card/Cargo.toml new file mode 100644 index 0000000..e12afc8 --- /dev/null +++ b/crates/modules/ui_engine/widgets/card/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "yahweh-widget-card" +version.workspace = true +edition.workspace = true +license.workspace = true +description = "Yahweh — widget card: container con padding + rounded + flex_col consistentes para timeline entries, list rows expandidas, info cards. Agnóstico del color (caller decide bg/border)." + +[dependencies] +gpui = { workspace = true } diff --git a/crates/modules/ui_engine/widgets/card/src/lib.rs b/crates/modules/ui_engine/widgets/card/src/lib.rs new file mode 100644 index 0000000..6928ed7 --- /dev/null +++ b/crates/modules/ui_engine/widgets/card/src/lib.rs @@ -0,0 +1,67 @@ +//! `yahweh-widget-card` — container card-shape para entries de +//! timeline, info cards y similares. +//! +//! Aporta la **forma**: padding consistente (12/8), `rounded(4)`, +//! `flex_col` con `gap(2)`. NO aporta colores — el caller decide +//! `bg`, `border_color`, etc. via builder calls. Esto permite que +//! distintos consumers (timeline con accent por kind, info card +//! con bg uniforme) compartan la misma proporción visual sin +//! acoplarse a una paleta fija. +//! +//! # Ejemplo +//! +//! ```ignore +//! use yahweh_widget_card::card; +//! use gpui::{rgb, prelude::*, px}; +//! +//! // Card con accent border-l (típico timeline entry): +//! let entry = card() +//! .bg(rgb(0x1d2128)) +//! .border_l_4() +//! .border_color(rgb(0x88c0d0)) +//! .child(div().child("header")) +//! .child(div().child("body")); +//! +//! // Card sin border (info card uniforme): +//! let info = card() +//! .bg(rgb(0x1d2128)) +//! .child("contenido"); +//! ``` + +#![forbid(unsafe_code)] + +use gpui::{div, prelude::*, px, Div}; + +/// Container card-shape: `flex_col` con padding `12/8`, `rounded(4)`, +/// `gap(2)` interno entre children y `mb(4)` para separación +/// vertical de cards apiladas. +/// +/// Sin colores aplicados — el caller agrega `.bg(...)`, +/// `.border_color(...)`, `.border_l_4()`, etc. según necesite. +/// +/// El return es un `Div` GPUI — todas las builder methods de div +/// están disponibles (children, hover, on_click, ids, etc.). +pub fn card() -> Div { + div() + .flex() + .flex_col() + .px(px(12.)) + .py(px(8.)) + .mb(px(4.)) + .rounded(px(4.)) + .gap(px(2.)) +} + +#[cfg(test)] +mod tests { + use super::*; + + /// Sanity smoke: el constructor devuelve un Div sin panic. No + /// podemos asertar las property de styling sin renderear (que + /// requiere TestAppContext + window). Si la signature cambia, + /// el código no compila — eso es la real garantía. + #[test] + fn card_returns_div_without_panic() { + let _d = card(); + } +}