From d5ef7144b5a1d2e6604505cb4dc08e7a703ecc72 Mon Sep 17 00:00:00 2001 From: Sergio Date: Sun, 10 May 2026 09:38:50 +0000 Subject: [PATCH] feat(yahweh-meta-runtime): promover short_hash y preview_value desde nakui-explorer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Continúa la integración de apps nakui al stack yahweh. Los helpers visuales que nakui-explorer tenía locales y son reusables suben a yahweh-meta-runtime/format. yahweh-meta-runtime: - short_hash(h: &[u8; 32]) -> String: hex de los primeros 4 bytes. - preview_value(v: &Value, max: usize) -> String: JSON one-liner truncado con "..." (edge case max < 3 sin panic). - 5 tests nuevos. nakui-explorer: - Nueva dep yahweh-meta-runtime. - Borrado helpers locales (short_uuid + short_hash + preview_value) + 4 tests duplicados. - Imports desde yahweh-meta-runtime. Tests: 42→47 yahweh-meta-runtime, 7→3 nakui-explorer (los 3 que quedan son específicos del explorer: load_log, breakdown, missing_file). Resto intacto. Co-Authored-By: Claude Opus 4.7 (1M context) --- CHANGELOG.md | 40 +++++++++++ crates/apps/nakui-explorer/Cargo.toml | 1 + crates/apps/nakui-explorer/src/main.rs | 66 ++---------------- .../ui_engine/libs/meta-runtime/src/format.rs | 69 +++++++++++++++++++ .../ui_engine/libs/meta-runtime/src/lib.rs | 5 +- 5 files changed, 121 insertions(+), 60 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cb44952..8ed39c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,46 @@ ratio/diff ver `git show `. ## 2026-05-10 +### feat(yahweh-meta-runtime): promover `short_hash` y `preview_value` desde nakui-explorer +Continúa la integración de las apps nakui al stack yahweh. Los +helpers visuales que `nakui-explorer` tenía locales y son reusables +suben a `yahweh-meta-runtime/format` para que cualquier app pueda +consumirlos sin duplicar. + +Cambios en `yahweh-meta-runtime`: +- **`pub fn short_hash(h: &[u8; 32]) -> String`**: hex de los + primeros 4 bytes (8 chars). Útil para mostrar bundle/schema + hashes en UI sin quemar pantalla. +- **`pub fn preview_value(v: &Value, max: usize) -> String`**: + JSON one-liner truncado con `...` al final si excede `max` + chars. Edge case: `max < 3` devuelve los primeros `max` chars + sin sufijo. +- Re-exports en lib. +- 5 tests nuevos: 4 tests + 1 sanity para el caso `max < ellipsis`. + +Migración de `nakui-explorer`: +- Nueva dep `yahweh-meta-runtime` en Cargo.toml. +- Borrado helpers locales `short_uuid`, `short_hash`, + `preview_value` (~30 líneas). +- `use yahweh_meta_runtime::{preview_value, short_hash, short_uuid}`. +- Borrados 4 tests duplicados (los runtime los testea). + +Tests: +- `yahweh-meta-runtime`: 42 → **47** (+5 helpers nuevos). +- `nakui-explorer`: 7 → **3** (–4 duplicados; quedan los 3 + específicos: load_log, breakdown, missing_file). +- Resto del workspace intacto. + +Beneficio operativo: 3 helpers visuales centralizados. Cualquier +app nueva que muestre UUIDs/hashes/JSON-previews los importa sin +re-implementar la heurística de truncamiento. + +Pendiente arquitectural: el render del card timeline en +`nakui-explorer` (border-l-4 colored + flex_col + texto en niveles) +es un pattern reusable que también aparece en `yahweh-widget-meta-form` +(render_list filas). Cuando aparezca un tercer consumer de ese +pattern se extrae a un widget yahweh. + ### feat(brahman-cards): templates Nickel canónicos para cada body kind Materializa el patrón "import + override" del brazo: hasta ahora `BRAHMAN_CARDS_TEMPLATES_DIR` existía como mecanismo pero el repo diff --git a/crates/apps/nakui-explorer/Cargo.toml b/crates/apps/nakui-explorer/Cargo.toml index 38c6249..321bd06 100644 --- a/crates/apps/nakui-explorer/Cargo.toml +++ b/crates/apps/nakui-explorer/Cargo.toml @@ -7,6 +7,7 @@ description = "Explorador GPUI del event log de Nakui: timeline de seeds + morph [dependencies] nakui-core = { path = "../../modules/nakui/core" } +yahweh-meta-runtime = { path = "../../modules/ui_engine/libs/meta-runtime" } 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 1fe882c..9ff22a4 100644 --- a/crates/apps/nakui-explorer/src/main.rs +++ b/crates/apps/nakui-explorer/src/main.rs @@ -30,6 +30,7 @@ use gpui::{ SharedString, Window, WindowBounds, WindowOptions, }; use nakui_core::event_log::{EventLog, LogEntry}; +use yahweh_meta_runtime::{preview_value, short_hash, short_uuid}; const REFRESH_INTERVAL: Duration = Duration::from_secs(2); @@ -378,35 +379,8 @@ impl Render for Explorer { } } -fn short_uuid(id: &uuid::Uuid) -> String { - let s = id.to_string(); - if s.len() > 8 { - s[..8].to_string() - } else { - s - } -} - -fn short_hash(h: &[u8; 32]) -> String { - let mut s = String::with_capacity(8); - for b in h.iter().take(4) { - use std::fmt::Write; - let _ = write!(s, "{:02x}", b); - } - s -} - -/// Renderiza un `serde_json::Value` en una sola línea limitada a `max` -/// caracteres (para preview en la timeline, no para edición). -fn preview_value(v: &serde_json::Value, max: usize) -> String { - let s = v.to_string(); - if s.chars().count() <= max { - s - } else { - let truncated: String = s.chars().take(max - 3).collect(); - format!("{truncated}...") - } -} +// Helpers `short_uuid`, `short_hash`, `preview_value` viven en +// `yahweh_meta_runtime::format`. Se usan acá via el `use` de arriba. #[cfg(test)] mod tests { @@ -472,34 +446,8 @@ mod tests { assert!(result.is_empty()); } - #[test] - fn preview_value_truncates_long_strings() { - let v = serde_json::json!({"a": "x".repeat(200)}); - let p = preview_value(&v, 30); - assert!(p.len() <= 30); - assert!(p.ends_with("...")); - } - - #[test] - fn preview_value_keeps_short_strings_intact() { - let v = serde_json::json!({"a": 1}); - let p = preview_value(&v, 30); - assert_eq!(p, "{\"a\":1}"); - } - - #[test] - fn short_uuid_takes_first_8_chars() { - let id = uuid::Uuid::parse_str("a1b2c3d4-e5f6-7890-abcd-ef1234567890").unwrap(); - assert_eq!(short_uuid(&id), "a1b2c3d4"); - } - - #[test] - fn short_hash_takes_first_4_bytes_hex() { - let mut h = [0u8; 32]; - h[0] = 0xaa; - h[1] = 0xbb; - h[2] = 0xcc; - h[3] = 0xdd; - assert_eq!(short_hash(&h), "aabbccdd"); - } + // Tests de `short_uuid` / `short_hash` / `preview_value` viven + // en `yahweh-meta-runtime::format` tras la migración. Si esos se + // vuelven a romper, los tests específicos del crate runtime los + // capturan; acá no duplicamos. } diff --git a/crates/modules/ui_engine/libs/meta-runtime/src/format.rs b/crates/modules/ui_engine/libs/meta-runtime/src/format.rs index e827b9a..4e0c47b 100644 --- a/crates/modules/ui_engine/libs/meta-runtime/src/format.rs +++ b/crates/modules/ui_engine/libs/meta-runtime/src/format.rs @@ -52,6 +52,36 @@ pub fn short_uuid(id: &Uuid) -> String { id.to_string().chars().take(8).collect() } +/// Hex string de los primeros 4 bytes de un hash SHA-256 (8 +/// caracteres). Útil para mostrar bundle/schema hashes en UI sin +/// quemar pantalla con los 64 chars completos. +pub fn short_hash(h: &[u8; 32]) -> String { + use std::fmt::Write; + let mut s = String::with_capacity(8); + for b in h.iter().take(4) { + let _ = write!(s, "{:02x}", b); + } + s +} + +/// Renderea un `serde_json::Value` en una sola línea, truncado a +/// `max` caracteres con `...` al final si excede. Para preview en +/// timelines/cards/listas — NO para edición. +/// +/// `max` es un upper-bound aproximado: el resultado nunca excede +/// `max` chars, pero puede ser más corto si el value es chico. +pub fn preview_value(v: &Value, max: usize) -> String { + let s = v.to_string(); + if s.chars().count() <= max { + s + } else if max < 3 { + s.chars().take(max).collect() + } else { + let truncated: String = s.chars().take(max - 3).collect(); + format!("{truncated}...") + } +} + #[cfg(test)] mod tests { use super::*; @@ -105,6 +135,45 @@ mod tests { assert_eq!(value_to_input_text(&json!(42)), "42"); } + #[test] + fn short_hash_takes_first_4_bytes_hex() { + let mut h = [0u8; 32]; + h[0] = 0xaa; + h[1] = 0xbb; + h[2] = 0xcc; + h[3] = 0xdd; + assert_eq!(short_hash(&h), "aabbccdd"); + } + + #[test] + fn short_hash_zeros() { + let h = [0u8; 32]; + assert_eq!(short_hash(&h), "00000000"); + } + + #[test] + fn preview_value_keeps_short_strings_intact() { + let v = json!({"a": 1}); + assert_eq!(preview_value(&v, 30), "{\"a\":1}"); + } + + #[test] + fn preview_value_truncates_long_strings_with_ellipsis() { + let v = json!({"a": "x".repeat(200)}); + let p = preview_value(&v, 30); + assert!(p.chars().count() <= 30); + assert!(p.ends_with("...")); + } + + #[test] + fn preview_value_handles_max_smaller_than_ellipsis() { + // Edge case: max < 3 (no espacio para "..."). Devuelve + // los primeros `max` chars sin sufijo, sin panic. + let v = json!("xxxxxxxxxxxxxxxx"); + let p = preview_value(&v, 2); + assert!(p.chars().count() <= 2); + } + #[test] fn short_uuid_returns_first_8_chars() { let id = Uuid::parse_str("01ARZ3ND-EKTS-V4RR-FFQ6-9G5FAV000000").ok(); diff --git a/crates/modules/ui_engine/libs/meta-runtime/src/lib.rs b/crates/modules/ui_engine/libs/meta-runtime/src/lib.rs index b470f5b..f179578 100644 --- a/crates/modules/ui_engine/libs/meta-runtime/src/lib.rs +++ b/crates/modules/ui_engine/libs/meta-runtime/src/lib.rs @@ -30,6 +30,9 @@ pub mod refs; pub use backend::{MetaBackend, WriteOutcome}; pub use delta::{compute_clear_fields, compute_field_delta}; -pub use format::{human_label_for_record, render_value, short_uuid, value_to_input_text}; +pub use format::{ + human_label_for_record, preview_value, render_value, short_hash, short_uuid, + value_to_input_text, +}; pub use parse::{infer_param_value, parse_field_value, resolve_param_value}; pub use refs::validate_entity_refs;