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>
This commit is contained in:
Sergio
2026-05-10 10:02:23 +00:00
parent 715cf6be03
commit 1be7bf9b1e
7 changed files with 133 additions and 16 deletions
+1
View File
@@ -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"] }
+3 -16
View File
@@ -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()
@@ -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 }
@@ -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();
}
}