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
|
## 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
|
### feat(yahweh-widget-banner): widget compartido para toasts/errores cross-app
|
||||||
Patrón visual común a `yahweh-widget-meta-form` (toast success +
|
Patrón visual común a `yahweh-widget-meta-form` (toast success +
|
||||||
error_banner) y `nakui-explorer` (error_banner): un `div` con bg
|
error_banner) y `nakui-explorer` (error_banner): un `div` con bg
|
||||||
|
|||||||
Generated
+8
@@ -6461,6 +6461,7 @@ dependencies = [
|
|||||||
"uuid",
|
"uuid",
|
||||||
"yahweh-meta-runtime",
|
"yahweh-meta-runtime",
|
||||||
"yahweh-widget-banner",
|
"yahweh-widget-banner",
|
||||||
|
"yahweh-widget-card",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -13006,6 +13007,13 @@ dependencies = [
|
|||||||
"gpui",
|
"gpui",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "yahweh-widget-card"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"gpui",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yahweh-widget-container-core"
|
name = "yahweh-widget-container-core"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ members = [
|
|||||||
"crates/modules/ui_engine/widgets/text_input",
|
"crates/modules/ui_engine/widgets/text_input",
|
||||||
"crates/modules/ui_engine/widgets/meta-form",
|
"crates/modules/ui_engine/widgets/meta-form",
|
||||||
"crates/modules/ui_engine/widgets/banner",
|
"crates/modules/ui_engine/widgets/banner",
|
||||||
|
"crates/modules/ui_engine/widgets/card",
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# modules/nakui/ — ERP matemático (nakui absorbido)
|
# 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" }
|
nakui-core = { path = "../../modules/nakui/core" }
|
||||||
yahweh-meta-runtime = { path = "../../modules/ui_engine/libs/meta-runtime" }
|
yahweh-meta-runtime = { path = "../../modules/ui_engine/libs/meta-runtime" }
|
||||||
yahweh-widget-banner = { path = "../../modules/ui_engine/widgets/banner" }
|
yahweh-widget-banner = { path = "../../modules/ui_engine/widgets/banner" }
|
||||||
|
yahweh-widget-card = { path = "../../modules/ui_engine/widgets/card" }
|
||||||
gpui = { workspace = true }
|
gpui = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
uuid = { workspace = true, features = ["serde"] }
|
uuid = { workspace = true, features = ["serde"] }
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ use gpui::{
|
|||||||
use nakui_core::event_log::{EventLog, LogEntry};
|
use nakui_core::event_log::{EventLog, LogEntry};
|
||||||
use yahweh_meta_runtime::{preview_value, short_hash, short_uuid};
|
use yahweh_meta_runtime::{preview_value, short_hash, short_uuid};
|
||||||
use yahweh_widget_banner::{banner, Banner};
|
use yahweh_widget_banner::{banner, Banner};
|
||||||
|
use yahweh_widget_card::card;
|
||||||
|
|
||||||
const REFRESH_INTERVAL: Duration = Duration::from_secs(2);
|
const REFRESH_INTERVAL: Duration = Duration::from_secs(2);
|
||||||
|
|
||||||
@@ -224,17 +225,10 @@ impl Render for Explorer {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|h| format!("schema={}", short_hash(h)))
|
.map(|h| format!("schema={}", short_hash(h)))
|
||||||
.unwrap_or_else(|| "schema=(legacy)".into());
|
.unwrap_or_else(|| "schema=(legacy)".into());
|
||||||
div()
|
card()
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.px(px(12.))
|
|
||||||
.py(px(8.))
|
|
||||||
.mb(px(4.))
|
|
||||||
.bg(card_bg)
|
.bg(card_bg)
|
||||||
.rounded(px(4.))
|
|
||||||
.border_l_4()
|
.border_l_4()
|
||||||
.border_color(accent_seed)
|
.border_color(accent_seed)
|
||||||
.gap(px(2.))
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.flex()
|
||||||
@@ -297,17 +291,10 @@ impl Render for Explorer {
|
|||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|h| format!("schema={}", short_hash(h)))
|
.map(|h| format!("schema={}", short_hash(h)))
|
||||||
.unwrap_or_else(|| "schema=(legacy)".into());
|
.unwrap_or_else(|| "schema=(legacy)".into());
|
||||||
div()
|
card()
|
||||||
.flex()
|
|
||||||
.flex_col()
|
|
||||||
.px(px(12.))
|
|
||||||
.py(px(8.))
|
|
||||||
.mb(px(4.))
|
|
||||||
.bg(card_bg)
|
.bg(card_bg)
|
||||||
.rounded(px(4.))
|
|
||||||
.border_l_4()
|
.border_l_4()
|
||||||
.border_color(accent_morphism)
|
.border_color(accent_morphism)
|
||||||
.gap(px(2.))
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.flex()
|
.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