feat: llimphi standalone — framework UI soberano extraído del monorepo

Motor gráfico Llimphi como workspace independiente: bucle Elm
(input→update→view→layout→raster→present) sobre wgpu+vello+taffy+parley.
Núcleo (hal/raster/layout/text/ui/theme/surface/motion/icons) + ~40 widgets
+ módulos, sin dependencias al resto del monorepo. cargo check --workspace
pasa (64 crates). Puerta de entrada: cargo run -p llimphi-ui --example counter.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-04 04:23:42 +00:00
commit e65e9cc623
286 changed files with 46136 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
[package]
name = "llimphi-widget-stat-card"
version.workspace = true
edition.workspace = true
license.workspace = true
authors.workspace = true
publish.workspace = true
description = "llimphi-widget-stat-card — tarjeta de dashboard con label chico + valor grande + descripción + accent vertical. Análogo Llimphi al `nahual-widget-stat-card` GPUI."
[dependencies]
llimphi-ui = { workspace = true }
llimphi-theme = { workspace = true }
llimphi-widget-card = { workspace = true }
+5
View File
@@ -0,0 +1,5 @@
# llimphi-widget-stat-card
> Card para métricas para [llimphi](../../README.md).
Label + valor grande + sub-label + sparkline opcional. Variante `compact` y `wide`. Usado por `cosmos-card`, `chasqui-card`, `arje-card`, etc.
+5
View File
@@ -0,0 +1,5 @@
# llimphi-widget-stat-card
> Card for metrics for [llimphi](../../README.md).
Label + large value + sub-label + optional sparkline. `compact` and `wide` variants. Used by `cosmos-card`, `chasqui-card`, `arje-card`, etc.
+144
View File
@@ -0,0 +1,144 @@
//! `llimphi-widget-stat-card` — tarjeta de dashboard con accent.
//!
//! Compone (sobre `llimphi-widget-card`):
//! - **Border-l-4** con un color de accent que el caller decide.
//! - **Label** chico arriba en el color del accent.
//! - **Value** grande (28 px) en el color principal del texto.
//! - **Description** chica en el color tenue.
//! - **Listing opcional** de items recientes con sub-header
//! `"recent (N):"`.
//!
//! Análogo Llimphi al `nahual-widget-stat-card` GPUI. Pensado para
//! dashboards estilo `minga-explorer`, `brahman-broker-explorer`.
#![forbid(unsafe_code)]
use llimphi_ui::llimphi_layout::taffy::{
prelude::{length, percent, Size, Style},
Rect,
};
use llimphi_ui::llimphi_raster::peniko::Color;
use llimphi_ui::llimphi_text::Alignment;
use llimphi_ui::View;
use llimphi_widget_card::{card_view, CardOptions, CardPalette};
/// Paleta del stat-card. `accent` se setea por instancia (verde/rojo/
/// ámbar etc.), los otros vienen del theme.
#[derive(Debug, Clone, Copy)]
pub struct StatCardPalette {
pub bg: Color,
pub fg_text: Color,
pub fg_muted: Color,
}
impl Default for StatCardPalette {
fn default() -> Self {
Self::from_theme(&llimphi_theme::Theme::dark())
}
}
impl StatCardPalette {
pub fn from_theme(t: &llimphi_theme::Theme) -> Self {
Self {
bg: t.bg_panel,
fg_text: t.fg_text,
fg_muted: t.fg_muted,
}
}
}
/// Compone un stat-card.
///
/// - `label`: header chico en color `accent`.
/// - `value`: texto principal grande.
/// - `description`: línea chica tenue debajo del value.
/// - `accent`: color del border-l + del label.
/// - `recent_items`: si no vacío, agrega "recent (N):" + una fila por
/// item.
pub fn stat_card_view<Msg: Clone + 'static>(
label: &str,
value: impl Into<String>,
description: &str,
accent: Color,
recent_items: &[String],
palette: &StatCardPalette,
) -> View<Msg> {
let label_row = View::new(Style {
size: Size {
width: percent(1.0_f32),
height: length(14.0_f32),
},
..Default::default()
})
.text_aligned(label.to_string(), 11.0, accent, Alignment::Start);
let value_row = View::new(Style {
size: Size {
width: percent(1.0_f32),
height: length(36.0_f32),
},
..Default::default()
})
.text_aligned(value.into(), 28.0, palette.fg_text, Alignment::Start);
let desc_row = View::new(Style {
size: Size {
width: percent(1.0_f32),
height: length(14.0_f32),
},
..Default::default()
})
.text_aligned(
description.to_string(),
11.0,
palette.fg_muted,
Alignment::Start,
);
let mut children: Vec<View<Msg>> = vec![label_row, value_row, desc_row];
if !recent_items.is_empty() {
children.push(
View::new(Style {
size: Size {
width: percent(1.0_f32),
height: length(16.0_f32),
},
padding: Rect {
left: length(0.0_f32),
right: length(0.0_f32),
top: length(6.0_f32),
bottom: length(0.0_f32),
},
..Default::default()
})
.text_aligned(
format!("recent ({}):", recent_items.len()),
10.0,
palette.fg_muted,
Alignment::Start,
),
);
for it in recent_items {
children.push(
View::new(Style {
size: Size {
width: percent(1.0_f32),
height: length(14.0_f32),
},
..Default::default()
})
.text_aligned(it.clone(), 11.0, palette.fg_text, Alignment::Start),
);
}
}
card_view(
children,
CardOptions {
accent: Some(accent),
..Default::default()
},
&CardPalette { bg: palette.bg },
)
}