tawasuyu

tawasuyu/brahman

Rust 582TOML 222Markdown 73JSON 198630 Sutichaykuna

feat(yahweh-widget-card): container card-shape compartido para timeline entries

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) <noreply@anthropic.com>

1be7bf9b1e381ac503609b4d77d385fee6c31ca0 · ruwaq Sergio · (5 killa ñawpaq) · tayta 715cf6be0 · kay commitpi sach'ata qhaway

Semántico sayk'uy · 4

crates/apps/nakui-explorer/src/main.rs

  • ukhunpi t'ikrakurqan fn render

crates/modules/ui_engine/widgets/card/src/lib.rs

7 qillqakuna · +133 −16

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 <sha>`.
 
 ## 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();
+    }
+}