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:
@@ -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
|
||||
|
||||
Generated
+8
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"] }
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user