feat(yahweh-meta-runtime): promover short_hash y preview_value desde nakui-explorer
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) <noreply@anthropic.com>
This commit is contained in:
@@ -6,6 +6,46 @@ ratio/diff ver `git show <sha>`.
|
|||||||
|
|
||||||
## 2026-05-10
|
## 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
|
### feat(brahman-cards): templates Nickel canónicos para cada body kind
|
||||||
Materializa el patrón "import + override" del brazo: hasta ahora
|
Materializa el patrón "import + override" del brazo: hasta ahora
|
||||||
`BRAHMAN_CARDS_TEMPLATES_DIR` existía como mecanismo pero el repo
|
`BRAHMAN_CARDS_TEMPLATES_DIR` existía como mecanismo pero el repo
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ description = "Explorador GPUI del event log de Nakui: timeline de seeds + morph
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nakui-core = { path = "../../modules/nakui/core" }
|
nakui-core = { path = "../../modules/nakui/core" }
|
||||||
|
yahweh-meta-runtime = { path = "../../modules/ui_engine/libs/meta-runtime" }
|
||||||
gpui = { workspace = true }
|
gpui = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
uuid = { workspace = true, features = ["serde"] }
|
uuid = { workspace = true, features = ["serde"] }
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ use gpui::{
|
|||||||
SharedString, Window, WindowBounds, WindowOptions,
|
SharedString, Window, WindowBounds, WindowOptions,
|
||||||
};
|
};
|
||||||
use nakui_core::event_log::{EventLog, LogEntry};
|
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);
|
const REFRESH_INTERVAL: Duration = Duration::from_secs(2);
|
||||||
|
|
||||||
@@ -378,35 +379,8 @@ impl Render for Explorer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn short_uuid(id: &uuid::Uuid) -> String {
|
// Helpers `short_uuid`, `short_hash`, `preview_value` viven en
|
||||||
let s = id.to_string();
|
// `yahweh_meta_runtime::format`. Se usan acá via el `use` de arriba.
|
||||||
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}...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
@@ -472,34 +446,8 @@ mod tests {
|
|||||||
assert!(result.is_empty());
|
assert!(result.is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
// Tests de `short_uuid` / `short_hash` / `preview_value` viven
|
||||||
fn preview_value_truncates_long_strings() {
|
// en `yahweh-meta-runtime::format` tras la migración. Si esos se
|
||||||
let v = serde_json::json!({"a": "x".repeat(200)});
|
// vuelven a romper, los tests específicos del crate runtime los
|
||||||
let p = preview_value(&v, 30);
|
// capturan; acá no duplicamos.
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,6 +52,36 @@ pub fn short_uuid(id: &Uuid) -> String {
|
|||||||
id.to_string().chars().take(8).collect()
|
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)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
@@ -105,6 +135,45 @@ mod tests {
|
|||||||
assert_eq!(value_to_input_text(&json!(42)), "42");
|
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]
|
#[test]
|
||||||
fn short_uuid_returns_first_8_chars() {
|
fn short_uuid_returns_first_8_chars() {
|
||||||
let id = Uuid::parse_str("01ARZ3ND-EKTS-V4RR-FFQ6-9G5FAV000000").ok();
|
let id = Uuid::parse_str("01ARZ3ND-EKTS-V4RR-FFQ6-9G5FAV000000").ok();
|
||||||
|
|||||||
@@ -30,6 +30,9 @@ pub mod refs;
|
|||||||
|
|
||||||
pub use backend::{MetaBackend, WriteOutcome};
|
pub use backend::{MetaBackend, WriteOutcome};
|
||||||
pub use delta::{compute_clear_fields, compute_field_delta};
|
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 parse::{infer_param_value, parse_field_value, resolve_param_value};
|
||||||
pub use refs::validate_entity_refs;
|
pub use refs::validate_entity_refs;
|
||||||
|
|||||||
Reference in New Issue
Block a user